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 31static const int kConditionVariableHashSize = 512; 32 33 34struct 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 48typedef BOpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash; 49static ConditionVariableHash sConditionVariableHash; 50static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables"); 51 52 53// #pragma mark - ConditionVariableEntry 54 55 56bool 57ConditionVariableEntry::Add(const void* object) 58{ 59 ASSERT(object != NULL); 60 61 fThread = get_current_thread(); 62 63 MutexLocker _(sConditionVariablesLock); 64 65 fVariable = sConditionVariableHash.Lookup(object); 66 67 if (fVariable == NULL) { 68 fWaitStatus = B_ENTRY_NOT_FOUND; 69 return false; 70 } 71 72 fWaitStatus = STATUS_ADDED; 73 fVariable->fEntries.Add(this); 74 75 return true; 76} 77 78 79status_t 80ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout) 81{ 82 MutexLocker conditionLocker(sConditionVariablesLock); 83 84 if (fVariable == NULL) 85 return fWaitStatus; 86 87 user_thread* userThread = get_user_thread(); 88 89 userThread->wait_status = 1; 90 fWaitStatus = STATUS_WAITING; 91 92 conditionLocker.Unlock(); 93 94 status_t error; 95 while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) { 96 } 97 98 conditionLocker.Lock(); 99 100 // remove entry from variable, if not done yet 101 if (fVariable != NULL) { 102 fVariable->fEntries.Remove(this); 103 fVariable = NULL; 104 } 105 106 return error; 107} 108 109 110status_t 111ConditionVariableEntry::Wait(const void* object, uint32 flags, 112 bigtime_t timeout) 113{ 114 if (Add(object)) 115 return Wait(flags, timeout); 116 return B_ENTRY_NOT_FOUND; 117} 118 119 120inline void 121ConditionVariableEntry::AddToLockedVariable(ConditionVariable* variable) 122{ 123 fThread = get_current_thread(); 124 fVariable = variable; 125 fWaitStatus = STATUS_ADDED; 126 fVariable->fEntries.Add(this); 127} 128 129 130// #pragma mark - ConditionVariable 131 132 133/*! Initialization method for anonymous (unpublished) condition variables. 134*/ 135void 136ConditionVariable::Init(const void* object, const char* objectType) 137{ 138 fObject = object; 139 fObjectType = objectType; 140 new(&fEntries) EntryList; 141} 142 143 144void 145ConditionVariable::Publish(const void* object, const char* objectType) 146{ 147 ASSERT(object != NULL); 148 149 fObject = object; 150 fObjectType = objectType; 151 new(&fEntries) EntryList; 152 153 MutexLocker locker(sConditionVariablesLock); 154 155 ASSERT(sConditionVariableHash.Lookup(object) == NULL); 156 157 sConditionVariableHash.InsertUnchecked(this); 158} 159 160 161void 162ConditionVariable::Unpublish() 163{ 164 ASSERT(fObject != NULL); 165 166 MutexLocker locker(sConditionVariablesLock); 167 168 sConditionVariableHash.RemoveUnchecked(this); 169 fObject = NULL; 170 fObjectType = NULL; 171 172 if (!fEntries.IsEmpty()) 173 _NotifyLocked(true, B_ENTRY_NOT_FOUND); 174} 175 176 177void 178ConditionVariable::Add(ConditionVariableEntry* entry) 179{ 180 MutexLocker _(sConditionVariablesLock); 181 entry->AddToLockedVariable(this); 182} 183 184 185status_t 186ConditionVariable::Wait(uint32 flags, bigtime_t timeout) 187{ 188 ConditionVariableEntry entry; 189 Add(&entry); 190 return entry.Wait(flags, timeout); 191} 192 193 194void 195ConditionVariable::_Notify(bool all, status_t result) 196{ 197 MutexLocker locker(sConditionVariablesLock); 198 199 if (!fEntries.IsEmpty()) { 200 if (result > B_OK) { 201 panic("tried to notify with invalid result %" B_PRId32 "\n", 202 result); 203 result = B_ERROR; 204 } 205 206 _NotifyLocked(all, result); 207 } 208} 209 210 211/*! Called with interrupts disabled and the condition variable spinlock and 212 thread lock held. 213*/ 214void 215ConditionVariable::_NotifyLocked(bool all, status_t result) 216{ 217 // dequeue and wake up the blocked threads 218 while (ConditionVariableEntry* entry = fEntries.RemoveHead()) { 219 entry->fVariable = NULL; 220 221 if (entry->fWaitStatus <= 0) 222 continue; 223 224 if (entry->fWaitStatus == STATUS_WAITING) 225 _kern_unblock_thread(get_thread_id(entry->fThread), result); 226 227 entry->fWaitStatus = result; 228 229 if (!all) 230 break; 231 } 232} 233 234 235// #pragma mark - 236 237 238void 239condition_variable_init() 240{ 241 status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); 242 if (error != B_OK) { 243 panic("condition_variable_init(): Failed to init hash table: %s", 244 strerror(error)); 245 } 246} 247