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 : #include <pthread.h>
8 : #include <sys/time.h>
9 : #include "native_client/src/shared/platform/nacl_sync.h"
10 : #include "native_client/src/include/atomic_ops.h"
11 : #include "native_client/src/include/nacl_base.h"
12 : #include "native_client/src/include/nacl_macros.h"
13 : #include "native_client/src/trusted/service_runtime/include/sys/time.h"
14 :
15 : static const uint64_t kMicrosecondsPerSecond = 1000 * 1000;
16 : static const uint64_t kNanosecondsPerMicrosecond = 1000;
17 :
18 : #if NACL_COND_BROADCAST_HACK
19 : #define ATOMIC_INC(_var) (AtomicIncrement((_var), (Atomic32)1))
20 : #define ATOMIC_DEC(_var) (AtomicIncrement((_var), (Atomic32)-1))
21 : #endif
22 :
23 : /* Condition variable C API */
24 :
25 265 : int NaClCondVarCtor(struct NaClCondVar *cvp) {
26 265 : if (0 != pthread_cond_init(&cvp->cv, (pthread_condattr_t *) 0)) {
27 0 : return 0;
28 : }
29 : #if NACL_COND_BROADCAST_HACK
30 : cvp->waiting = 0;
31 : #endif
32 265 : return 1;
33 : }
34 :
35 208 : void NaClCondVarDtor(struct NaClCondVar *cvp) {
36 208 : pthread_cond_destroy(&cvp->cv);
37 208 : }
38 :
39 1052 : NaClSyncStatus NaClCondVarSignal(struct NaClCondVar *cvp) {
40 1052 : pthread_cond_signal(&cvp->cv);
41 1052 : return NACL_SYNC_OK;
42 : }
43 :
44 : #if NACL_COND_BROADCAST_HACK
45 : /* Strange linking bug:
46 : * If pthread_cond_broadcast is not referenced in this file, then
47 : * the function is never linked in, but libstdc++ tries to use it
48 : * anyway, resulting in an exception being thrown.
49 : * BUG= http://code.google.com/p/nativeclient/issues/detail?id=1987
50 : */
51 : int (*fake_reference_to_pthread_cond_broadcast)(pthread_cond_t*) =
52 : pthread_cond_broadcast;
53 : #endif
54 :
55 31 : NaClSyncStatus NaClCondVarBroadcast(struct NaClCondVar *cvp) {
56 : #if NACL_COND_BROADCAST_HACK
57 : /* There is a race condition where cvp->waiting may be
58 : * incremented before a thread actually calls pthread_cond_(timed)wait.
59 : * In that case, we'll send an extra signal and it'll go nowhere.
60 : * This is not a problem, because broadcasting always
61 : * has this race condition. If both the broadcast and
62 : * wait are protected by the same mutex, then the waiting counter
63 : * will be accurate
64 : */
65 : uint32_t waiting = cvp->waiting;
66 : while (waiting > 0) {
67 : pthread_cond_signal(&cvp->cv);
68 : waiting--;
69 : }
70 : #else
71 31 : pthread_cond_broadcast(&cvp->cv);
72 : #endif
73 31 : return NACL_SYNC_OK;
74 : }
75 :
76 : NaClSyncStatus NaClCondVarWait(struct NaClCondVar *cvp,
77 245 : struct NaClMutex *mp) {
78 : #if NACL_COND_BROADCAST_HACK
79 : ATOMIC_INC(&cvp->waiting);
80 : #endif
81 245 : pthread_cond_wait(&cvp->cv, &mp->mu);
82 : #if NACL_COND_BROADCAST_HACK
83 : ATOMIC_DEC(&cvp->waiting);
84 : #endif
85 245 : return NACL_SYNC_OK;
86 : }
87 :
88 : NaClSyncStatus NaClCondVarTimedWaitRelative(
89 : struct NaClCondVar *cvp,
90 : struct NaClMutex *mp,
91 0 : NACL_TIMESPEC_T const *reltime) {
92 : uint64_t relative_wait_us =
93 : reltime->tv_sec * kMicrosecondsPerSecond +
94 0 : reltime->tv_nsec / kNanosecondsPerMicrosecond;
95 : uint64_t current_time_us;
96 : uint64_t wakeup_time_us;
97 : int result;
98 : struct timespec ts;
99 : struct timeval tv;
100 0 : struct timezone tz = { 0, 0 }; /* UTC */
101 0 : if (gettimeofday(&tv, &tz) == 0) {
102 0 : current_time_us = tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec;
103 : } else {
104 0 : return NACL_SYNC_INTERNAL_ERROR;
105 : }
106 0 : wakeup_time_us = current_time_us + relative_wait_us;
107 0 : ts.tv_sec = wakeup_time_us / kMicrosecondsPerSecond;
108 0 : ts.tv_nsec = (wakeup_time_us - ts.tv_sec * kMicrosecondsPerSecond) *
109 : kNanosecondsPerMicrosecond;
110 :
111 : #if NACL_COND_BROADCAST_HACK
112 : ATOMIC_INC(&cvp->waiting);
113 : #endif
114 0 : result = pthread_cond_timedwait(&cvp->cv, &mp->mu, &ts);
115 : #if NACL_COND_BROADCAST_HACK
116 : ATOMIC_DEC(&cvp->waiting);
117 : #endif
118 0 : if (0 == result) {
119 0 : return NACL_SYNC_OK;
120 : }
121 0 : return NACL_SYNC_CONDVAR_TIMEDOUT;
122 : }
123 :
124 : NaClSyncStatus NaClCondVarTimedWaitAbsolute(
125 : struct NaClCondVar *cvp,
126 : struct NaClMutex *mp,
127 0 : NACL_TIMESPEC_T const *abstime) {
128 : struct timespec ts;
129 : int result;
130 0 : ts.tv_sec = abstime->tv_sec;
131 0 : ts.tv_nsec = abstime->tv_nsec;
132 : #if NACL_COND_BROADCAST_HACK
133 : ATOMIC_INC(&cvp->waiting);
134 : #endif
135 0 : result = pthread_cond_timedwait(&cvp->cv, &mp->mu, &ts);
136 : #if NACL_COND_BROADCAST_HACK
137 : ATOMIC_DEC(&cvp->waiting);
138 : #endif
139 0 : if (0 == result) {
140 0 : return NACL_SYNC_OK;
141 : }
142 0 : return NACL_SYNC_CONDVAR_TIMEDOUT;
143 : }
|