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
29TEST(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
55TEST(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
67static void SemWaitThreadTestFn(sem_t& sem) {
68  ASSERT_EQ(0, sem_wait(&sem));
69}
70
71static void* SemWaitThreadFn(void* arg) {
72  SemWaitThreadTestFn(*reinterpret_cast<sem_t*>(arg));
73  return nullptr;
74}
75
76TEST(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
95static 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
104TEST(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
135TEST(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
142TEST(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
163extern "C" void android_set_application_target_sdk_version(uint32_t target);
164
165static void sem_wait_test_signal_handler(int) {
166}
167
168static 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
189TEST(semaphore, sem_wait_no_EINTR_in_sdk_less_equal_than_23) {
190#if defined(__BIONIC__)
191  android_set_application_target_sdk_version(23U);
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
211TEST(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