1 /* 2 * Copyright 2007-2011, 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 <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 schedulerLocker(gSchedulerLock); 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 145 schedulerLocker.Unlock(); 146 147 conditionLocker.Lock(); 148 149 // remove entry from variable, if not done yet 150 if (fVariable != NULL) { 151 fVariable->fEntries.Remove(this); 152 fVariable = NULL; 153 } 154 155 return error; 156 } 157 158 159 status_t 160 ConditionVariableEntry::Wait(const void* object, uint32 flags, 161 bigtime_t timeout) 162 { 163 if (Add(object)) 164 return Wait(flags, timeout); 165 return B_ENTRY_NOT_FOUND; 166 } 167 168 169 inline void 170 ConditionVariableEntry::AddToVariable(ConditionVariable* variable) 171 { 172 fThread = thread_get_current_thread(); 173 174 InterruptsSpinLocker _(sConditionVariablesLock); 175 176 fVariable = variable; 177 fWaitStatus = STATUS_ADDED; 178 fVariable->fEntries.Add(this); 179 } 180 181 182 // #pragma mark - ConditionVariable 183 184 185 /*! Initialization method for anonymous (unpublished) condition variables. 186 */ 187 void 188 ConditionVariable::Init(const void* object, const char* objectType) 189 { 190 fObject = object; 191 fObjectType = objectType; 192 new(&fEntries) EntryList; 193 194 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType)); 195 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized, 196 this); 197 } 198 199 200 void 201 ConditionVariable::Publish(const void* object, const char* objectType) 202 { 203 ASSERT(object != NULL); 204 205 fObject = object; 206 fObjectType = objectType; 207 new(&fEntries) EntryList; 208 209 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType)); 210 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized, 211 this); 212 213 InterruptsLocker _; 214 SpinLocker locker(sConditionVariablesLock); 215 216 ASSERT_PRINT(sConditionVariableHash.Lookup(object) == NULL, 217 "condition variable: %p\n", sConditionVariableHash.Lookup(object)); 218 219 sConditionVariableHash.InsertUnchecked(this); 220 } 221 222 223 void 224 ConditionVariable::Unpublish(bool schedulerLocked) 225 { 226 ASSERT(fObject != NULL); 227 228 InterruptsLocker _; 229 SpinLocker schedulerLocker(schedulerLocked ? NULL : &gSchedulerLock); 230 SpinLocker locker(sConditionVariablesLock); 231 232 #if KDEBUG 233 ConditionVariable* variable = sConditionVariableHash.Lookup(fObject); 234 if (variable != this) { 235 panic("Condition variable %p not published, found: %p", this, variable); 236 return; 237 } 238 #endif 239 240 sConditionVariableHash.RemoveUnchecked(this); 241 fObject = NULL; 242 fObjectType = NULL; 243 244 if (!fEntries.IsEmpty()) 245 _NotifyLocked(true, B_ENTRY_NOT_FOUND); 246 } 247 248 249 void 250 ConditionVariable::Add(ConditionVariableEntry* entry) 251 { 252 entry->AddToVariable(this); 253 } 254 255 256 status_t 257 ConditionVariable::Wait(uint32 flags, bigtime_t timeout) 258 { 259 ConditionVariableEntry entry; 260 Add(&entry); 261 return entry.Wait(flags, timeout); 262 } 263 264 265 /*static*/ void 266 ConditionVariable::NotifyOne(const void* object, bool schedulerLocked, 267 status_t result) 268 { 269 InterruptsSpinLocker locker(sConditionVariablesLock); 270 ConditionVariable* variable = sConditionVariableHash.Lookup(object); 271 locker.Unlock(); 272 if (variable == NULL) 273 return; 274 275 variable->NotifyOne(schedulerLocked, result); 276 } 277 278 279 /*static*/ void 280 ConditionVariable::NotifyAll(const void* object, bool schedulerLocked, 281 status_t result) 282 { 283 InterruptsSpinLocker locker(sConditionVariablesLock); 284 ConditionVariable* variable = sConditionVariableHash.Lookup(object); 285 locker.Unlock(); 286 if (variable == NULL) 287 return; 288 289 variable->NotifyAll(schedulerLocked, result); 290 } 291 292 293 /*static*/ void 294 ConditionVariable::ListAll() 295 { 296 kprintf(" variable object (type) waiting threads\n"); 297 kprintf("------------------------------------------------------------\n"); 298 ConditionVariableHash::Iterator it(&sConditionVariableHash); 299 while (ConditionVariable* variable = it.Next()) { 300 // count waiting threads 301 int count = variable->fEntries.Count(); 302 303 kprintf("%p %p %-20s %15d\n", variable, variable->fObject, 304 variable->fObjectType, count); 305 } 306 } 307 308 309 void 310 ConditionVariable::Dump() const 311 { 312 kprintf("condition variable %p\n", this); 313 kprintf(" object: %p (%s)\n", fObject, fObjectType); 314 kprintf(" threads:"); 315 316 for (EntryList::ConstIterator it = fEntries.GetIterator(); 317 ConditionVariableEntry* entry = it.Next();) { 318 kprintf(" %ld", entry->fThread->id); 319 } 320 kprintf("\n"); 321 } 322 323 324 void 325 ConditionVariable::_Notify(bool all, bool schedulerLocked, status_t result) 326 { 327 InterruptsLocker _; 328 SpinLocker schedulerLocker(schedulerLocked ? NULL : &gSchedulerLock); 329 SpinLocker locker(sConditionVariablesLock); 330 331 if (!fEntries.IsEmpty()) { 332 if (result > B_OK) { 333 panic("tried to notify with invalid result %ld\n", result); 334 result = B_ERROR; 335 } 336 337 _NotifyLocked(all, result); 338 } 339 } 340 341 342 /*! Called with interrupts disabled and the condition variable spinlock and 343 scheduler lock held. 344 */ 345 void 346 ConditionVariable::_NotifyLocked(bool all, status_t result) 347 { 348 // dequeue and wake up the blocked threads 349 while (ConditionVariableEntry* entry = fEntries.RemoveHead()) { 350 entry->fVariable = NULL; 351 352 if (entry->fWaitStatus <= 0) 353 continue; 354 355 if (entry->fWaitStatus == STATUS_WAITING) 356 thread_unblock_locked(entry->fThread, result); 357 358 entry->fWaitStatus = result; 359 360 if (!all) 361 break; 362 } 363 } 364 365 366 // #pragma mark - 367 368 369 void 370 condition_variable_init() 371 { 372 new(&sConditionVariableHash) ConditionVariableHash; 373 374 status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); 375 if (error != B_OK) { 376 panic("condition_variable_init(): Failed to init hash table: %s", 377 strerror(error)); 378 } 379 380 add_debugger_command_etc("cvar", &dump_condition_variable, 381 "Dump condition variable info", 382 "<address>\n" 383 "Prints info for the specified condition variable.\n" 384 " <address> - Address of the condition variable or the object it is\n" 385 " associated with.\n", 0); 386 add_debugger_command_etc("cvars", &list_condition_variables, 387 "List condition variables", 388 "\n" 389 "Lists all existing condition variables\n", 0); 390 } 391