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 HashTableLink<ConditionVariable>* GetLink(ConditionVariable* variable) const 45 { return variable; } 46 }; 47 48 typedef OpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash; 49 static ConditionVariableHash sConditionVariableHash; 50 static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables"); 51 52 53 // #pragma mark - ConditionVariableEntry 54 55 56 bool 57 ConditionVariableEntry::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 79 status_t 80 ConditionVariableEntry::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 110 status_t 111 ConditionVariableEntry::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 120 inline void 121 ConditionVariableEntry::AddToVariable(ConditionVariable* variable) 122 { 123 fThread = get_current_thread(); 124 125 MutexLocker _(sConditionVariablesLock); 126 127 fVariable = variable; 128 fWaitStatus = STATUS_ADDED; 129 fVariable->fEntries.Add(this); 130 } 131 132 133 // #pragma mark - ConditionVariable 134 135 136 /*! Initialization method for anonymous (unpublished) condition variables. 137 */ 138 void 139 ConditionVariable::Init(const void* object, const char* objectType) 140 { 141 fObject = object; 142 fObjectType = objectType; 143 new(&fEntries) EntryList; 144 } 145 146 147 void 148 ConditionVariable::Publish(const void* object, const char* objectType) 149 { 150 ASSERT(object != NULL); 151 152 fObject = object; 153 fObjectType = objectType; 154 new(&fEntries) EntryList; 155 156 MutexLocker locker(sConditionVariablesLock); 157 158 ASSERT(sConditionVariableHash.Lookup(object) == NULL); 159 160 sConditionVariableHash.InsertUnchecked(this); 161 } 162 163 164 void 165 ConditionVariable::Unpublish(bool threadsLocked) 166 { 167 ASSERT(fObject != NULL); 168 169 MutexLocker locker(sConditionVariablesLock); 170 171 sConditionVariableHash.RemoveUnchecked(this); 172 fObject = NULL; 173 fObjectType = NULL; 174 175 if (!fEntries.IsEmpty()) 176 _NotifyChecked(true, B_ENTRY_NOT_FOUND); 177 } 178 179 180 void 181 ConditionVariable::Add(ConditionVariableEntry* entry) 182 { 183 entry->AddToVariable(this); 184 } 185 186 187 status_t 188 ConditionVariable::Wait(uint32 flags, bigtime_t timeout) 189 { 190 ConditionVariableEntry entry; 191 Add(&entry); 192 return entry.Wait(flags, timeout); 193 } 194 195 196 void 197 ConditionVariable::_Notify(bool all, bool threadsLocked) 198 { 199 MutexLocker locker(sConditionVariablesLock); 200 201 if (!fEntries.IsEmpty()) 202 _NotifyChecked(all, B_OK); 203 } 204 205 206 /*! Called with interrupts disabled and the condition variable spinlock and 207 thread lock held. 208 */ 209 void 210 ConditionVariable::_NotifyChecked(bool all, status_t result) 211 { 212 // dequeue and wake up the blocked threads 213 while (ConditionVariableEntry* entry = fEntries.RemoveHead()) { 214 entry->fVariable = NULL; 215 216 if (entry->fWaitStatus <= 0) 217 continue; 218 219 if (entry->fWaitStatus == STATUS_WAITING) 220 _kern_unblock_thread(get_thread_id(entry->fThread), result); 221 222 entry->fWaitStatus = result; 223 224 if (!all) 225 break; 226 } 227 } 228 229 230 // #pragma mark - 231 232 233 void 234 condition_variable_init() 235 { 236 status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); 237 if (error != B_OK) { 238 panic("condition_variable_init(): Failed to init hash table: %s", 239 strerror(error)); 240 } 241 } 242