Make sem_wait able to return errno EINTR for sdk > 23.
Posix standards says sem_wait is interruptible by the delivery
of a signal. To keep compatiblity with old apps, only fix that
in newer sdk versions.
Bug: 26743454
Change-Id: I924cbb436658e3e0f397c922d866ece99b8241a3
diff --git a/libc/bionic/libc_init_dynamic.cpp b/libc/bionic/libc_init_dynamic.cpp
index 97d9e39..734d86b 100644
--- a/libc/bionic/libc_init_dynamic.cpp
+++ b/libc/bionic/libc_init_dynamic.cpp
@@ -114,3 +114,9 @@
exit(slingshot(args.argc, args.argv, args.envp));
}
+
+extern "C" uint32_t android_get_application_target_sdk_version();
+
+uint32_t bionic_get_application_target_sdk_version() {
+ return android_get_application_target_sdk_version();
+}
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 3cda1a2..d1494d7 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -26,6 +26,7 @@
* SUCH DAMAGE.
*/
+#include <android/api-level.h>
#include <elf.h>
#include <errno.h>
#include <stddef.h>
@@ -106,3 +107,7 @@
exit(slingshot(args.argc, args.argv, args.envp));
}
+
+uint32_t bionic_get_application_target_sdk_version() {
+ return __ANDROID_API__;
+}
diff --git a/libc/bionic/semaphore.cpp b/libc/bionic/semaphore.cpp
index b30c0b0..1981647 100644
--- a/libc/bionic/semaphore.cpp
+++ b/libc/bionic/semaphore.cpp
@@ -41,6 +41,7 @@
#include "private/bionic_constants.h"
#include "private/bionic_futex.h"
+#include "private/bionic_sdk_version.h"
#include "private/bionic_time_conversions.h"
// In this implementation, a semaphore contains a
@@ -220,7 +221,13 @@
return 0;
}
- __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, false, nullptr);
+ int result = __futex_wait_ex(sem_count_ptr, shared, shared | SEMCOUNT_MINUS_ONE, false, nullptr);
+ if (bionic_get_application_target_sdk_version() > 23) {
+ if (result ==-EINTR) {
+ errno = EINTR;
+ return -1;
+ }
+ }
}
}
diff --git a/tests/semaphore_test.cpp b/tests/semaphore_test.cpp
index 84343da..7dc7225 100644
--- a/tests/semaphore_test.cpp
+++ b/tests/semaphore_test.cpp
@@ -24,6 +24,7 @@
#include <unistd.h>
#include "private/bionic_constants.h"
+#include "ScopedSignalHandler.h"
TEST(semaphore, sem_init) {
sem_t s;
@@ -158,3 +159,71 @@
ASSERT_EQ(0, sem_getvalue(&s, &i));
ASSERT_EQ(1, i);
}
+
+extern "C" void android_set_application_target_sdk_version(uint32_t target);
+
+static void sem_wait_test_signal_handler(int) {
+}
+
+static void* SemWaitEINTRThreadFn(void* arg) {
+ sem_t* sem = reinterpret_cast<sem_t*>(arg);
+ uintptr_t have_eintr = 0;
+ uintptr_t have_error = 0;
+ while (true) {
+ int result = sem_wait(sem);
+ if (result == 0) {
+ break;
+ }
+ if (result == -1) {
+ if (errno == EINTR) {
+ have_eintr = 1;
+ } else {
+ have_error = 1;
+ break;
+ }
+ }
+ }
+ return reinterpret_cast<void*>((have_eintr << 1) | have_error);
+}
+
+TEST(semaphore, sem_wait_no_EINTR_in_sdk_less_equal_than_23) {
+#if defined(__BIONIC__)
+ android_set_application_target_sdk_version(23U);
+ sem_t s;
+ ASSERT_EQ(0, sem_init(&s, 0, 0));
+ ScopedSignalHandler handler(SIGUSR1, sem_wait_test_signal_handler);
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, nullptr, SemWaitEINTRThreadFn, &s));
+ // Give some time for the thread to run sem_wait.
+ usleep(500000);
+ ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+ // Give some time for the thread to handle signal.
+ usleep(500000);
+ ASSERT_EQ(0, sem_post(&s));
+ void* result;
+ ASSERT_EQ(0, pthread_join(thread, &result));
+ ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(result));
+#else
+ GTEST_LOG_(INFO) << "This test tests sem_wait's compatibility for old sdk versions";
+#endif
+}
+
+TEST(semaphore, sem_wait_EINTR_in_sdk_greater_than_23) {
+#if defined(__BIONIC__)
+ android_set_application_target_sdk_version(24U);
+#endif
+ sem_t s;
+ ASSERT_EQ(0, sem_init(&s, 0, 0));
+ ScopedSignalHandler handler(SIGUSR1, sem_wait_test_signal_handler);
+ pthread_t thread;
+ ASSERT_EQ(0, pthread_create(&thread, nullptr, SemWaitEINTRThreadFn, &s));
+ // Give some time for the thread to run sem_wait.
+ usleep(500000);
+ ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+ // Give some time for the thread to handle signal.
+ usleep(500000);
+ ASSERT_EQ(0, sem_post(&s));
+ void* result;
+ ASSERT_EQ(0, pthread_join(thread, &result));
+ ASSERT_EQ(2U, reinterpret_cast<uintptr_t>(result));
+}