1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <semaphore.h>
18 
19 #include <errno.h>
20 #include <gtest/gtest.h>
21 #include <limits.h>
22 #include <pthread.h>
23 #include <time.h>
24 #include <unistd.h>
25 
26 #include "private/bionic_constants.h"
27 #include "ScopedSignalHandler.h"
28 
TEST(semaphore,sem_init)29 TEST(semaphore, sem_init) {
30   sem_t s;
31 
32   // Perfectly fine initial values.
33   ASSERT_EQ(0, sem_init(&s, 0, 0));
34   ASSERT_EQ(0, sem_init(&s, 0, 1));
35   ASSERT_EQ(0, sem_init(&s, 0, 123));
36 
37   // Too small an initial value.
38   errno = 0;
39   ASSERT_EQ(-1, sem_init(&s, 0, -1));
40   ASSERT_EQ(EINVAL, errno);
41 
42   ASSERT_EQ(SEM_VALUE_MAX, sysconf(_SC_SEM_VALUE_MAX));
43 
44   // The largest initial value.
45   ASSERT_EQ(0, sem_init(&s, 0, SEM_VALUE_MAX));
46 
47   // Too large an initial value.
48   errno = 0;
49   ASSERT_EQ(-1, sem_init(&s, 0, SEM_VALUE_MAX + 1));
50   ASSERT_EQ(EINVAL, errno);
51 
52   ASSERT_EQ(0, sem_destroy(&s));
53 }
54 
TEST(semaphore,sem_trywait)55 TEST(semaphore, sem_trywait) {
56   sem_t s;
57   ASSERT_EQ(0, sem_init(&s, 0, 3));
58   ASSERT_EQ(0, sem_trywait(&s));
59   ASSERT_EQ(0, sem_trywait(&s));
60   ASSERT_EQ(0, sem_trywait(&s));
61   errno = 0;
62   ASSERT_EQ(-1, sem_trywait(&s));
63   ASSERT_EQ(EAGAIN, errno);
64   ASSERT_EQ(0, sem_destroy(&s));
65 }
66 
SemWaitThreadTestFn(sem_t & sem)67 static void SemWaitThreadTestFn(sem_t& sem) {
68   ASSERT_EQ(0, sem_wait(&sem));
69 }
70 
SemWaitThreadFn(void * arg)71 static void* SemWaitThreadFn(void* arg) {
72   SemWaitThreadTestFn(*reinterpret_cast<sem_t*>(arg));
73   return nullptr;
74 }
75 
TEST(semaphore,sem_wait__sem_post)76 TEST(semaphore, sem_wait__sem_post) {
77   sem_t s;
78   ASSERT_EQ(0, sem_init(&s, 0, 0));
79 
80   pthread_t t1, t2, t3;
81   ASSERT_EQ(0, pthread_create(&t1, NULL, SemWaitThreadFn, &s));
82   ASSERT_EQ(0, pthread_create(&t2, NULL, SemWaitThreadFn, &s));
83   ASSERT_EQ(0, pthread_create(&t3, NULL, SemWaitThreadFn, &s));
84 
85   ASSERT_EQ(0, sem_post(&s));
86   ASSERT_EQ(0, sem_post(&s));
87   ASSERT_EQ(0, sem_post(&s));
88 
89   void* result;
90   ASSERT_EQ(0, pthread_join(t1, &result));
91   ASSERT_EQ(0, pthread_join(t2, &result));
92   ASSERT_EQ(0, pthread_join(t3, &result));
93 }
94 
timespec_add_ms(timespec & ts,size_t ms)95 static inline void timespec_add_ms(timespec& ts, size_t ms) {
96   ts.tv_sec  += ms / 1000;
97   ts.tv_nsec += (ms % 1000) * 1000000;
98   if (ts.tv_nsec >= NS_PER_S) {
99     ts.tv_sec++;
100     ts.tv_nsec -= NS_PER_S;
101   }
102 }
103 
TEST(semaphore,sem_timedwait)104 TEST(semaphore, sem_timedwait) {
105   sem_t s;
106   ASSERT_EQ(0, sem_init(&s, 0, 0));
107 
108   timespec ts;
109   ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &ts));
110   timespec_add_ms(ts, 100);
111 
112   errno = 0;
113   ASSERT_EQ(-1, sem_timedwait(&s, &ts));
114   ASSERT_EQ(ETIMEDOUT, errno);
115 
116   // A negative timeout is an error.
117   errno = 0;
118   ts.tv_nsec = -1;
119   ASSERT_EQ(-1, sem_timedwait(&s, &ts));
120   ASSERT_EQ(EINVAL, errno);
121   errno = 0;
122   ts.tv_nsec = NS_PER_S;
123   ASSERT_EQ(-1, sem_timedwait(&s, &ts));
124   ASSERT_EQ(EINVAL, errno);
125 
126   errno = 0;
127   ts.tv_nsec = NS_PER_S - 1;
128   ts.tv_sec = -1;
129   ASSERT_EQ(-1, sem_timedwait(&s, &ts));
130   ASSERT_EQ(ETIMEDOUT, errno);
131 
132   ASSERT_EQ(0, sem_destroy(&s));
133 }
134 
TEST(semaphore_DeathTest,sem_timedwait_null_timeout)135 TEST(semaphore_DeathTest, sem_timedwait_null_timeout) {
136   sem_t s;
137   ASSERT_EQ(0, sem_init(&s, 0, 0));
138 
139   ASSERT_EXIT(sem_timedwait(&s, nullptr), testing::KilledBySignal(SIGSEGV), "");
140 }
141 
TEST(semaphore,sem_getvalue)142 TEST(semaphore, sem_getvalue) {
143   sem_t s;
144   ASSERT_EQ(0, sem_init(&s, 0, 0));
145 
146   int i;
147   ASSERT_EQ(0, sem_getvalue(&s, &i));
148   ASSERT_EQ(0, i);
149 
150   ASSERT_EQ(0, sem_post(&s));
151   ASSERT_EQ(0, sem_getvalue(&s, &i));
152   ASSERT_EQ(1, i);
153 
154   ASSERT_EQ(0, sem_post(&s));
155   ASSERT_EQ(0, sem_getvalue(&s, &i));
156   ASSERT_EQ(2, i);
157 
158   ASSERT_EQ(0, sem_wait(&s));
159   ASSERT_EQ(0, sem_getvalue(&s, &i));
160   ASSERT_EQ(1, i);
161 }
162 
163 extern "C" void android_set_application_target_sdk_version(uint32_t target);
164 
sem_wait_test_signal_handler(int)165 static void sem_wait_test_signal_handler(int) {
166 }
167 
SemWaitEINTRThreadFn(void * arg)168 static void* SemWaitEINTRThreadFn(void* arg) {
169   sem_t* sem = reinterpret_cast<sem_t*>(arg);
170   uintptr_t have_eintr = 0;
171   uintptr_t have_error = 0;
172   while (true) {
173     int result = sem_wait(sem);
174     if (result == 0) {
175       break;
176     }
177     if (result == -1) {
178       if (errno == EINTR) {
179         have_eintr = 1;
180       } else {
181         have_error = 1;
182         break;
183       }
184     }
185   }
186   return reinterpret_cast<void*>((have_eintr << 1) | have_error);
187 }
188 
TEST(semaphore,sem_wait_no_EINTR_in_sdk_less_equal_than_23)189 TEST(semaphore, sem_wait_no_EINTR_in_sdk_less_equal_than_23) {
190 #if defined(__BIONIC__)
191   android_set_application_target_sdk_version(__ANDROID_API_M__);
192   sem_t s;
193   ASSERT_EQ(0, sem_init(&s, 0, 0));
194   ScopedSignalHandler handler(SIGUSR1, sem_wait_test_signal_handler);
195   pthread_t thread;
196   ASSERT_EQ(0, pthread_create(&thread, nullptr, SemWaitEINTRThreadFn, &s));
197   // Give some time for the thread to run sem_wait.
198   usleep(500000);
199   ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
200   // Give some time for the thread to handle signal.
201   usleep(500000);
202   ASSERT_EQ(0, sem_post(&s));
203   void* result;
204   ASSERT_EQ(0, pthread_join(thread, &result));
205   ASSERT_EQ(0U, reinterpret_cast<uintptr_t>(result));
206 #else
207   GTEST_LOG_(INFO) << "This test tests sem_wait's compatibility for old sdk versions";
208 #endif
209 }
210 
TEST(semaphore,sem_wait_EINTR_in_sdk_greater_than_23)211 TEST(semaphore, sem_wait_EINTR_in_sdk_greater_than_23) {
212 #if defined(__BIONIC__)
213   android_set_application_target_sdk_version(24U);
214 #endif
215   sem_t s;
216   ASSERT_EQ(0, sem_init(&s, 0, 0));
217   ScopedSignalHandler handler(SIGUSR1, sem_wait_test_signal_handler);
218   pthread_t thread;
219   ASSERT_EQ(0, pthread_create(&thread, nullptr, SemWaitEINTRThreadFn, &s));
220   // Give some time for the thread to run sem_wait.
221   usleep(500000);
222   ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
223   // Give some time for the thread to handle signal.
224   usleep(500000);
225   ASSERT_EQ(0, sem_post(&s));
226   void* result;
227   ASSERT_EQ(0, pthread_join(thread, &result));
228   ASSERT_EQ(2U, reinterpret_cast<uintptr_t>(result));
229 }
230