btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / condition_variable.cpp
blob85c7a3f87bc005580be59edd251d4cc1b0f154c2
1 /*
2 * Copyright 2007-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <condition_variable.h>
8 #include <new>
9 #include <stdlib.h>
10 #include <string.h>
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>
22 #define STATUS_ADDED 1
23 #define STATUS_WAITING 2
26 static const int kConditionVariableHashSize = 512;
29 struct ConditionVariableHashDefinition {
30 typedef const void* KeyType;
31 typedef ConditionVariable ValueType;
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; }
43 typedef BOpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash;
44 static ConditionVariableHash sConditionVariableHash;
45 static spinlock sConditionVariablesLock;
48 static int
49 list_condition_variables(int argc, char** argv)
51 ConditionVariable::ListAll();
52 return 0;
56 static int
57 dump_condition_variable(int argc, char** argv)
59 if (argc != 2) {
60 print_debugger_command_usage(argv[0]);
61 return 0;
64 addr_t address = parse_expression(argv[1]);
65 if (address == 0)
66 return 0;
68 ConditionVariable* variable = sConditionVariableHash.Lookup((void*)address);
70 if (variable == NULL) {
71 // It must be a direct pointer to a condition variable.
72 variable = (ConditionVariable*)address;
75 if (variable != NULL) {
76 variable->Dump();
78 set_debug_variable("_cvar", (addr_t)variable);
79 set_debug_variable("_object", (addr_t)variable->Object());
81 } else
82 kprintf("no condition variable at or with key %p\n", (void*)address);
84 return 0;
88 // #pragma mark - ConditionVariableEntry
91 bool
92 ConditionVariableEntry::Add(const void* object)
94 ASSERT(object != NULL);
96 fThread = thread_get_current_thread();
98 InterruptsSpinLocker _(sConditionVariablesLock);
100 fVariable = sConditionVariableHash.Lookup(object);
102 if (fVariable == NULL) {
103 fWaitStatus = B_ENTRY_NOT_FOUND;
104 return false;
107 fWaitStatus = STATUS_ADDED;
108 fVariable->fEntries.Add(this);
110 return true;
114 status_t
115 ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
117 if (!are_interrupts_enabled()) {
118 panic("ConditionVariableEntry::Wait() called with interrupts "
119 "disabled, entry: %p, variable: %p", this, fVariable);
120 return B_ERROR;
123 InterruptsLocker _;
125 SpinLocker conditionLocker(sConditionVariablesLock);
127 if (fVariable == NULL)
128 return fWaitStatus;
130 thread_prepare_to_block(fThread, flags,
131 THREAD_BLOCK_TYPE_CONDITION_VARIABLE, fVariable);
133 fWaitStatus = STATUS_WAITING;
135 conditionLocker.Unlock();
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();
143 conditionLocker.Lock();
145 // remove entry from variable, if not done yet
146 if (fVariable != NULL) {
147 fVariable->fEntries.Remove(this);
148 fVariable = NULL;
151 return error;
155 status_t
156 ConditionVariableEntry::Wait(const void* object, uint32 flags,
157 bigtime_t timeout)
159 if (Add(object))
160 return Wait(flags, timeout);
161 return B_ENTRY_NOT_FOUND;
165 inline void
166 ConditionVariableEntry::AddToVariable(ConditionVariable* variable)
168 fThread = thread_get_current_thread();
170 InterruptsSpinLocker _(sConditionVariablesLock);
172 fVariable = variable;
173 fWaitStatus = STATUS_ADDED;
174 fVariable->fEntries.Add(this);
178 // #pragma mark - ConditionVariable
181 /*! Initialization method for anonymous (unpublished) condition variables.
183 void
184 ConditionVariable::Init(const void* object, const char* objectType)
186 fObject = object;
187 fObjectType = objectType;
188 new(&fEntries) EntryList;
190 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType));
191 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized,
192 this);
196 void
197 ConditionVariable::Publish(const void* object, const char* objectType)
199 ASSERT(object != NULL);
201 fObject = object;
202 fObjectType = objectType;
203 new(&fEntries) EntryList;
205 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType));
206 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized,
207 this);
209 InterruptsLocker _;
210 SpinLocker locker(sConditionVariablesLock);
212 ASSERT_PRINT(sConditionVariableHash.Lookup(object) == NULL,
213 "condition variable: %p\n", sConditionVariableHash.Lookup(object));
215 sConditionVariableHash.InsertUnchecked(this);
219 void
220 ConditionVariable::Unpublish()
222 ASSERT(fObject != NULL);
224 InterruptsSpinLocker locker(sConditionVariablesLock);
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;
232 #endif
234 sConditionVariableHash.RemoveUnchecked(this);
235 fObject = NULL;
236 fObjectType = NULL;
238 if (!fEntries.IsEmpty())
239 _NotifyLocked(true, B_ENTRY_NOT_FOUND);
243 void
244 ConditionVariable::Add(ConditionVariableEntry* entry)
246 entry->AddToVariable(this);
250 status_t
251 ConditionVariable::Wait(uint32 flags, bigtime_t timeout)
253 ConditionVariableEntry entry;
254 Add(&entry);
255 return entry.Wait(flags, timeout);
259 /*static*/ void
260 ConditionVariable::NotifyOne(const void* object, status_t result)
262 InterruptsSpinLocker locker(sConditionVariablesLock);
263 ConditionVariable* variable = sConditionVariableHash.Lookup(object);
264 locker.Unlock();
265 if (variable == NULL)
266 return;
268 variable->NotifyOne(result);
272 /*static*/ void
273 ConditionVariable::NotifyAll(const void* object, status_t result)
275 InterruptsSpinLocker locker(sConditionVariablesLock);
276 ConditionVariable* variable = sConditionVariableHash.Lookup(object);
277 locker.Unlock();
278 if (variable == NULL)
279 return;
281 variable->NotifyAll(result);
285 /*static*/ void
286 ConditionVariable::ListAll()
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();
295 kprintf("%p %p %-20s %15d\n", variable, variable->fObject,
296 variable->fObjectType, count);
301 void
302 ConditionVariable::Dump() const
304 kprintf("condition variable %p\n", this);
305 kprintf(" object: %p (%s)\n", fObject, fObjectType);
306 kprintf(" threads:");
308 for (EntryList::ConstIterator it = fEntries.GetIterator();
309 ConditionVariableEntry* entry = it.Next();) {
310 kprintf(" %" B_PRId32, entry->fThread->id);
312 kprintf("\n");
316 void
317 ConditionVariable::_Notify(bool all, status_t result)
319 InterruptsSpinLocker locker(sConditionVariablesLock);
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;
327 _NotifyLocked(all, result);
332 /*! Called with interrupts disabled and the condition variable spinlock and
333 scheduler lock held.
335 void
336 ConditionVariable::_NotifyLocked(bool all, status_t result)
338 // dequeue and wake up the blocked threads
339 while (ConditionVariableEntry* entry = fEntries.RemoveHead()) {
340 entry->fVariable = NULL;
342 if (entry->fWaitStatus <= 0)
343 continue;
345 if (entry->fWaitStatus == STATUS_WAITING) {
346 SpinLocker _(entry->fThread->scheduler_lock);
347 thread_unblock_locked(entry->fThread, result);
350 entry->fWaitStatus = result;
352 if (!all)
353 break;
358 // #pragma mark -
361 void
362 condition_variable_init()
364 new(&sConditionVariableHash) ConditionVariableHash;
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));
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);