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