2 * Copyright 2007-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
6 #include <condition_variable.h>
13 #include <kscheduler.h>
16 #include <listeners.h>
17 #include <scheduling_analysis.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
;
49 list_condition_variables(int argc
, char** argv
)
51 ConditionVariable::ListAll();
57 dump_condition_variable(int argc
, char** argv
)
60 print_debugger_command_usage(argv
[0]);
64 addr_t address
= parse_expression(argv
[1]);
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
) {
78 set_debug_variable("_cvar", (addr_t
)variable
);
79 set_debug_variable("_object", (addr_t
)variable
->Object());
82 kprintf("no condition variable at or with key %p\n", (void*)address
);
88 // #pragma mark - ConditionVariableEntry
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
;
107 fWaitStatus
= STATUS_ADDED
;
108 fVariable
->fEntries
.Add(this);
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
);
125 SpinLocker
conditionLocker(sConditionVariablesLock
);
127 if (fVariable
== NULL
)
130 thread_prepare_to_block(fThread
, flags
,
131 THREAD_BLOCK_TYPE_CONDITION_VARIABLE
, fVariable
);
133 fWaitStatus
= STATUS_WAITING
;
135 conditionLocker
.Unlock();
138 if ((flags
& (B_RELATIVE_TIMEOUT
| B_ABSOLUTE_TIMEOUT
)) != 0)
139 error
= thread_block_with_timeout(flags
, timeout
);
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);
156 ConditionVariableEntry::Wait(const void* object
, uint32 flags
,
160 return Wait(flags
, timeout
);
161 return B_ENTRY_NOT_FOUND
;
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.
184 ConditionVariable::Init(const void* object
, const char* objectType
)
187 fObjectType
= objectType
;
188 new(&fEntries
) EntryList
;
190 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object
, objectType
));
191 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized
,
197 ConditionVariable::Publish(const void* object
, const char* objectType
)
199 ASSERT(object
!= NULL
);
202 fObjectType
= objectType
;
203 new(&fEntries
) EntryList
;
205 T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object
, objectType
));
206 NotifyWaitObjectListeners(&WaitObjectListener::ConditionVariableInitialized
,
210 SpinLocker
locker(sConditionVariablesLock
);
212 ASSERT_PRINT(sConditionVariableHash
.Lookup(object
) == NULL
,
213 "condition variable: %p\n", sConditionVariableHash
.Lookup(object
));
215 sConditionVariableHash
.InsertUnchecked(this);
220 ConditionVariable::Unpublish()
222 ASSERT(fObject
!= NULL
);
224 InterruptsSpinLocker
locker(sConditionVariablesLock
);
227 ConditionVariable
* variable
= sConditionVariableHash
.Lookup(fObject
);
228 if (variable
!= this) {
229 panic("Condition variable %p not published, found: %p", this, variable
);
234 sConditionVariableHash
.RemoveUnchecked(this);
238 if (!fEntries
.IsEmpty())
239 _NotifyLocked(true, B_ENTRY_NOT_FOUND
);
244 ConditionVariable::Add(ConditionVariableEntry
* entry
)
246 entry
->AddToVariable(this);
251 ConditionVariable::Wait(uint32 flags
, bigtime_t timeout
)
253 ConditionVariableEntry entry
;
255 return entry
.Wait(flags
, timeout
);
260 ConditionVariable::NotifyOne(const void* object
, status_t result
)
262 InterruptsSpinLocker
locker(sConditionVariablesLock
);
263 ConditionVariable
* variable
= sConditionVariableHash
.Lookup(object
);
265 if (variable
== NULL
)
268 variable
->NotifyOne(result
);
273 ConditionVariable::NotifyAll(const void* object
, status_t result
)
275 InterruptsSpinLocker
locker(sConditionVariablesLock
);
276 ConditionVariable
* variable
= sConditionVariableHash
.Lookup(object
);
278 if (variable
== NULL
)
281 variable
->NotifyAll(result
);
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
);
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
);
317 ConditionVariable::_Notify(bool all
, status_t result
)
319 InterruptsSpinLocker
locker(sConditionVariablesLock
);
321 if (!fEntries
.IsEmpty()) {
323 panic("tried to notify with invalid result %" B_PRId32
"\n", result
);
327 _NotifyLocked(all
, result
);
332 /*! Called with interrupts disabled and the condition variable spinlock and
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)
345 if (entry
->fWaitStatus
== STATUS_WAITING
) {
346 SpinLocker
_(entry
->fThread
->scheduler_lock
);
347 thread_unblock_locked(entry
->fThread
, result
);
350 entry
->fWaitStatus
= result
;
362 condition_variable_init()
364 new(&sConditionVariableHash
) ConditionVariableHash
;
366 status_t error
= sConditionVariableHash
.Init(kConditionVariableHashSize
);
368 panic("condition_variable_init(): Failed to init hash table: %s",
372 add_debugger_command_etc("cvar", &dump_condition_variable
,
373 "Dump condition variable info",
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",
381 "Lists all existing condition variables\n", 0);