1 /* 2 * Copyright 2007-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "condition_variable.h" 7 8 #include <new> 9 #include <stdlib.h> 10 #include <string.h> 11 12 #include <KernelExport.h> 13 14 // libroot 15 #include <user_thread.h> 16 17 // system 18 #include <syscalls.h> 19 #include <user_thread_defs.h> 20 21 #include <lock.h> 22 #include <util/AutoLock.h> 23 24 #include "thread.h" 25 26 27 #define STATUS_ADDED 1 28 #define STATUS_WAITING 2 29 30 31 static const int kConditionVariableHashSize = 512; 32 33 34 struct ConditionVariableHashDefinition { 35 typedef const void* KeyType; 36 typedef ConditionVariable ValueType; 37 38 size_t HashKey(const void* key) const 39 { return (size_t)key; } 40 size_t Hash(ConditionVariable* variable) const 41 { return (size_t)variable->fObject; } 42 bool Compare(const void* key, ConditionVariable* variable) const 43 { return key == variable->fObject; } 44 ConditionVariable*& GetLink(ConditionVariable* variable) const 45 { return variable->fNext; } 46 }; 47 48 typedef BOpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash; 49 static ConditionVariableHash sConditionVariableHash; 50 static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables"); 51 52 53 // #pragma mark - ConditionVariableEntry 54 55 56 ConditionVariableEntry::ConditionVariableEntry() 57 : fVariable(NULL) 58 { 59 } 60 61 62 ConditionVariableEntry::~ConditionVariableEntry() 63 { 64 if (fVariable != NULL) 65 _RemoveFromVariable(); 66 } 67 68 69 bool 70 ConditionVariableEntry::Add(const void* object) 71 { 72 ASSERT(object != NULL); 73 74 fThread = get_current_thread(); 75 76 MutexLocker _(sConditionVariablesLock); 77 78 fVariable = sConditionVariableHash.Lookup(object); 79 80 if (fVariable == NULL) { 81 fWaitStatus = B_ENTRY_NOT_FOUND; 82 return false; 83 } 84 85 fWaitStatus = STATUS_ADDED; 86 fVariable->fEntries.Add(this); 87 88 return true; 89 } 90 91 92 status_t 93 ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout) 94 { 95 MutexLocker conditionLocker(sConditionVariablesLock); 96 97 if (fVariable == NULL) 98 return fWaitStatus; 99 100 user_thread* userThread = get_user_thread(); 101 102 userThread->wait_status = 1; 103 fWaitStatus = STATUS_WAITING; 104 105 conditionLocker.Unlock(); 106 107 status_t error; 108 while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) { 109 } 110 111 _RemoveFromVariable(); 112 return error; 113 } 114 115 116 status_t 117 ConditionVariableEntry::Wait(const void* object, uint32 flags, 118 bigtime_t timeout) 119 { 120 if (Add(object)) 121 return Wait(flags, timeout); 122 return B_ENTRY_NOT_FOUND; 123 } 124 125 126 inline void 127 ConditionVariableEntry::_AddToLockedVariable(ConditionVariable* variable) 128 { 129 fThread = get_current_thread(); 130 fVariable = variable; 131 fWaitStatus = STATUS_ADDED; 132 fVariable->fEntries.Add(this); 133 } 134 135 136 void 137 ConditionVariableEntry::_RemoveFromVariable() 138 { 139 MutexLocker _(sConditionVariablesLock); 140 if (fVariable != NULL) { 141 fVariable->fEntries.Remove(this); 142 fVariable = NULL; 143 } 144 } 145 146 147 // #pragma mark - ConditionVariable 148 149 150 /*! Initialization method for anonymous (unpublished) condition variables. 151 */ 152 void 153 ConditionVariable::Init(const void* object, const char* objectType) 154 { 155 fObject = object; 156 fObjectType = objectType; 157 new(&fEntries) EntryList; 158 } 159 160 161 void 162 ConditionVariable::Publish(const void* object, const char* objectType) 163 { 164 ASSERT(object != NULL); 165 166 fObject = object; 167 fObjectType = objectType; 168 new(&fEntries) EntryList; 169 170 MutexLocker locker(sConditionVariablesLock); 171 172 ASSERT(sConditionVariableHash.Lookup(object) == NULL); 173 174 sConditionVariableHash.InsertUnchecked(this); 175 } 176 177 178 void 179 ConditionVariable::Unpublish() 180 { 181 ASSERT(fObject != NULL); 182 183 MutexLocker locker(sConditionVariablesLock); 184 185 sConditionVariableHash.RemoveUnchecked(this); 186 fObject = NULL; 187 fObjectType = NULL; 188 189 if (!fEntries.IsEmpty()) 190 _NotifyLocked(true, B_ENTRY_NOT_FOUND); 191 } 192 193 194 void 195 ConditionVariable::Add(ConditionVariableEntry* entry) 196 { 197 MutexLocker _(sConditionVariablesLock); 198 entry->_AddToLockedVariable(this); 199 } 200 201 202 status_t 203 ConditionVariable::Wait(uint32 flags, bigtime_t timeout) 204 { 205 ConditionVariableEntry entry; 206 Add(&entry); 207 return entry.Wait(flags, timeout); 208 } 209 210 211 int32 212 ConditionVariable::_Notify(bool all, status_t result) 213 { 214 MutexLocker locker(sConditionVariablesLock); 215 216 if (!fEntries.IsEmpty()) { 217 if (result > B_OK) { 218 panic("tried to notify with invalid result %" B_PRId32 "\n", 219 result); 220 result = B_ERROR; 221 } 222 223 return _NotifyLocked(all, result); 224 } 225 return 0; 226 } 227 228 229 /*! Called with interrupts disabled and the condition variable spinlock and 230 thread lock held. 231 */ 232 int32 233 ConditionVariable::_NotifyLocked(bool all, status_t result) 234 { 235 int32 notified = 0; 236 237 // dequeue and wake up the blocked threads 238 while (ConditionVariableEntry* entry = fEntries.RemoveHead()) { 239 entry->fVariable = NULL; 240 241 if (entry->fWaitStatus <= 0) 242 continue; 243 244 if (entry->fWaitStatus == STATUS_WAITING) 245 _kern_unblock_thread(get_thread_id(entry->fThread), result); 246 247 entry->fWaitStatus = result; 248 249 notified++; 250 if (!all) 251 break; 252 } 253 254 return notified; 255 } 256 257 258 // #pragma mark - 259 260 261 void 262 condition_variable_init() 263 { 264 status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); 265 if (error != B_OK) { 266 panic("condition_variable_init(): Failed to init hash table: %s", 267 strerror(error)); 268 } 269 } 270