1 /* 2 * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2007, Ryan Leavengood, leavengood@gmail.com. 4 * All rights reserved. Distributed under the terms of the MIT License. 5 */ 6 7 8 #include <pthread.h> 9 #include "pthread_private.h" 10 11 #include <stdlib.h> 12 #include <stdio.h> 13 #include <string.h> 14 15 #include <syscall_utils.h> 16 17 #include <syscalls.h> 18 #include <user_mutex_defs.h> 19 20 21 #define COND_FLAG_SHARED 0x01 22 #define COND_FLAG_MONOTONIC 0x02 23 24 25 static const pthread_condattr pthread_condattr_default = { 26 false, 27 CLOCK_REALTIME 28 }; 29 30 31 int 32 pthread_cond_init(pthread_cond_t* cond, const pthread_condattr_t* _attr) 33 { 34 const pthread_condattr* attr = _attr != NULL 35 ? *_attr : &pthread_condattr_default; 36 37 cond->flags = 0; 38 if (attr->process_shared) 39 cond->flags |= COND_FLAG_SHARED; 40 41 if (attr->clock_id == CLOCK_MONOTONIC) 42 cond->flags |= COND_FLAG_MONOTONIC; 43 44 cond->mutex = NULL; 45 cond->waiter_count = 0; 46 cond->lock = 0; 47 48 return 0; 49 } 50 51 52 int 53 pthread_cond_destroy(pthread_cond_t* cond) 54 { 55 return 0; 56 } 57 58 59 static status_t 60 cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex, uint32 flags, 61 bigtime_t timeout) 62 { 63 if (mutex->owner != find_thread(NULL)) { 64 // calling thread isn't mutex owner 65 return EPERM; 66 } 67 68 if (cond->mutex != NULL && cond->mutex != mutex) { 69 // condition variable already used with different mutex 70 return EINVAL; 71 } 72 73 cond->mutex = mutex; 74 cond->waiter_count++; 75 76 // make sure the user mutex we use for blocking is locked 77 atomic_or((int32*)&cond->lock, B_USER_MUTEX_LOCKED); 78 79 // atomically unlock the mutex and start waiting on the user mutex 80 mutex->owner = -1; 81 mutex->owner_count = 0; 82 83 status_t status = _kern_mutex_switch_lock((int32*)&mutex->lock, 84 (int32*)&cond->lock, "pthread condition", flags, timeout); 85 86 if (status == B_INTERRUPTED) { 87 // EINTR is not an allowed return value. We either have to restart 88 // waiting -- which we can't atomically -- or return a spurious 0. 89 status = 0; 90 } 91 92 pthread_mutex_lock(mutex); 93 94 cond->waiter_count--; 95 // If there are no more waiters, we can change mutexes. 96 if (cond->waiter_count == 0) 97 cond->mutex = NULL; 98 99 return status; 100 } 101 102 103 static inline void 104 cond_signal(pthread_cond_t* cond, bool broadcast) 105 { 106 if (cond->waiter_count == 0) 107 return; 108 109 // release the condition lock 110 _kern_mutex_unlock((int32*)&cond->lock, 111 broadcast ? B_USER_MUTEX_UNBLOCK_ALL : 0); 112 } 113 114 115 int 116 pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* _mutex) 117 { 118 RETURN_AND_TEST_CANCEL(cond_wait(cond, _mutex, 0, B_INFINITE_TIMEOUT)); 119 } 120 121 122 int 123 pthread_cond_clockwait(pthread_cond_t* cond, pthread_mutex_t* mutex, 124 clockid_t clock_id, const struct timespec* abstime) 125 { 126 if (abstime == NULL || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000 * 1000 * 1000) 127 RETURN_AND_TEST_CANCEL(EINVAL); 128 129 bigtime_t timeoutMicros = ((bigtime_t)abstime->tv_sec) * 1000000 130 + abstime->tv_nsec / 1000; 131 uint32 flags = 0; 132 switch (clock_id) { 133 case CLOCK_REALTIME: 134 flags = B_ABSOLUTE_REAL_TIME_TIMEOUT; 135 break; 136 case CLOCK_MONOTONIC : 137 flags = B_ABSOLUTE_TIMEOUT; 138 break; 139 default: 140 return B_BAD_VALUE; 141 } 142 143 RETURN_AND_TEST_CANCEL(cond_wait(cond, mutex, flags, timeoutMicros)); 144 } 145 146 147 int 148 pthread_cond_timedwait(pthread_cond_t* cond, pthread_mutex_t* mutex, 149 const struct timespec* abstime) 150 { 151 return pthread_cond_clockwait(cond, mutex, 152 (cond->flags & COND_FLAG_MONOTONIC) != 0 ? CLOCK_MONOTONIC : CLOCK_REALTIME, 153 abstime); 154 } 155 156 157 int 158 pthread_cond_broadcast(pthread_cond_t* cond) 159 { 160 cond_signal(cond, true); 161 return 0; 162 } 163 164 165 int 166 pthread_cond_signal(pthread_cond_t* cond) 167 { 168 cond_signal(cond, false); 169 return 0; 170 } 171