Fix pthread_getattr_np, pthread_attr_setguardsize, and pthread_attr_setstacksize.

pthread_getattr_np was reporting the values supplied to us, not the values we
actually used, which is kinda the whole point of pthread_getattr_np.

pthread_attr_setguardsize and pthread_attr_setstacksize were reporting EINVAL
for any size that wasn't a multiple of the system page size. This is
unnecessary. We can just round like POSIX suggests and glibc already does.

Also improve the error reporting for pthread_create failures.

Change-Id: I7ebc518628a8a1161ec72e111def911d500bba71
diff --git a/tests/pthread_test.cpp b/tests/pthread_test.cpp
index d754312..c7dbdc7 100644
--- a/tests/pthread_test.cpp
+++ b/tests/pthread_test.cpp
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 
 #include <errno.h>
+#include <limits.h>
 #include <pthread.h>
 #include <unistd.h>
 
@@ -339,3 +340,95 @@
   ASSERT_EQ(0, pthread_join(t2, &join_result));
   ASSERT_EQ(0, reinterpret_cast<int>(join_result));
 }
+
+static void* GetActualGuardSizeFn(void* arg) {
+  pthread_attr_t attributes;
+  pthread_getattr_np(pthread_self(), &attributes);
+  pthread_attr_getguardsize(&attributes, reinterpret_cast<size_t*>(arg));
+  return NULL;
+}
+
+static size_t GetActualGuardSize(const pthread_attr_t& attributes) {
+  size_t result;
+  pthread_t t;
+  pthread_create(&t, &attributes, GetActualGuardSizeFn, &result);
+  void* join_result;
+  pthread_join(t, &join_result);
+  return result;
+}
+
+static void* GetActualStackSizeFn(void* arg) {
+  pthread_attr_t attributes;
+  pthread_getattr_np(pthread_self(), &attributes);
+  pthread_attr_getstacksize(&attributes, reinterpret_cast<size_t*>(arg));
+  return NULL;
+}
+
+static size_t GetActualStackSize(const pthread_attr_t& attributes) {
+  size_t result;
+  pthread_t t;
+  pthread_create(&t, &attributes, GetActualStackSizeFn, &result);
+  void* join_result;
+  pthread_join(t, &join_result);
+  return result;
+}
+
+TEST(pthread, pthread_attr_setguardsize) {
+  pthread_attr_t attributes;
+  ASSERT_EQ(0, pthread_attr_init(&attributes));
+
+  // Get the default guard size.
+  size_t default_guard_size;
+  ASSERT_EQ(0, pthread_attr_getguardsize(&attributes, &default_guard_size));
+
+  // No such thing as too small: will be rounded up to one page by pthread_create.
+  ASSERT_EQ(0, pthread_attr_setguardsize(&attributes, 128));
+  size_t guard_size;
+  ASSERT_EQ(0, pthread_attr_getguardsize(&attributes, &guard_size));
+  ASSERT_EQ(128U, guard_size);
+  ASSERT_EQ(4096U, GetActualGuardSize(attributes));
+
+  // Large enough and a multiple of the page size.
+  ASSERT_EQ(0, pthread_attr_setguardsize(&attributes, 32*1024));
+  ASSERT_EQ(0, pthread_attr_getguardsize(&attributes, &guard_size));
+  ASSERT_EQ(32*1024U, guard_size);
+
+  // Large enough but not a multiple of the page size; will be rounded up by pthread_create.
+  ASSERT_EQ(0, pthread_attr_setguardsize(&attributes, 32*1024 + 1));
+  ASSERT_EQ(0, pthread_attr_getguardsize(&attributes, &guard_size));
+  ASSERT_EQ(32*1024U + 1, guard_size);
+}
+
+TEST(pthread, pthread_attr_setstacksize) {
+  pthread_attr_t attributes;
+  ASSERT_EQ(0, pthread_attr_init(&attributes));
+
+  // Get the default stack size.
+  size_t default_stack_size;
+  ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &default_stack_size));
+
+  // Too small.
+  ASSERT_EQ(EINVAL, pthread_attr_setstacksize(&attributes, 128));
+  size_t stack_size;
+  ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &stack_size));
+  ASSERT_EQ(default_stack_size, stack_size);
+  ASSERT_GE(GetActualStackSize(attributes), default_stack_size);
+
+  // Large enough and a multiple of the page size.
+  ASSERT_EQ(0, pthread_attr_setstacksize(&attributes, 32*1024));
+  ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &stack_size));
+  ASSERT_EQ(32*1024U, stack_size);
+  ASSERT_EQ(GetActualStackSize(attributes), 32*1024U);
+
+  // Large enough but not a multiple of the page size; will be rounded up by pthread_create.
+  ASSERT_EQ(0, pthread_attr_setstacksize(&attributes, 32*1024 + 1));
+  ASSERT_EQ(0, pthread_attr_getstacksize(&attributes, &stack_size));
+  ASSERT_EQ(32*1024U + 1, stack_size);
+#if __BIONIC__
+  // Bionic rounds up, which is what POSIX allows.
+  ASSERT_EQ(GetActualStackSize(attributes), (32 + 4)*1024U);
+#else
+  // glibc rounds down, in violation of POSIX. They document this in their BUGS section.
+  ASSERT_EQ(GetActualStackSize(attributes), 32*1024U);
+#endif
+}