1 /*
2  * Copyright (c) 2011 The Native Client Authors. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  */
6 
7 /*
8  * Native Client condition variable API
9  */
10 
11 #include <errno.h>
12 #include <limits.h>
13 #include <unistd.h>
14 
15 #include "native_client/src/untrusted/nacl/nacl_irt.h"
16 #include "native_client/src/untrusted/pthread/pthread.h"
17 #include "native_client/src/untrusted/pthread/pthread_internal.h"
18 #include "native_client/src/untrusted/pthread/pthread_types.h"
19 
20 /*
21  * This implementation is based on the condvar implementation in
22  * Android's C library, Bionic.  See libc/bionic/pthread.c in Bionic:
23  *
24  * https://android.googlesource.com/platform/bionic.git/+/7a34ed2bb36fcbe6967d8b670f4d70ada1dcef49/libc/bionic/pthread.c
25  *
26  * Technically there is a race condition in our pthread_cond_wait():
27  * It could miss a wakeup if pthread_cond_signal() or
28  * pthread_cond_broadcast() is called an exact multiple of 2^32 times
29  * between pthread_cond_wait()'s fetching of cond->sequence_number and
30  * its call to futex_wait().  That is very unlikely to happen,
31  * however.
32  *
33  * Unlike glibc's more complex condvar implementation, we do not
34  * attempt to optimize pthread_cond_signal/broadcast() to avoid a
35  * futex_wake() call in the case where there are no waiting threads.
36  */
37 
38 
39 /*
40  * Initialize condition variable COND using attributes ATTR, or use
41  * the default values if later is NULL.
42  */
pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * cond_attr)43 int pthread_cond_init(pthread_cond_t *cond,
44                       const pthread_condattr_t *cond_attr) {
45   cond->sequence_number = 0;
46   return 0;
47 }
48 
49 /*
50  * Destroy condition variable COND.
51  */
pthread_cond_destroy(pthread_cond_t * cond)52 int pthread_cond_destroy(pthread_cond_t *cond) {
53   return 0;
54 }
55 
pulse(pthread_cond_t * cond,int count)56 static int pulse(pthread_cond_t *cond, int count) {
57   /*
58    * This atomic increment executes the full memory barrier that
59    * pthread_cond_signal/broadcast() are required to execute.
60    */
61   __sync_fetch_and_add(&cond->sequence_number, 1);
62 
63   int unused_woken_count;
64   __libnacl_irt_futex.futex_wake(&cond->sequence_number, count,
65                                  &unused_woken_count);
66   return 0;
67 }
68 
69 /*
70  * Wake up one thread waiting for condition variable COND.
71  */
pthread_cond_signal(pthread_cond_t * cond)72 int pthread_cond_signal(pthread_cond_t *cond) {
73   return pulse(cond, 1);
74 }
75 
pthread_cond_broadcast(pthread_cond_t * cond)76 int pthread_cond_broadcast(pthread_cond_t *cond) {
77   return pulse(cond, INT_MAX);
78 }
79 
pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)80 int pthread_cond_wait(pthread_cond_t *cond,
81                       pthread_mutex_t *mutex) {
82   return pthread_cond_timedwait_abs(cond, mutex, NULL);
83 }
84 
pthread_cond_timedwait_abs(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)85 int pthread_cond_timedwait_abs(pthread_cond_t *cond,
86                                pthread_mutex_t *mutex,
87                                const struct timespec *abstime) {
88   int old_value = cond->sequence_number;
89 
90   int err = pthread_mutex_unlock(mutex);
91   if (err != 0)
92     return err;
93 
94   int status = __libnacl_irt_futex.futex_wait_abs(&cond->sequence_number,
95                                                   old_value, abstime);
96 
97   err = pthread_mutex_lock(mutex);
98   if (err != 0)
99     return err;
100 
101   /*
102    * futex_wait() can return EWOULDBLOCK but pthread_cond_wait() is
103    * not allowed to return that.
104    */
105   if (status == ETIMEDOUT) {
106     return ETIMEDOUT;
107   } else {
108     return 0;
109   }
110 }
111 
pthread_condattr_init(pthread_condattr_t * attr)112 int pthread_condattr_init(pthread_condattr_t *attr) {
113   return 0;
114 }
115 
pthread_condattr_destroy(pthread_condattr_t * attr)116 int pthread_condattr_destroy(pthread_condattr_t *attr) {
117   return 0;
118 }
119 
pthread_condattr_getpshared(const pthread_condattr_t * attr,int * pshared)120 int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *pshared) {
121   *pshared = PTHREAD_PROCESS_PRIVATE;
122   return 0;
123 }
124 
pthread_condattr_setpshared(pthread_condattr_t * attr,int pshared)125 int pthread_condattr_setpshared(pthread_condattr_t *attr, int pshared) {
126   switch (pshared) {
127     case PTHREAD_PROCESS_PRIVATE:
128       return 0;
129     case PTHREAD_PROCESS_SHARED:
130       return ENOTSUP;
131     default:
132       return EINVAL;
133   }
134 }
135 
pthread_condattr_getclock(const pthread_condattr_t * attr,clockid_t * clock_id)136 int pthread_condattr_getclock(const pthread_condattr_t *attr,
137                               clockid_t *clock_id) {
138   *clock_id = CLOCK_REALTIME;
139   return 0;
140 }
141 
pthread_condattr_setclock(pthread_condattr_t * attr,clockid_t clock_id)142 int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) {
143   switch (clock_id) {
144     case CLOCK_REALTIME:  /* Only one really supported.  */
145       return 0;
146 
147     case CLOCK_MONOTONIC: /* A "known clock", but not supported for this.  */
148       return ENOTSUP;
149 
150     default:
151       /*
152        * Anything else is either not a "known clock", or is a CPU-time clock.
153        */
154       return EINVAL;
155   }
156 }
157