1 /* 2 * Copyright 2007-2008, Ingo Weinhold, bonefish@cs.tu-berlin.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 <debug.h> 13 #include <kscheduler.h> 14 #include <ksignal.h> 15 #include <int.h> 16 #include <listeners.h> 17 #include <scheduling_analysis.h> 18 #include <thread.h> 19 #include <util/AutoLock.h> 20 21 22 #define STATUS_ADDED 1 23 #define STATUS_WAITING 2 24 25 26 static const int kConditionVariableHashSize = 512; 27 28 29 struct ConditionVariableHashDefinition { 30 typedef const void* KeyType; 31 typedef ConditionVariable ValueType; 32 33 size_t HashKey(const void* key) const 34 { return (size_t)key; } 35 size_t Hash(ConditionVariable* variable) const 36 { return (size_t)variable->fObject; } 37 bool Compare(const void* key, ConditionVariable* variable) const 38 { return key == variable->fObject; } 39 ConditionVariable*& GetLink(ConditionVariable* variable) const 40 { return variable->fNext; } 41 }; 42 43 typedef BOpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash; 44 static ConditionVariableHash sConditionVariableHash; 45 static spinlock sConditionVariablesLock; 46 47 48 static int 49 list_condition_variables(int argc, char** argv) 50 { 51 ConditionVariable::ListAll(); 52 return 0; 53 } 54 55 56 static int 57 dump_condition_variable(int argc, char** argv) 58 { 59 if (argc != 2) { 60 print_debugger_command_usage(argv[0]); 61 return 0; 62 } 63 64 addr_t address = parse_expression(argv[1]); 65 if (address == 0) 66 return 0; 67 68 ConditionVariable* variable = sConditionVariableHash.Lookup((void*)address); 69 70 if (variable == NULL) { 71 // It must be a direct pointer to a condition variable. 72 variable = (ConditionVariable*)address; 73 } 74 75 if (variable != NULL) { 76 variable->Dump(); 77 78 set_debug_variable("_cvar", (addr_t)variable); 79 set_debug_variable("_object", (addr_t)variable->Object()); 80 81 } else 82 kprintf("no condition variable at or with key %p\n", (void*)address); 83 84 return 0; 85 } 86 87 88 // #pragma mark - ConditionVariableEntry 89 90 91 bool 92 ConditionVariableEntry::Add(const void* object) 93 { 94 ASSERT(object != NULL); 95 96 fThread = thread_get_current_thread(); 97 98 InterruptsSpinLocker _(sConditionVariablesLock); 99 100 fVariable = sConditionVariableHash.Lookup(object); 101 102 if (fVariable == NULL) { 103 fWaitStatus = B_ENTRY_NOT_FOUND; 104 return false; 105 } 106 107 fWaitStatus = STATUS_ADDED; 108 fVariable->fEntries.Add(this); 109 110 return true; 111 } 112 113 114 status_t 115 ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout) 116 { 117 if (!are_interrupts_enabled()) { 118 panic("ConditionVariableEntry::Wait() called with interrupts " 119 "disabled, entry: %p, variable: %p", this, fVariable); 120 return B_ERROR; 121 } 122 123 InterruptsLocker _; 124 125 SpinLocker conditionLocker(sConditionVariablesLock); 126 127 if (fVariable == NULL) 128 return fWaitStatus; 129 130 thread_prepare_to_block(fThread, flags, 131 THREAD_BLOCK_TYPE_CONDITION_VARIABLE, fVariable); 132 133 fWaitStatus = STATUS_WAITING; 134 135 conditionLocker.Unlock(); 136 137 SpinLocker threadLocker(gThreadSpinlock); 138 139 status_t error; 140 if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) != 0) 141 error = thread_block_with_timeout_locked(flags, timeout); 142 else 143 error = thread_block_locked(thread_get_current_thread()); 144 threadLocker.Unlock(); 145 146 conditionLocker.Lock(); 147 148 // remove entry from variable, if not done yet 149 if (fVariable != NULL) { 150 fVariable->fEntries.Remove(this); 151 fVariable = NULL; 152 } 153 154 return error; 155 } 156 157 158 status_t 159 ConditionVariableEntry::Wait(const void* object, uint32 flags, 160 bigtime_t timeout) 161 { 162 if (Add(object)) 163 return Wait(flags, timeout); 164 return B_ENTRY_NOT_FOUND; 165 } 166 167 168 inline void 169 ConditionVariableEntry::AddToVariable(ConditionVariable* variable) 170 { 171 fThread = thread_get_current_thread(); 172 173 InterruptsSpinLocker _(sConditionVariablesLock); 174 175 fVariable = variable; 176 fWaitStatus = STATUS_ADDED; 177 fVariable->fEntries.Add(this); 178 } 179 180 181 // #pragma mark - ConditionVariable 182 183 184 /*! Initialization method for anonymous (unpublished) condition variables. 185 */ 186 void 187 ConditionVariable::Init(const void* object, const char* objectType) 188 { 189 fObject = object; 190 fObjectType = objectType; 191 new(&fEntries) EntryList; 192 193 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType)); 194 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized, 195 this); 196 } 197 198 199 void 200 ConditionVariable::Publish(const void* object, const char* objectType) 201 { 202 ASSERT(object != NULL); 203 204 fObject = object; 205 fObjectType = objectType; 206 new(&fEntries) EntryList; 207 208 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType)); 209 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized, 210 this); 211 212 InterruptsLocker _; 213 SpinLocker locker(sConditionVariablesLock); 214 215 ASSERT_PRINT(sConditionVariableHash.Lookup(object) == NULL, 216 "condition variable: %p\n", sConditionVariableHash.Lookup(object)); 217 218 sConditionVariableHash.InsertUnchecked(this); 219 } 220 221 222 void 223 ConditionVariable::Unpublish(bool threadsLocked) 224 { 225 ASSERT(fObject != NULL); 226 227 InterruptsLocker _; 228 SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock); 229 SpinLocker locker(sConditionVariablesLock); 230 231 #if KDEBUG 232 ConditionVariable* variable = sConditionVariableHash.Lookup(fObject); 233 if (variable != this) { 234 panic("Condition variable %p not published, found: %p", this, variable); 235 return; 236 } 237 #endif 238 239 sConditionVariableHash.RemoveUnchecked(this); 240 fObject = NULL; 241 fObjectType = NULL; 242 243 if (!fEntries.IsEmpty()) 244 _NotifyLocked(true, B_ENTRY_NOT_FOUND); 245 } 246 247 248 void 249 ConditionVariable::Add(ConditionVariableEntry* entry) 250 { 251 entry->AddToVariable(this); 252 } 253 254 255 status_t 256 ConditionVariable::Wait(uint32 flags, bigtime_t timeout) 257 { 258 ConditionVariableEntry entry; 259 Add(&entry); 260 return entry.Wait(flags, timeout); 261 } 262 263 264 /*static*/ void 265 ConditionVariable::NotifyOne(const void* object, bool threadsLocked, 266 status_t result) 267 { 268 InterruptsSpinLocker locker(sConditionVariablesLock); 269 ConditionVariable* variable = sConditionVariableHash.Lookup(object); 270 locker.Unlock(); 271 if (variable == NULL) 272 return; 273 274 variable->NotifyOne(threadsLocked, result); 275 } 276 277 278 /*static*/ void 279 ConditionVariable::NotifyAll(const void* object, 280 bool threadsLocked, status_t result) 281 { 282 InterruptsSpinLocker locker(sConditionVariablesLock); 283 ConditionVariable* variable = sConditionVariableHash.Lookup(object); 284 locker.Unlock(); 285 if (variable == NULL) 286 return; 287 288 variable->NotifyAll(threadsLocked, result); 289 } 290 291 292 /*static*/ void 293 ConditionVariable::ListAll() 294 { 295 kprintf(" variable object (type) waiting threads\n"); 296 kprintf("------------------------------------------------------------\n"); 297 ConditionVariableHash::Iterator it(&sConditionVariableHash); 298 while (ConditionVariable* variable = it.Next()) { 299 // count waiting threads 300 int count = variable->fEntries.Count(); 301 302 kprintf("%p %p %-20s %15d\n", variable, variable->fObject, 303 variable->fObjectType, count); 304 } 305 } 306 307 308 void 309 ConditionVariable::Dump() const 310 { 311 kprintf("condition variable %p\n", this); 312 kprintf(" object: %p (%s)\n", fObject, fObjectType); 313 kprintf(" threads:"); 314 315 for (EntryList::ConstIterator it = fEntries.GetIterator(); 316 ConditionVariableEntry* entry = it.Next();) { 317 kprintf(" %ld", entry->fThread->id); 318 } 319 kprintf("\n"); 320 } 321 322 323 void 324 ConditionVariable::_Notify(bool all, bool threadsLocked, status_t result) 325 { 326 InterruptsLocker _; 327 SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock); 328 SpinLocker locker(sConditionVariablesLock); 329 330 if (!fEntries.IsEmpty()) { 331 if (result > B_OK) { 332 panic("tried to notify with invalid result %ld\n", result); 333 result = B_ERROR; 334 } 335 336 _NotifyLocked(all, result); 337 } 338 } 339 340 341 /*! Called with interrupts disabled and the condition variable spinlock and 342 thread lock held. 343 */ 344 void 345 ConditionVariable::_NotifyLocked(bool all, status_t result) 346 { 347 // dequeue and wake up the blocked threads 348 while (ConditionVariableEntry* entry = fEntries.RemoveHead()) { 349 entry->fVariable = NULL; 350 351 if (entry->fWaitStatus <= 0) 352 continue; 353 354 if (entry->fWaitStatus == STATUS_WAITING) 355 thread_unblock_locked(entry->fThread, result); 356 357 entry->fWaitStatus = result; 358 359 if (!all) 360 break; 361 } 362 } 363 364 365 // #pragma mark - 366 367 368 void 369 condition_variable_init() 370 { 371 new(&sConditionVariableHash) ConditionVariableHash; 372 373 status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); 374 if (error != B_OK) { 375 panic("condition_variable_init(): Failed to init hash table: %s", 376 strerror(error)); 377 } 378 379 add_debugger_command_etc("cvar", &dump_condition_variable, 380 "Dump condition variable info", 381 "<address>\n" 382 "Prints info for the specified condition variable.\n" 383 " <address> - Address of the condition variable or the object it is\n" 384 " associated with.\n", 0); 385 add_debugger_command_etc("cvars", &list_condition_variables, 386 "List condition variables", 387 "\n" 388 "Lists all existing condition variables\n", 0); 389 } 390