1 /*
2 * Copyright (C) 2015-2021 Alibaba Group Holding Limited
3 */
4
5 #include <stdint.h>
6 #include <string.h>
7 #include <errno.h>
8 #include <time.h>
9 #include <pthread.h>
10 #include <aos/kernel.h>
11
12 #include "internal/pthread.h"
13
pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * attr)14 int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr)
15 {
16 int ret = 0;
17
18 if (cond == NULL) {
19 return EINVAL;
20 }
21
22 memset(cond, 0, sizeof(pthread_cond_t));
23 ret = aos_mutex_new((aos_mutex_t*)(&(cond->lock)));
24 if (ret != 0) {
25 return EAGAIN;
26 }
27
28 ret = aos_sem_new((aos_sem_t*)(&(cond->wait_sem)), 0);
29 if (ret != 0) {
30 aos_mutex_free((aos_mutex_t*)(&(cond->lock)));
31 return EAGAIN;
32 }
33
34 ret = aos_sem_new((aos_sem_t*)(&(cond->wait_done)), 0);
35 if (ret != 0) {
36 aos_mutex_free((aos_mutex_t*)(&(cond->lock)));
37 aos_sem_free((aos_sem_t*)(&(cond->wait_sem)));
38 return EAGAIN;
39 }
40
41 cond->waiting = cond->signals = 0;
42 cond->flag = PTHREAD_DYN_INIT;
43
44 if (attr != NULL) {
45 cond->attr = *attr;
46 }
47
48 return 0;
49 }
50
pthread_cond_destroy(pthread_cond_t * cond)51 int pthread_cond_destroy(pthread_cond_t *cond)
52 {
53 if (cond == NULL) {
54 return EINVAL;
55 }
56
57 aos_mutex_free((aos_mutex_t*)(&(cond->lock)));
58 aos_sem_free((aos_sem_t*)(&(cond->wait_sem)));
59 aos_sem_free((aos_sem_t*)(&(cond->wait_done)));
60
61 return 0;
62 }
63
pthread_cond_broadcast(pthread_cond_t * cond)64 int pthread_cond_broadcast(pthread_cond_t *cond)
65 {
66 int i = 0;
67 int num_waiting = 0;
68
69 if (cond == NULL)
70 return EINVAL;
71
72 if (cond->flag != PTHREAD_DYN_INIT)
73 return 0;
74
75 /* If there are waiting threads not already signalled, then
76 * signal the condition and wait for the thread to respond.
77 */
78 aos_mutex_lock((aos_mutex_t*)(&(cond->lock)), AOS_WAIT_FOREVER);
79
80 if (cond->waiting > cond->signals) {
81 num_waiting = (cond->waiting - cond->signals);
82 cond->signals = cond->waiting;
83
84 for (i = 0; i < num_waiting; ++i) {
85 aos_sem_signal((aos_sem_t*)(&(cond->wait_sem)));
86 }
87 /* Now all released threads are blocked here, waiting for us.
88 * Collect them all (and win fabulous prizes!) :-)
89 */
90 aos_mutex_unlock((aos_mutex_t*)(&(cond->lock)));
91 for (i = 0; i < num_waiting; ++i) {
92 aos_sem_wait((aos_sem_t*)(&(cond->wait_done)), AOS_WAIT_FOREVER);
93 }
94 } else {
95 aos_mutex_unlock((aos_mutex_t*)(&(cond->lock)));
96 }
97
98 return 0;
99 }
100
pthread_cond_signal(pthread_cond_t * cond)101 int pthread_cond_signal(pthread_cond_t *cond)
102 {
103 if (cond == NULL)
104 return EINVAL;
105
106 if (cond->flag != PTHREAD_DYN_INIT)
107 return 0;
108
109 /* If there are waiting threads not already signalled, then
110 * signal the condition and wait for the thread to respond.
111 */
112 aos_mutex_lock((aos_mutex_t*)(&(cond->lock)), AOS_WAIT_FOREVER);
113 if ( cond->waiting > cond->signals ) {
114 (cond->signals)++;
115 aos_sem_signal((aos_sem_t*)(&(cond->wait_sem)));
116 aos_mutex_unlock((aos_mutex_t*)(&(cond->lock)));
117 aos_sem_wait((aos_sem_t*)(&(cond->wait_done)), AOS_WAIT_FOREVER);
118 } else {
119 aos_mutex_unlock((aos_mutex_t*)(&(cond->lock)));
120 }
121
122 return 0;
123 }
124
pthread_cond_timedwait_ms(pthread_cond_t * cond,pthread_mutex_t * mutex,int64_t ms)125 static int pthread_cond_timedwait_ms(pthread_cond_t *cond, pthread_mutex_t *mutex, int64_t ms)
126 {
127 int ret = 0;
128
129 if (cond->flag == PTHREAD_STATIC_INIT) {
130 /* The cond is inited by PTHREAD_COND_INITIALIZER*/
131 ret = pthread_cond_init(cond, NULL);
132 if (ret != 0)
133 return ret;
134 }
135
136 /* Obtain the protection mutex, and increment the number of waiters.
137 * This allows the signal mechanism to only perform a signal if there
138 * are waiting threads.
139 */
140 aos_mutex_lock((aos_mutex_t*)(&(cond->lock)), AOS_WAIT_FOREVER);
141 (cond->waiting)++;
142 aos_mutex_unlock((aos_mutex_t*)(&(cond->lock)));
143
144 /* Unlock the mutex, as is required by condition variable semantics */
145 aos_mutex_unlock((aos_mutex_t*)(&(mutex->mutex)));
146
147 /* Wait for a signal */
148 ret = aos_sem_wait((aos_sem_t*)(&(cond->wait_sem)), ms);
149
150 /* Let the signaler know we have completed the wait, otherwise
151 * the signaler can race ahead and get the condition semaphore
152 * if we are stopped between the mutex unlock and semaphore wait,
153 * giving a deadlock.
154 */
155 aos_mutex_lock((aos_mutex_t*)(&(cond->lock)), AOS_WAIT_FOREVER);
156
157 if (ret == -ETIMEDOUT) {
158 ret = ETIMEDOUT;
159 }
160
161 if (cond->signals > 0) {
162 /* If we timed out, we need to eat a condition signal */
163 if (ret == ETIMEDOUT) {
164 aos_sem_wait((aos_sem_t*)(&(cond->wait_sem)), AOS_NO_WAIT);
165 }
166 /* We always notify the signal thread that we are done */
167 aos_sem_signal((aos_sem_t*)(&(cond->wait_done)));
168
169 /* Signal handshake complete */
170 (cond->signals)--;
171 }
172
173 (cond->waiting)--;
174
175 aos_mutex_unlock((aos_mutex_t*)(&(cond->lock)));
176
177 /* Lock the mutex, as is required by condition variable semantics */
178 aos_mutex_lock((aos_mutex_t*)(&(mutex->mutex)), AOS_WAIT_FOREVER);
179
180 return ret;
181 }
182
183
pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)184 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
185 const struct timespec *abstime)
186 {
187 int ret;
188 struct timeval now_tv = {0};
189 struct timespec now_tp = {0};
190 int64_t timeout_ms = 0;
191
192 if ((cond == NULL) || (mutex == NULL)) {
193 return EINVAL;
194 }
195
196 if (abstime == NULL) {
197 timeout_ms = AOS_WAIT_FOREVER;
198 } else {
199 if (cond->attr.clock == CLOCK_MONOTONIC) {
200 ret = clock_gettime(CLOCK_MONOTONIC, &now_tp);
201 if (ret != 0) {
202 return EINVAL;
203 }
204
205 timeout_ms = (abstime->tv_sec - now_tp.tv_sec) * 1000 +
206 ((abstime->tv_nsec - now_tp.tv_nsec) / 1000000);
207 } else {
208 /* CLOCK_REALTIME */
209 gettimeofday(&now_tv, NULL);
210 timeout_ms = (abstime->tv_sec - now_tv.tv_sec) * 1000 +
211 ((abstime->tv_nsec - now_tv.tv_usec * 1000) / 1000000);
212 }
213
214 if (timeout_ms <= 0) {
215 /* The absolute time has already passed. Do not need to wait. */
216 return ETIMEDOUT;
217 }
218 }
219
220 return pthread_cond_timedwait_ms(cond, mutex, timeout_ms);
221 }
222
pthread_cond_timedwait_relative_np(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * reltime)223 int pthread_cond_timedwait_relative_np(pthread_cond_t *cond, pthread_mutex_t *mutex,
224 const struct timespec *reltime)
225 {
226 int64_t timeout_ms = 0;
227
228 if ((cond == NULL) || (mutex == NULL)) {
229 return EINVAL;
230 }
231
232 if (reltime == NULL) {
233 timeout_ms = AOS_WAIT_FOREVER;
234 } else {
235 timeout_ms = ((reltime->tv_sec) * 1000) + ((reltime->tv_nsec) / 1000000);
236 if (timeout_ms <= 0) {
237 return ETIMEDOUT;
238 }
239 }
240
241 return pthread_cond_timedwait_ms(cond, mutex, timeout_ms);
242 }
243
pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)244 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
245 {
246 return pthread_cond_timedwait(cond, mutex, NULL);
247 }
248
pthread_condattr_init(pthread_condattr_t * attr)249 int pthread_condattr_init(pthread_condattr_t *attr)
250 {
251 if (attr == NULL) {
252 return EINVAL;
253 }
254
255 attr->flag = PTHREAD_DYN_INIT;
256 attr->clock = DEFAULT_COND_CLOCK;
257 attr->pshared = DEFAULT_COND_SHARED;
258
259 return 0;
260 }
261
pthread_condattr_destroy(pthread_condattr_t * attr)262 int pthread_condattr_destroy(pthread_condattr_t *attr)
263 {
264 if (attr == NULL) {
265 return EINVAL;
266 }
267
268 memset(attr, 0 ,sizeof(pthread_condattr_t));
269
270 return 0;
271 }
272
pthread_condattr_getclock(const pthread_condattr_t * attr,clockid_t * clock_id)273 int pthread_condattr_getclock(const pthread_condattr_t *attr, clockid_t *clock_id)
274 {
275 if ((attr == NULL) || (clock_id == NULL)) {
276 return EINVAL;
277 }
278
279 *clock_id = attr->clock;
280
281 return 0;
282 }
283
pthread_condattr_setclock(pthread_condattr_t * attr,clockid_t clock_id)284 int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id)
285 {
286 if (attr == NULL) {
287 return EINVAL;
288 }
289
290 if ((clock_id != CLOCK_REALTIME) && (clock_id != CLOCK_MONOTONIC)) {
291 return EINVAL;
292 }
293
294 attr->clock = clock_id;
295
296 return 0;
297 }
298