* Replaced the broken condition variable implementation (the
sConditionVariablesLock is needed) with the one I used for the UserlandFS, which is a slightly adjusted kernel version. * Got rid of sThreadsLock. It's not necessary and didn't work like used anyway. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@29448 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,45 +1,102 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2009, Haiku Inc. All Rights Reserved.
|
* Copyright 2007-2009, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT license.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
|
||||||
* Authors:
|
|
||||||
* Ingo Weinhold, [email protected].
|
|
||||||
* Axel Dörfler, [email protected].
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <condition_variable.h>
|
#include "condition_variable.h"
|
||||||
|
|
||||||
#include <new>
|
#include <new>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <Debug.h>
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
// libroot
|
||||||
|
#include <user_thread.h>
|
||||||
|
|
||||||
|
// system
|
||||||
|
#include <syscalls.h>
|
||||||
|
#include <user_thread_defs.h>
|
||||||
|
|
||||||
#include <lock.h>
|
#include <lock.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
#include "thread.h"
|
||||||
|
|
||||||
|
|
||||||
#define STATUS_ADDED 1
|
#define STATUS_ADDED 1
|
||||||
#define STATUS_WAITING 2
|
#define STATUS_WAITING 2
|
||||||
|
|
||||||
|
|
||||||
struct condition_private {
|
static const int kConditionVariableHashSize = 512;
|
||||||
mutex lock;
|
|
||||||
sem_id wait_sem;
|
|
||||||
const void* object;
|
struct ConditionVariableHashDefinition {
|
||||||
|
typedef const void* KeyType;
|
||||||
|
typedef ConditionVariable ValueType;
|
||||||
|
|
||||||
|
size_t HashKey(const void* key) const
|
||||||
|
{ return (size_t)key; }
|
||||||
|
size_t Hash(ConditionVariable* variable) const
|
||||||
|
{ return (size_t)variable->fObject; }
|
||||||
|
bool Compare(const void* key, ConditionVariable* variable) const
|
||||||
|
{ return key == variable->fObject; }
|
||||||
|
HashTableLink<ConditionVariable>* GetLink(ConditionVariable* variable) const
|
||||||
|
{ return variable; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef OpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash;
|
||||||
|
static ConditionVariableHash sConditionVariableHash;
|
||||||
|
static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables");
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - ConditionVariableEntry
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
ConditionVariableEntry::Add(const void* object)
|
||||||
|
{
|
||||||
|
ASSERT(object != NULL);
|
||||||
|
|
||||||
|
fThread = get_current_thread();
|
||||||
|
|
||||||
|
MutexLocker _(sConditionVariablesLock);
|
||||||
|
|
||||||
|
fVariable = sConditionVariableHash.Lookup(object);
|
||||||
|
|
||||||
|
if (fVariable == NULL) {
|
||||||
|
fWaitStatus = B_ENTRY_NOT_FOUND;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fWaitStatus = STATUS_ADDED;
|
||||||
|
fVariable->fEntries.Add(this);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
|
ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
|
||||||
{
|
{
|
||||||
|
MutexLocker conditionLocker(sConditionVariablesLock);
|
||||||
|
|
||||||
if (fVariable == NULL)
|
if (fVariable == NULL)
|
||||||
return fWaitStatus;
|
return fWaitStatus;
|
||||||
|
|
||||||
condition_private* condition = (condition_private*)fVariable->fObject;
|
user_thread* userThread = get_user_thread();
|
||||||
|
|
||||||
|
userThread->wait_status = 1;
|
||||||
fWaitStatus = STATUS_WAITING;
|
fWaitStatus = STATUS_WAITING;
|
||||||
|
|
||||||
status_t status;
|
conditionLocker.Unlock();
|
||||||
do {
|
|
||||||
status = acquire_sem_etc(condition->wait_sem, 1, flags, timeout);
|
|
||||||
} while (status == B_INTERRUPTED);
|
|
||||||
|
|
||||||
mutex_lock(&condition->lock);
|
status_t error;
|
||||||
|
while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) {
|
||||||
|
}
|
||||||
|
|
||||||
|
conditionLocker.Lock();
|
||||||
|
|
||||||
// remove entry from variable, if not done yet
|
// remove entry from variable, if not done yet
|
||||||
if (fVariable != NULL) {
|
if (fVariable != NULL) {
|
||||||
@@ -47,39 +104,77 @@ ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
|
|||||||
fVariable = NULL;
|
fVariable = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_unlock(&condition->lock);
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
return status;
|
|
||||||
|
status_t
|
||||||
|
ConditionVariableEntry::Wait(const void* object, uint32 flags,
|
||||||
|
bigtime_t timeout)
|
||||||
|
{
|
||||||
|
if (Add(object))
|
||||||
|
return Wait(flags, timeout);
|
||||||
|
return B_ENTRY_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
ConditionVariableEntry::AddToVariable(ConditionVariable* variable)
|
ConditionVariableEntry::AddToVariable(ConditionVariable* variable)
|
||||||
{
|
{
|
||||||
|
fThread = get_current_thread();
|
||||||
|
|
||||||
|
MutexLocker _(sConditionVariablesLock);
|
||||||
|
|
||||||
fVariable = variable;
|
fVariable = variable;
|
||||||
fWaitStatus = STATUS_ADDED;
|
fWaitStatus = STATUS_ADDED;
|
||||||
fVariable->fEntries.Add(this);
|
fVariable->fEntries.Add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark - ConditionVariable
|
||||||
|
|
||||||
|
|
||||||
|
/*! Initialization method for anonymous (unpublished) condition variables.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
ConditionVariable::Init(const void* object, const char* objectType)
|
ConditionVariable::Init(const void* object, const char* objectType)
|
||||||
{
|
{
|
||||||
|
fObject = object;
|
||||||
|
fObjectType = objectType;
|
||||||
|
new(&fEntries) EntryList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ConditionVariable::Publish(const void* object, const char* objectType)
|
||||||
|
{
|
||||||
|
ASSERT(object != NULL);
|
||||||
|
|
||||||
|
fObject = object;
|
||||||
fObjectType = objectType;
|
fObjectType = objectType;
|
||||||
new(&fEntries) EntryList;
|
new(&fEntries) EntryList;
|
||||||
|
|
||||||
condition_private* condition = new condition_private;
|
MutexLocker locker(sConditionVariablesLock);
|
||||||
mutex_init(&condition->lock, objectType);
|
|
||||||
condition->wait_sem = create_sem(0, "condition variable wait");
|
|
||||||
if (condition->wait_sem < B_OK)
|
|
||||||
panic("cannot create condition variable.");
|
|
||||||
|
|
||||||
condition->object = object;
|
ASSERT(sConditionVariableHash.Lookup(object) == NULL);
|
||||||
|
|
||||||
fObject = condition;
|
sConditionVariableHash.InsertUnchecked(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ConditionVariable::Unpublish(bool threadsLocked)
|
||||||
|
{
|
||||||
|
ASSERT(fObject != NULL);
|
||||||
|
|
||||||
|
MutexLocker locker(sConditionVariablesLock);
|
||||||
|
|
||||||
|
sConditionVariableHash.RemoveUnchecked(this);
|
||||||
|
fObject = NULL;
|
||||||
|
fObjectType = NULL;
|
||||||
|
|
||||||
|
if (!fEntries.IsEmpty())
|
||||||
|
_NotifyChecked(true, B_ENTRY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -90,26 +185,60 @@ ConditionVariable::Add(ConditionVariableEntry* entry)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ConditionVariable::Wait(uint32 flags, bigtime_t timeout)
|
||||||
|
{
|
||||||
|
ConditionVariableEntry entry;
|
||||||
|
Add(&entry);
|
||||||
|
return entry.Wait(flags, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ConditionVariable::_Notify(bool all, bool threadsLocked)
|
ConditionVariable::_Notify(bool all, bool threadsLocked)
|
||||||
{
|
{
|
||||||
condition_private* condition = (condition_private*)fObject;
|
MutexLocker locker(sConditionVariablesLock);
|
||||||
mutex_lock(&condition->lock);
|
|
||||||
|
|
||||||
uint32 count = 0;
|
if (!fEntries.IsEmpty())
|
||||||
|
_NotifyChecked(all, B_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Called with interrupts disabled and the condition variable spinlock and
|
||||||
|
thread lock held.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ConditionVariable::_NotifyChecked(bool all, status_t result)
|
||||||
|
{
|
||||||
|
// dequeue and wake up the blocked threads
|
||||||
while (ConditionVariableEntry* entry = fEntries.RemoveHead()) {
|
while (ConditionVariableEntry* entry = fEntries.RemoveHead()) {
|
||||||
entry->fVariable = NULL;
|
entry->fVariable = NULL;
|
||||||
|
|
||||||
if (entry->fWaitStatus <= 0)
|
if (entry->fWaitStatus <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
entry->fWaitStatus = B_OK;
|
if (entry->fWaitStatus == STATUS_WAITING)
|
||||||
count++;
|
_kern_unblock_thread(get_thread_id(entry->fThread), result);
|
||||||
|
|
||||||
|
entry->fWaitStatus = result;
|
||||||
|
|
||||||
if (!all)
|
if (!all)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
release_sem_etc(condition->wait_sem, count, 0);
|
|
||||||
mutex_unlock(&condition->lock);
|
|
||||||
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
condition_variable_init()
|
||||||
|
{
|
||||||
|
status_t error = sConditionVariableHash.Init(kConditionVariableHashSize);
|
||||||
|
if (error != B_OK) {
|
||||||
|
panic("condition_variable_init(): Failed to init hash table: %s",
|
||||||
|
strerror(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef THREAD_H
|
||||||
|
#define THREAD_H
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct thread;
|
||||||
|
|
||||||
|
|
||||||
|
static inline struct thread*
|
||||||
|
get_current_thread()
|
||||||
|
{
|
||||||
|
return (struct thread*)find_thread(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline thread_id
|
||||||
|
get_thread_id(struct thread* thread)
|
||||||
|
{
|
||||||
|
return (thread_id)thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // THREAD_H
|
||||||
Reference in New Issue
Block a user