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 _NotifyChecked(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::ListAll() 266 { 267 kprintf(" variable object (type) waiting threads\n"); 268 kprintf("------------------------------------------------------------\n"); 269 ConditionVariableHash::Iterator it(&sConditionVariableHash); 270 while (ConditionVariable* variable = it.Next()) { 271 // count waiting threads 272 int count = variable->fEntries.Count(); 273 274 kprintf("%p %p %-20s %15d\n", variable, variable->fObject, 275 variable->fObjectType, count); 276 } 277 } 278 279 280 void 281 ConditionVariable::Dump() const 282 { 283 kprintf("condition variable %p\n", this); 284 kprintf(" object: %p (%s)\n", fObject, fObjectType); 285 kprintf(" threads:"); 286 287 for (EntryList::ConstIterator it = fEntries.GetIterator(); 288 ConditionVariableEntry* entry = it.Next();) { 289 kprintf(" %ld", entry->fThread->id); 290 } 291 kprintf("\n"); 292 } 293 294 295 void 296 ConditionVariable::_Notify(bool all, bool threadsLocked) 297 { 298 InterruptsLocker _; 299 SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock); 300 SpinLocker locker(sConditionVariablesLock); 301 302 if (!fEntries.IsEmpty()) 303 _NotifyChecked(all, B_OK); 304 } 305 306 307 /*! Called with interrupts disabled and the condition variable spinlock and 308 thread lock held. 309 */ 310 void 311 ConditionVariable::_NotifyChecked(bool all, status_t result) 312 { 313 // dequeue and wake up the blocked threads 314 while (ConditionVariableEntry* entry = fEntries.RemoveHead()) { 315 entry->fVariable = NULL; 316 317 if (entry->fWaitStatus <= 0) 318 continue; 319 320 if (entry->fWaitStatus == STATUS_WAITING) 321 thread_unblock_locked(entry->fThread, result); 322 323 entry->fWaitStatus = result; 324 325 if (!all) 326 break; 327 } 328 } 329 330 331 // #pragma mark - 332 333 334 void 335 condition_variable_init() 336 { 337 new(&sConditionVariableHash) ConditionVariableHash; 338 339 status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); 340 if (error != B_OK) { 341 panic("condition_variable_init(): Failed to init hash table: %s", 342 strerror(error)); 343 } 344 345 add_debugger_command_etc("cvar", &dump_condition_variable, 346 "Dump condition variable info", 347 "<address>\n" 348 "Prints info for the specified condition variable.\n" 349 " <address> - Address of the condition variable or the object it is\n" 350 " associated with.\n", 0); 351 add_debugger_command_etc("cvars", &list_condition_variables, 352 "List condition variables", 353 "\n" 354 "Lists all existing condition variables\n", 0); 355 } 356