* Introduced a set of functions (thread_prepare_to_block(),
thread_block(), thread_unblock(),...) that allow a thread to wait for
something without needing a semaphore or condition variable. It can
simply block and another thread can unblock it. Supports timeouts and
interrupting. Both semaphores and condition variables use this
common mechanism, now.
* Semaphores:
- Some simplifications due to the thread blocking mechanism.
- Changed locking order to sem -> thread. It was the other way around
before and when introducing the wait_for_objects() support I had
also introduced a situation where the locking was reverse, which
could potentially cause a dead lock on SMP systems.
- Instead of queueing thread structures, a semaphore queues
queued_thread entries now, which are created on the stack. The
thread::sem structure could thus be removed.
- Added sem_entry::net_count, which is sem_entry::count plus the
acquisition count of all waiting threads. This number is needed in
remove_thread_from_sem() and instead of computing it there we
maintain it.
- Fixed remove_thread_from_sem(). It would not unblock threads, if
the sem count was <= 0.
- Made sem::last_acquirer unconditional. It is actually needed for
sem_info::latest_holder. Fixed fill_sem_info() accordingly.
- Added some optional tracing output, though only via ktrace_printf().
* Condition variables:
- Could be simplified significantly through the use of the thread
blocking mechanism. Removed a good deal of unnecessary code.
- Moved the ConditionVariableEntry "flags" parameter from Wait() to
Add(), and adjusted all places where condition variables are used
accordingly.
* snooze() uses thread_block_with_timeout() instead of a semaphore.
* Simplified thread interrupting in the signal and user debugger code.
Instead of separate functions for threads waiting on a semaphore or
condititon variable, we only have a single thread_interrupt(), now.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25099 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
* Copyright 2007-2008, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef _KERNEL_CONDITION_VARIABLE_H
|
#ifndef _KERNEL_CONDITION_VARIABLE_H
|
||||||
@@ -40,24 +40,16 @@ public:
|
|||||||
inline PrivateConditionVariable* Variable() const
|
inline PrivateConditionVariable* Variable() const
|
||||||
{ return fVariable; }
|
{ return fVariable; }
|
||||||
|
|
||||||
class Private;
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool Add(const void* object);
|
bool Add(const void* object, uint32 flags);
|
||||||
status_t Wait(uint32 flags);
|
status_t Wait();
|
||||||
status_t Wait(const void* object, uint32 flags);
|
status_t Wait(const void* object, uint32 flags);
|
||||||
|
|
||||||
private:
|
|
||||||
void _Remove();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
PrivateConditionVariable* fVariable;
|
PrivateConditionVariable* fVariable;
|
||||||
struct thread* fThread;
|
struct thread* fThread;
|
||||||
uint32 fFlags;
|
|
||||||
status_t fResult;
|
|
||||||
|
|
||||||
friend class PrivateConditionVariable;
|
friend class PrivateConditionVariable;
|
||||||
friend class Private;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -103,8 +95,8 @@ public:
|
|||||||
template<typename Type = void>
|
template<typename Type = void>
|
||||||
class ConditionVariableEntry : public PrivateConditionVariableEntry {
|
class ConditionVariableEntry : public PrivateConditionVariableEntry {
|
||||||
public:
|
public:
|
||||||
inline bool Add(const Type* object);
|
inline bool Add(const Type* object, uint32 flags = 0);
|
||||||
inline status_t Wait(uint32 flags = 0);
|
inline status_t Wait();
|
||||||
inline status_t Wait(const Type* object, uint32 flags = 0);
|
inline status_t Wait(const Type* object, uint32 flags = 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -143,17 +135,17 @@ ConditionVariable<Type>::NotifyAll(bool threadsLocked)
|
|||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
inline bool
|
inline bool
|
||||||
ConditionVariableEntry<Type>::Add(const Type* object)
|
ConditionVariableEntry<Type>::Add(const Type* object, uint32 flags)
|
||||||
{
|
{
|
||||||
return PrivateConditionVariableEntry::Add(object);
|
return PrivateConditionVariableEntry::Add(object, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
inline status_t
|
inline status_t
|
||||||
ConditionVariableEntry<Type>::Wait(uint32 flags)
|
ConditionVariableEntry<Type>::Wait()
|
||||||
{
|
{
|
||||||
return PrivateConditionVariableEntry::Wait(flags);
|
return PrivateConditionVariableEntry::Wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -170,8 +162,6 @@ extern "C" {
|
|||||||
|
|
||||||
struct thread;
|
struct thread;
|
||||||
|
|
||||||
extern status_t condition_variable_interrupt_thread(struct thread* thread);
|
|
||||||
|
|
||||||
extern void condition_variable_init();
|
extern void condition_variable_init();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ extern "C" {
|
|||||||
|
|
||||||
extern status_t sem_init(struct kernel_args *args);
|
extern status_t sem_init(struct kernel_args *args);
|
||||||
extern int sem_delete_owned_sems(team_id owner);
|
extern int sem_delete_owned_sems(team_id owner);
|
||||||
extern status_t sem_interrupt_thread(struct thread *t);
|
|
||||||
extern int32 sem_used_sems(void);
|
extern int32 sem_used_sems(void);
|
||||||
extern int32 sem_max_sems(void);
|
extern int32 sem_max_sems(void);
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,11 @@
|
|||||||
#include <thread_types.h>
|
#include <thread_types.h>
|
||||||
#include <arch/thread.h>
|
#include <arch/thread.h>
|
||||||
|
|
||||||
|
// For the thread blocking inline functions only.
|
||||||
|
#include <kscheduler.h>
|
||||||
|
#include <ksignal.h>
|
||||||
|
|
||||||
|
|
||||||
struct kernel_args;
|
struct kernel_args;
|
||||||
struct select_info;
|
struct select_info;
|
||||||
|
|
||||||
@@ -72,6 +77,12 @@ status_t deselect_thread(int32 object, struct select_info *info, bool kernel);
|
|||||||
|
|
||||||
#define syscall_64_bit_return_value() arch_syscall_64_bit_return_value()
|
#define syscall_64_bit_return_value() arch_syscall_64_bit_return_value()
|
||||||
|
|
||||||
|
status_t thread_block();
|
||||||
|
status_t thread_block_with_timeout(uint32 timeoutFlags, bigtime_t timeout);
|
||||||
|
status_t thread_block_with_timeout_locked(uint32 timeoutFlags,
|
||||||
|
bigtime_t timeout);
|
||||||
|
bool thread_unblock(status_t threadID, status_t status);
|
||||||
|
|
||||||
// used in syscalls.c
|
// used in syscalls.c
|
||||||
status_t _user_set_thread_priority(thread_id thread, int32 newPriority);
|
status_t _user_set_thread_priority(thread_id thread, int32 newPriority);
|
||||||
status_t _user_rename_thread(thread_id thread, const char *name);
|
status_t _user_rename_thread(thread_id thread, const char *name);
|
||||||
@@ -100,4 +111,87 @@ int _user_setrlimit(int resource, const struct rlimit * rlp);
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\a thread must be the current thread.
|
||||||
|
Thread lock can be, but doesn't need to be held.
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
thread_is_interrupted(struct thread* thread, uint32 flags)
|
||||||
|
{
|
||||||
|
return ((flags & B_CAN_INTERRUPT)
|
||||||
|
&& (thread->sig_pending & ~thread->sig_block_mask) != 0)
|
||||||
|
|| ((flags & B_KILL_CAN_INTERRUPT)
|
||||||
|
&& (thread->sig_pending & KILL_SIGNALS));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
thread_is_blocked(struct thread* thread)
|
||||||
|
{
|
||||||
|
return thread->wait.status == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\a thread must be the current thread.
|
||||||
|
Thread lock can be, but doesn't need to be locked.
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
thread_prepare_to_block(struct thread* thread, uint32 flags, uint32 type,
|
||||||
|
void* object)
|
||||||
|
{
|
||||||
|
thread->wait.flags = flags;
|
||||||
|
thread->wait.type = type;
|
||||||
|
thread->wait.object = object;
|
||||||
|
atomic_set(&thread->wait.status, 1);
|
||||||
|
// Set status last to guarantee that the other fields are initialized
|
||||||
|
// when a thread is waiting.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline status_t
|
||||||
|
thread_block_locked(struct thread* thread)
|
||||||
|
{
|
||||||
|
if (thread->wait.status == 1) {
|
||||||
|
// check for signals, if interruptable
|
||||||
|
if (thread_is_interrupted(thread, thread->wait.flags)) {
|
||||||
|
thread->wait.status = B_INTERRUPTED;
|
||||||
|
} else {
|
||||||
|
thread->next_state = B_THREAD_WAITING;
|
||||||
|
scheduler_reschedule();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return thread->wait.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
thread_unblock_locked(struct thread* thread, status_t status)
|
||||||
|
{
|
||||||
|
if (atomic_test_and_set(&thread->wait.status, status, 1) != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// wake up the thread, if it is sleeping
|
||||||
|
if (thread->state == B_THREAD_WAITING)
|
||||||
|
scheduler_enqueue_in_run_queue(thread);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline status_t
|
||||||
|
thread_interrupt(struct thread* thread, bool kill)
|
||||||
|
{
|
||||||
|
if ((thread->wait.flags & B_CAN_INTERRUPT) != 0
|
||||||
|
|| (kill && (thread->wait.flags & B_KILL_CAN_INTERRUPT) != 0)) {
|
||||||
|
thread_unblock_locked(thread, B_INTERRUPTED);
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif /* _THREAD_H */
|
#endif /* _THREAD_H */
|
||||||
|
|||||||
@@ -55,6 +55,15 @@ typedef enum job_control_state {
|
|||||||
#define THREAD_STOPPED 0x3
|
#define THREAD_STOPPED 0x3
|
||||||
#define THREAD_CONTINUED 0x4
|
#define THREAD_CONTINUED 0x4
|
||||||
|
|
||||||
|
// The type of object a thread blocks on (thread::wait::type, set by
|
||||||
|
// thread_prepare_to_block()).
|
||||||
|
enum {
|
||||||
|
THREAD_BLOCK_TYPE_SEMAPHORE = 0,
|
||||||
|
THREAD_BLOCK_TYPE_CONDITION_VARIABLE = 1,
|
||||||
|
THREAD_BLOCK_TYPE_SNOOZE = 2,
|
||||||
|
THREAD_BLOCK_TYPE_USER_BASE = 10000
|
||||||
|
};
|
||||||
|
|
||||||
struct image;
|
struct image;
|
||||||
// defined in image.c
|
// defined in image.c
|
||||||
struct select_info;
|
struct select_info;
|
||||||
@@ -233,12 +242,12 @@ struct thread {
|
|||||||
bool was_yielded;
|
bool was_yielded;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
sem_id blocking;
|
status_t status; // current wait status
|
||||||
int32 count;
|
uint32 flags; // interrupable flags
|
||||||
int32 acquire_count;
|
uint32 type; // type of the object waited on
|
||||||
status_t acquire_status;
|
void* object; // pointer to the object waited on
|
||||||
int32 flags;
|
timer unblock_timer; // timer for block with timeout
|
||||||
} sem;
|
} wait;
|
||||||
|
|
||||||
struct PrivateConditionVariableEntry *condition_variable_entry;
|
struct PrivateConditionVariableEntry *condition_variable_entry;
|
||||||
|
|
||||||
|
|||||||
@@ -408,14 +408,14 @@ RequestOwner::Wait(bool interruptable)
|
|||||||
|
|
||||||
// add an entry to wait on
|
// add an entry to wait on
|
||||||
ConditionVariableEntry<> entry;
|
ConditionVariableEntry<> entry;
|
||||||
entry.Add(this);
|
entry.Add(this, interruptable ? B_CAN_INTERRUPT : 0);
|
||||||
|
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
|
|
||||||
// wait
|
// wait
|
||||||
TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this));
|
TRACE(("%p->RequestOwner::Wait(): waiting for condition...\n", this));
|
||||||
|
|
||||||
error = entry.Wait(interruptable ? B_CAN_INTERRUPT : 0);
|
error = entry.Wait();
|
||||||
|
|
||||||
TRACE(("%p->RequestOwner::Wait(): condition occurred: %lx\n", this,
|
TRACE(("%p->RequestOwner::Wait(): condition occurred: %lx\n", this,
|
||||||
error));
|
error));
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2007, Ingo Weinhold, [email protected].
|
* Copyright 2007-2008, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -92,30 +92,36 @@ dump_condition_variable(int argc, char** argv)
|
|||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
PrivateConditionVariableEntry::Add(const void* object)
|
PrivateConditionVariableEntry::Add(const void* object, uint32 flags)
|
||||||
{
|
{
|
||||||
ASSERT(object != NULL);
|
ASSERT(object != NULL);
|
||||||
|
|
||||||
fThread = thread_get_current_thread();
|
fThread = thread_get_current_thread();
|
||||||
fFlags = 0;
|
|
||||||
fResult = B_OK;
|
|
||||||
|
|
||||||
InterruptsLocker _;
|
InterruptsLocker _;
|
||||||
SpinLocker locker(sConditionVariablesLock);
|
SpinLocker locker(sConditionVariablesLock);
|
||||||
|
|
||||||
// add to the queue for the variable
|
|
||||||
fVariable = sConditionVariableHash.Lookup(object);
|
fVariable = sConditionVariableHash.Lookup(object);
|
||||||
if (fVariable)
|
|
||||||
fVariable->fEntries.Add(this);
|
|
||||||
else
|
|
||||||
fResult = B_ENTRY_NOT_FOUND;
|
|
||||||
|
|
||||||
return (fVariable != NULL);
|
struct thread* thread = thread_get_current_thread();
|
||||||
|
thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_CONDITION_VARIABLE,
|
||||||
|
fVariable);
|
||||||
|
|
||||||
|
if (fVariable == NULL) {
|
||||||
|
SpinLocker threadLocker(thread_spinlock);
|
||||||
|
thread_unblock_locked(thread, B_ENTRY_NOT_FOUND);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to the queue for the variable
|
||||||
|
fVariable->fEntries.Add(this);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
PrivateConditionVariableEntry::Wait(uint32 flags)
|
PrivateConditionVariableEntry::Wait()
|
||||||
{
|
{
|
||||||
if (!are_interrupts_enabled()) {
|
if (!are_interrupts_enabled()) {
|
||||||
panic("wait_for_condition_variable_entry() called with interrupts "
|
panic("wait_for_condition_variable_entry() called with interrupts "
|
||||||
@@ -124,77 +130,32 @@ PrivateConditionVariableEntry::Wait(uint32 flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
InterruptsLocker _;
|
InterruptsLocker _;
|
||||||
|
|
||||||
SpinLocker threadLocker(thread_spinlock);
|
SpinLocker threadLocker(thread_spinlock);
|
||||||
SpinLocker locker(sConditionVariablesLock);
|
status_t error = thread_block_locked(thread_get_current_thread());
|
||||||
|
|
||||||
// check whether this entry has already been notified
|
|
||||||
// (set the flags while at it)
|
|
||||||
if (fVariable == NULL)
|
|
||||||
return fResult;
|
|
||||||
fFlags = flags;
|
|
||||||
|
|
||||||
// When interruptable, check pending signals first
|
|
||||||
struct thread* thread = thread_get_current_thread();
|
|
||||||
if (((flags & B_CAN_INTERRUPT)
|
|
||||||
&& (thread->sig_pending & ~thread->sig_block_mask) != 0)
|
|
||||||
|| ((flags & B_KILL_CAN_INTERRUPT)
|
|
||||||
&& (thread->sig_pending & KILL_SIGNALS))) {
|
|
||||||
// remove entry from the variables
|
|
||||||
_Remove();
|
|
||||||
return B_INTERRUPTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait
|
|
||||||
thread->next_state = B_THREAD_WAITING;
|
|
||||||
thread->condition_variable_entry = this;
|
|
||||||
thread->sem.blocking = -1;
|
|
||||||
|
|
||||||
locker.Unlock();
|
|
||||||
scheduler_reschedule();
|
|
||||||
threadLocker.Unlock();
|
threadLocker.Unlock();
|
||||||
|
|
||||||
return fResult;
|
SpinLocker locker(sConditionVariablesLock);
|
||||||
|
|
||||||
|
// remove entry from variable, if not done yet
|
||||||
|
if (fVariable != NULL) {
|
||||||
|
fVariable->fEntries.Remove(this);
|
||||||
|
fVariable = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
PrivateConditionVariableEntry::Wait(const void* object, uint32 flags)
|
PrivateConditionVariableEntry::Wait(const void* object, uint32 flags)
|
||||||
{
|
{
|
||||||
if (Add(object))
|
if (Add(object, flags))
|
||||||
return Wait(flags);
|
return Wait();
|
||||||
return B_ENTRY_NOT_FOUND;
|
return B_ENTRY_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Removes the entry from its variable.
|
|
||||||
Interrupts must be disabled, sConditionVariablesLock must be held.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
PrivateConditionVariableEntry::_Remove()
|
|
||||||
{
|
|
||||||
if (fVariable) {
|
|
||||||
fVariable->fEntries.Remove(this);
|
|
||||||
fVariable = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class PrivateConditionVariableEntry::Private {
|
|
||||||
public:
|
|
||||||
inline Private(PrivateConditionVariableEntry& entry)
|
|
||||||
: fEntry(entry)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32 Flags() const { return fEntry.fFlags; }
|
|
||||||
inline void Remove() const { fEntry._Remove(); }
|
|
||||||
inline void SetResult(status_t result) { fEntry.fResult = result; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
PrivateConditionVariableEntry& fEntry;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - PrivateConditionVariable
|
// #pragma mark - PrivateConditionVariable
|
||||||
|
|
||||||
|
|
||||||
@@ -306,15 +267,7 @@ PrivateConditionVariable::_Notify(bool all, status_t result)
|
|||||||
while (PrivateConditionVariableEntry* entry = fEntries.RemoveHead()) {
|
while (PrivateConditionVariableEntry* entry = fEntries.RemoveHead()) {
|
||||||
entry->fVariable = NULL;
|
entry->fVariable = NULL;
|
||||||
|
|
||||||
struct thread* thread = entry->fThread;
|
thread_unblock_locked(entry->fThread, B_OK);
|
||||||
|
|
||||||
if (thread->condition_variable_entry != NULL)
|
|
||||||
thread->condition_variable_entry->fResult = result;
|
|
||||||
|
|
||||||
// wake up the thread
|
|
||||||
thread->condition_variable_entry = NULL;
|
|
||||||
if (thread->state == B_THREAD_WAITING)
|
|
||||||
scheduler_enqueue_in_run_queue(thread);
|
|
||||||
|
|
||||||
if (!all)
|
if (!all)
|
||||||
break;
|
break;
|
||||||
@@ -325,41 +278,6 @@ PrivateConditionVariable::_Notify(bool all, status_t result)
|
|||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
/*! Interrupts must be disabled, thread lock must be held.
|
|
||||||
*/
|
|
||||||
status_t
|
|
||||||
condition_variable_interrupt_thread(struct thread* thread)
|
|
||||||
{
|
|
||||||
SpinLocker locker(sConditionVariablesLock);
|
|
||||||
|
|
||||||
if (thread == NULL || thread->state != B_THREAD_WAITING
|
|
||||||
|| thread->condition_variable_entry == NULL) {
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateConditionVariableEntry* entry = thread->condition_variable_entry;
|
|
||||||
uint32 flags = PrivateConditionVariableEntry::Private(*entry).Flags();
|
|
||||||
|
|
||||||
// interruptable?
|
|
||||||
if ((flags & B_CAN_INTERRUPT) == 0
|
|
||||||
&& ((flags & B_KILL_CAN_INTERRUPT) == 0
|
|
||||||
|| (thread->sig_pending & KILL_SIGNALS) == 0)) {
|
|
||||||
return B_NOT_ALLOWED;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateConditionVariableEntry::Private(*entry).SetResult(B_INTERRUPTED);
|
|
||||||
|
|
||||||
// remove entry from its variable
|
|
||||||
PrivateConditionVariableEntry::Private(*entry).Remove();
|
|
||||||
|
|
||||||
// wake up the thread
|
|
||||||
thread->condition_variable_entry = NULL;
|
|
||||||
scheduler_enqueue_in_run_queue(thread);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
condition_variable_init()
|
condition_variable_init()
|
||||||
{
|
{
|
||||||
@@ -383,4 +301,3 @@ condition_variable_init()
|
|||||||
"\n"
|
"\n"
|
||||||
"Lists all existing condition variables\n", 0);
|
"Lists all existing condition variables\n", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2366,12 +2366,13 @@ _user_debug_thread(thread_id threadID)
|
|||||||
scheduler_enqueue_in_run_queue(thread);
|
scheduler_enqueue_in_run_queue(thread);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case B_THREAD_WAITING:
|
default:
|
||||||
// thread waiting: interrupt it
|
// thread may be waiting: interrupt it
|
||||||
if (thread->sem.blocking >= 0)
|
thread_interrupt(thread, false);
|
||||||
sem_interrupt_thread(thread);
|
// TODO: If the thread is already in the kernel and e.g.
|
||||||
else if (thread->condition_variable_entry)
|
// about to acquire a semaphore (before
|
||||||
condition_variable_interrupt_thread(thread);
|
// thread_prepare_to_block()), we won't interrupt it.
|
||||||
|
// Maybe we should rather send a signal (SIGTRAP).
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -359,13 +359,13 @@ Inode::WriteDataToBuffer(const void *_data, size_t *_length, bool nonBlocking)
|
|||||||
return B_WOULD_BLOCK;
|
return B_WOULD_BLOCK;
|
||||||
|
|
||||||
ConditionVariableEntry<> entry;
|
ConditionVariableEntry<> entry;
|
||||||
entry.Add(this);
|
entry.Add(this, B_CAN_INTERRUPT);
|
||||||
|
|
||||||
WriteRequest request(minToWrite);
|
WriteRequest request(minToWrite);
|
||||||
fWriteRequests.Add(&request);
|
fWriteRequests.Add(&request);
|
||||||
|
|
||||||
benaphore_unlock(&fRequestLock);
|
benaphore_unlock(&fRequestLock);
|
||||||
status_t status = entry.Wait(B_CAN_INTERRUPT);
|
status_t status = entry.Wait();
|
||||||
benaphore_lock(&fRequestLock);
|
benaphore_lock(&fRequestLock);
|
||||||
|
|
||||||
fWriteRequests.Remove(&request);
|
fWriteRequests.Remove(&request);
|
||||||
@@ -473,11 +473,11 @@ Inode::WaitForReadRequest(ReadRequest &request)
|
|||||||
|
|
||||||
// add the entry to wait on
|
// add the entry to wait on
|
||||||
ConditionVariableEntry<> entry;
|
ConditionVariableEntry<> entry;
|
||||||
entry.Add(&request);
|
entry.Add(&request, B_CAN_INTERRUPT);
|
||||||
|
|
||||||
// wait
|
// wait
|
||||||
benaphore_unlock(&fRequestLock);
|
benaphore_unlock(&fRequestLock);
|
||||||
status_t status = entry.Wait(B_CAN_INTERRUPT);
|
status_t status = entry.Wait();
|
||||||
benaphore_lock(&fRequestLock);
|
benaphore_lock(&fRequestLock);
|
||||||
|
|
||||||
// unpublish the condition variable
|
// unpublish the condition variable
|
||||||
|
|||||||
+182
-337
@@ -1,4 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||||
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
* Copyright 2002-2008, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*
|
*
|
||||||
@@ -21,6 +22,8 @@
|
|||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <team.h>
|
#include <team.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
#include <vfs.h>
|
#include <vfs.h>
|
||||||
#include <vm_low_memory.h>
|
#include <vm_low_memory.h>
|
||||||
#include <vm_page.h>
|
#include <vm_page.h>
|
||||||
@@ -34,13 +37,36 @@
|
|||||||
|
|
||||||
//#define TRACE_SEM
|
//#define TRACE_SEM
|
||||||
#ifdef TRACE_SEM
|
#ifdef TRACE_SEM
|
||||||
# define TRACE(x) dprintf x
|
# define TRACE(x) dprintf_no_syslog x
|
||||||
#else
|
#else
|
||||||
# define TRACE(x) ;
|
# define TRACE(x) ;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//#define KTRACE_SEM
|
||||||
|
#ifdef KTRACE_SEM
|
||||||
|
# define KTRACE(x...) ktrace_printf(x)
|
||||||
|
#else
|
||||||
|
# define KTRACE(x...) do {} while (false)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DEBUG_LAST_ACQUIRER
|
#define DEBUG_LAST_ACQUIRER
|
||||||
|
|
||||||
|
struct queued_thread : DoublyLinkedListLinkImpl<queued_thread> {
|
||||||
|
queued_thread(struct thread *thread, int32 count)
|
||||||
|
:
|
||||||
|
thread(thread),
|
||||||
|
count(count),
|
||||||
|
queued(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thread *thread;
|
||||||
|
int32 count;
|
||||||
|
bool queued;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<queued_thread> ThreadQueue;
|
||||||
|
|
||||||
struct sem_entry {
|
struct sem_entry {
|
||||||
sem_id id;
|
sem_id id;
|
||||||
spinlock lock; // protects only the id field when unused
|
spinlock lock; // protects only the id field when unused
|
||||||
@@ -48,12 +74,14 @@ struct sem_entry {
|
|||||||
// when slot in use
|
// when slot in use
|
||||||
struct {
|
struct {
|
||||||
int32 count;
|
int32 count;
|
||||||
struct thread_queue queue;
|
int32 net_count;
|
||||||
|
// count + acquisition count of all blocked
|
||||||
|
// threads
|
||||||
char *name;
|
char *name;
|
||||||
team_id owner; // if set to -1, means owned by a port
|
team_id owner; // if set to -1, means owned by a port
|
||||||
select_info *select_infos;
|
select_info *select_infos;
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
thread_id last_acquirer;
|
thread_id last_acquirer;
|
||||||
|
#ifdef DEBUG_LAST_ACQUIRER
|
||||||
int32 last_acquire_count;
|
int32 last_acquire_count;
|
||||||
thread_id last_releaser;
|
thread_id last_releaser;
|
||||||
int32 last_release_count;
|
int32 last_release_count;
|
||||||
@@ -66,6 +94,8 @@ struct sem_entry {
|
|||||||
struct sem_entry *next;
|
struct sem_entry *next;
|
||||||
} unused;
|
} unused;
|
||||||
} u;
|
} u;
|
||||||
|
|
||||||
|
ThreadQueue queue; // should be in u.used, but has a constructor
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int32 kMaxSemaphores = 131072;
|
static const int32 kMaxSemaphores = 131072;
|
||||||
@@ -84,24 +114,13 @@ static spinlock sem_spinlock = 0;
|
|||||||
#define GRAB_SEM_LOCK(s) acquire_spinlock(&(s).lock)
|
#define GRAB_SEM_LOCK(s) acquire_spinlock(&(s).lock)
|
||||||
#define RELEASE_SEM_LOCK(s) release_spinlock(&(s).lock)
|
#define RELEASE_SEM_LOCK(s) release_spinlock(&(s).lock)
|
||||||
|
|
||||||
static int remove_thread_from_sem(struct thread *thread, struct sem_entry *sem,
|
|
||||||
struct thread_queue *queue, status_t acquireStatus, bool hasThreadLock);
|
|
||||||
|
|
||||||
struct sem_timeout_args {
|
|
||||||
thread_id blocked_thread;
|
|
||||||
sem_id blocked_sem_id;
|
|
||||||
int32 sem_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
dump_sem_list(int argc, char **argv)
|
dump_sem_list(int argc, char **argv)
|
||||||
{
|
{
|
||||||
const char *name = NULL;
|
const char *name = NULL;
|
||||||
team_id owner = -1;
|
team_id owner = -1;
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
thread_id last = -1;
|
thread_id last = -1;
|
||||||
#endif
|
|
||||||
int32 i;
|
int32 i;
|
||||||
|
|
||||||
if (argc > 2) {
|
if (argc > 2) {
|
||||||
@@ -109,38 +128,26 @@ dump_sem_list(int argc, char **argv)
|
|||||||
owner = strtoul(argv[2], NULL, 0);
|
owner = strtoul(argv[2], NULL, 0);
|
||||||
else if (!strcmp(argv[1], "name"))
|
else if (!strcmp(argv[1], "name"))
|
||||||
name = argv[2];
|
name = argv[2];
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
else if (!strcmp(argv[1], "last"))
|
else if (!strcmp(argv[1], "last"))
|
||||||
last = strtoul(argv[2], NULL, 0);
|
last = strtoul(argv[2], NULL, 0);
|
||||||
#endif
|
|
||||||
} else if (argc > 1)
|
} else if (argc > 1)
|
||||||
owner = strtoul(argv[1], NULL, 0);
|
owner = strtoul(argv[1], NULL, 0);
|
||||||
|
|
||||||
kprintf("sem id count team"
|
kprintf("sem id count team last name\n");
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
" last"
|
|
||||||
#endif
|
|
||||||
" name\n");
|
|
||||||
|
|
||||||
for (i = 0; i < sMaxSems; i++) {
|
for (i = 0; i < sMaxSems; i++) {
|
||||||
struct sem_entry *sem = &sSems[i];
|
struct sem_entry *sem = &sSems[i];
|
||||||
if (sem->id < 0
|
if (sem->id < 0
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
|| (last != -1 && sem->u.used.last_acquirer != last)
|
|| (last != -1 && sem->u.used.last_acquirer != last)
|
||||||
#endif
|
|
||||||
|| (name != NULL && strstr(sem->u.used.name, name) == NULL)
|
|| (name != NULL && strstr(sem->u.used.name, name) == NULL)
|
||||||
|| (owner != -1 && sem->u.used.owner != owner))
|
|| (owner != -1 && sem->u.used.owner != owner))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
kprintf("%p %6ld %5ld %6ld "
|
kprintf("%p %6ld %5ld %6ld "
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
"%6ld "
|
"%6ld "
|
||||||
#endif
|
|
||||||
" %s\n", sem, sem->id, sem->u.used.count,
|
" %s\n", sem, sem->id, sem->u.used.count,
|
||||||
sem->u.used.owner,
|
sem->u.used.owner,
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
sem->u.used.last_acquirer > 0 ? sem->u.used.last_acquirer : 0,
|
sem->u.used.last_acquirer > 0 ? sem->u.used.last_acquirer : 0,
|
||||||
#endif
|
|
||||||
sem->u.used.name);
|
sem->u.used.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,12 +165,10 @@ dump_sem(struct sem_entry *sem)
|
|||||||
kprintf("owner: %ld\n", sem->u.used.owner);
|
kprintf("owner: %ld\n", sem->u.used.owner);
|
||||||
kprintf("count: %ld\n", sem->u.used.count);
|
kprintf("count: %ld\n", sem->u.used.count);
|
||||||
kprintf("queue: ");
|
kprintf("queue: ");
|
||||||
if (sem->u.used.queue.head != NULL) {
|
if (!sem->queue.IsEmpty()) {
|
||||||
struct thread *thread = sem->u.used.queue.head;
|
ThreadQueue::Iterator it = sem->queue.GetIterator();
|
||||||
while (thread != NULL) {
|
while (queued_thread* entry = it.Next())
|
||||||
kprintf(" %ld", thread->id);
|
kprintf(" %ld", entry->thread->id);
|
||||||
thread = thread->queue_next;
|
|
||||||
}
|
|
||||||
kprintf("\n");
|
kprintf("\n");
|
||||||
} else
|
} else
|
||||||
kprintf(" -\n");
|
kprintf(" -\n");
|
||||||
@@ -178,16 +183,19 @@ dump_sem(struct sem_entry *sem)
|
|||||||
kprintf("last released by: %ld, count: %ld\n", sem->u.used.last_releaser,
|
kprintf("last released by: %ld, count: %ld\n", sem->u.used.last_releaser,
|
||||||
sem->u.used.last_release_count);
|
sem->u.used.last_release_count);
|
||||||
|
|
||||||
|
if (sem->u.used.last_releaser != 0)
|
||||||
|
set_debug_variable("_releaser", sem->u.used.last_releaser);
|
||||||
|
else
|
||||||
|
unset_debug_variable("_releaser");
|
||||||
|
#else
|
||||||
|
kprintf("last acquired by: %ld\n", sem->u.used.last_acquirer);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (sem->u.used.last_acquirer != 0)
|
if (sem->u.used.last_acquirer != 0)
|
||||||
set_debug_variable("_acquirer", sem->u.used.last_acquirer);
|
set_debug_variable("_acquirer", sem->u.used.last_acquirer);
|
||||||
else
|
else
|
||||||
unset_debug_variable("_acquirer");
|
unset_debug_variable("_acquirer");
|
||||||
|
|
||||||
if (sem->u.used.last_releaser != 0)
|
|
||||||
set_debug_variable("_releaser", sem->u.used.last_releaser);
|
|
||||||
else
|
|
||||||
unset_debug_variable("_releaser");
|
|
||||||
#endif
|
|
||||||
} else {
|
} else {
|
||||||
kprintf("next: %p\n", sem->u.unused.next);
|
kprintf("next: %p\n", sem->u.unused.next);
|
||||||
kprintf("next_id: %ld\n", sem->u.unused.next_id);
|
kprintf("next_id: %ld\n", sem->u.unused.next_id);
|
||||||
@@ -238,13 +246,6 @@ dump_sem_info(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static inline void
|
|
||||||
clear_thread_queue(struct thread_queue *queue)
|
|
||||||
{
|
|
||||||
queue->head = queue->tail = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! \brief Appends a semaphore slot to the free list.
|
/*! \brief Appends a semaphore slot to the free list.
|
||||||
|
|
||||||
The semaphore list must be locked.
|
The semaphore list must be locked.
|
||||||
@@ -281,56 +282,6 @@ notify_sem_select_events(struct sem_entry* sem, uint16 events)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Called from a timer handler. Wakes up a semaphore */
|
|
||||||
static int32
|
|
||||||
sem_timeout(timer *data)
|
|
||||||
{
|
|
||||||
struct sem_timeout_args *args = (struct sem_timeout_args *)data->user_data;
|
|
||||||
struct thread *thread;
|
|
||||||
int slot;
|
|
||||||
int state;
|
|
||||||
struct thread_queue wakeupQueue;
|
|
||||||
|
|
||||||
thread = thread_get_thread_struct(args->blocked_thread);
|
|
||||||
if (thread == NULL)
|
|
||||||
return B_HANDLED_INTERRUPT;
|
|
||||||
slot = args->blocked_sem_id % sMaxSems;
|
|
||||||
|
|
||||||
state = disable_interrupts();
|
|
||||||
GRAB_SEM_LOCK(sSems[slot]);
|
|
||||||
|
|
||||||
TRACE(("sem_timeout: called on %p sem %ld, thread %ld\n",
|
|
||||||
data, args->blocked_sem_id, args->blocked_thread));
|
|
||||||
|
|
||||||
if (sSems[slot].id != args->blocked_sem_id) {
|
|
||||||
// this thread was not waiting on this semaphore
|
|
||||||
panic("sem_timeout: thread %ld was trying to wait on sem %ld which "
|
|
||||||
"doesn't exist!\n", args->blocked_thread, args->blocked_sem_id);
|
|
||||||
|
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
|
||||||
restore_interrupts(state);
|
|
||||||
return B_HANDLED_INTERRUPT;
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_thread_queue(&wakeupQueue);
|
|
||||||
remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue, B_TIMED_OUT,
|
|
||||||
false);
|
|
||||||
|
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
|
||||||
|
|
||||||
GRAB_THREAD_LOCK();
|
|
||||||
// put the threads in the run q here to make sure we dont deadlock in sem_interrupt_thread
|
|
||||||
while ((thread = thread_dequeue(&wakeupQueue)) != NULL)
|
|
||||||
scheduler_enqueue_in_run_queue(thread);
|
|
||||||
|
|
||||||
RELEASE_THREAD_LOCK();
|
|
||||||
|
|
||||||
restore_interrupts(state);
|
|
||||||
|
|
||||||
return B_INVOKE_SCHEDULER;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Fills the thread_info structure with information from the specified
|
/*! Fills the thread_info structure with information from the specified
|
||||||
thread.
|
thread.
|
||||||
The thread lock must be held when called.
|
The thread lock must be held when called.
|
||||||
@@ -342,13 +293,7 @@ fill_sem_info(struct sem_entry *sem, sem_info *info, size_t size)
|
|||||||
info->team = sem->u.used.owner;
|
info->team = sem->u.used.owner;
|
||||||
strlcpy(info->name, sem->u.used.name, sizeof(info->name));
|
strlcpy(info->name, sem->u.used.name, sizeof(info->name));
|
||||||
info->count = sem->u.used.count;
|
info->count = sem->u.used.count;
|
||||||
|
info->latest_holder = sem->u.used.last_acquirer;
|
||||||
// ToDo: not sure if this is the latest holder, or the next
|
|
||||||
// holder...
|
|
||||||
if (sem->u.used.queue.head != NULL)
|
|
||||||
info->latest_holder = sem->u.used.queue.head->id;
|
|
||||||
else
|
|
||||||
info->latest_holder = -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -389,17 +334,12 @@ sem_init(kernel_args *args)
|
|||||||
add_debugger_command_etc("sems", &dump_sem_list,
|
add_debugger_command_etc("sems", &dump_sem_list,
|
||||||
"Dump a list of all active semaphores (for team, with name, etc.)",
|
"Dump a list of all active semaphores (for team, with name, etc.)",
|
||||||
"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]"
|
"[ ([ \"team\" | \"owner\" ] <team>) | (\"name\" <name>) ]"
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
" | (\"last\" <last acquirer>)\n"
|
||||||
" | (\"last\" <last acquirer>)"
|
|
||||||
#endif
|
|
||||||
"\n"
|
|
||||||
"Prints a list of all active semaphores meeting the given\n"
|
"Prints a list of all active semaphores meeting the given\n"
|
||||||
"requirement. If no argument is given, all sems are listed.\n"
|
"requirement. If no argument is given, all sems are listed.\n"
|
||||||
" <team> - The team owning the semaphores.\n"
|
" <team> - The team owning the semaphores.\n"
|
||||||
" <name> - Part of the name of the semaphores.\n"
|
" <name> - Part of the name of the semaphores.\n"
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
|
||||||
" <last acquirer> - The thread that last acquired the semaphore.\n"
|
" <last acquirer> - The thread that last acquired the semaphore.\n"
|
||||||
#endif
|
|
||||||
, 0);
|
, 0);
|
||||||
add_debugger_command_etc("sem", &dump_sem_info,
|
add_debugger_command_etc("sem", &dump_sem_info,
|
||||||
"Dump info about a particular semaphore",
|
"Dump info about a particular semaphore",
|
||||||
@@ -478,7 +418,8 @@ create_sem_etc(int32 count, const char *name, team_id owner)
|
|||||||
GRAB_SEM_LOCK(*sem);
|
GRAB_SEM_LOCK(*sem);
|
||||||
sem->id = sem->u.unused.next_id;
|
sem->id = sem->u.unused.next_id;
|
||||||
sem->u.used.count = count;
|
sem->u.used.count = count;
|
||||||
clear_thread_queue(&sem->u.used.queue);
|
sem->u.used.net_count = count;
|
||||||
|
new(&sem->queue) ThreadQueue;
|
||||||
sem->u.used.name = tempName;
|
sem->u.used.name = tempName;
|
||||||
sem->u.used.owner = owner;
|
sem->u.used.owner = owner;
|
||||||
sem->u.used.select_infos = NULL;
|
sem->u.used.select_infos = NULL;
|
||||||
@@ -486,6 +427,9 @@ create_sem_etc(int32 count, const char *name, team_id owner)
|
|||||||
RELEASE_SEM_LOCK(*sem);
|
RELEASE_SEM_LOCK(*sem);
|
||||||
|
|
||||||
atomic_add(&sUsedSems, 1);
|
atomic_add(&sUsedSems, 1);
|
||||||
|
|
||||||
|
KTRACE("create_sem_etc(count: %ld, name: %s, owner: %ld) -> %ld",
|
||||||
|
count, name, owner, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
RELEASE_SEM_LIST_LOCK();
|
RELEASE_SEM_LIST_LOCK();
|
||||||
@@ -572,95 +516,52 @@ deselect_sem(int32 id, struct select_info* info, bool kernel)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Wake up a thread that's blocked on a semaphore
|
|
||||||
this function must be entered with interrupts disabled and THREADLOCK held
|
|
||||||
*/
|
|
||||||
status_t
|
|
||||||
sem_interrupt_thread(struct thread *thread)
|
|
||||||
{
|
|
||||||
struct thread_queue wakeupQueue;
|
|
||||||
int32 slot;
|
|
||||||
|
|
||||||
TRACE(("sem_interrupt_thread: called on thread %p (%ld), blocked on sem %ld\n",
|
|
||||||
thread, thread->id, thread->sem.blocking));
|
|
||||||
|
|
||||||
if (thread->state != B_THREAD_WAITING || thread->sem.blocking < 0)
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
if ((thread->sem.flags & B_CAN_INTERRUPT) == 0
|
|
||||||
&& ((thread->sem.flags & B_KILL_CAN_INTERRUPT) == 0
|
|
||||||
|| (thread->sig_pending & KILL_SIGNALS) == 0)) {
|
|
||||||
return B_NOT_ALLOWED;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot = thread->sem.blocking % sMaxSems;
|
|
||||||
|
|
||||||
GRAB_SEM_LOCK(sSems[slot]);
|
|
||||||
|
|
||||||
if (sSems[slot].id != thread->sem.blocking) {
|
|
||||||
panic("sem_interrupt_thread: thread %ld blocks on sem %ld, but that "
|
|
||||||
"sem doesn't exist!\n", thread->id, thread->sem.blocking);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_thread_queue(&wakeupQueue);
|
|
||||||
status_t result = remove_thread_from_sem(thread, &sSems[slot],
|
|
||||||
&wakeupQueue, B_INTERRUPTED, true);
|
|
||||||
|
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
|
||||||
|
|
||||||
if (result != B_OK) {
|
|
||||||
// The thread is not in the wait queue anymore. Probably it just timed
|
|
||||||
// out before we locked the sem.
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((thread = thread_dequeue(&wakeupQueue)) != NULL)
|
|
||||||
scheduler_enqueue_in_run_queue(thread);
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Forcibly removes a thread from a semaphores wait queue. May have to wake up
|
/*! Forcibly removes a thread from a semaphores wait queue. May have to wake up
|
||||||
other threads in the process. All threads that need to be woken up are added
|
other threads in the process.
|
||||||
to the passed in thread_queue.
|
Must be called with semaphore lock held. The thread lock must not be held.
|
||||||
Must be called with semaphore lock held.
|
|
||||||
*/
|
*/
|
||||||
static int
|
static void
|
||||||
remove_thread_from_sem(struct thread *thread, struct sem_entry *sem,
|
remove_thread_from_sem(queued_thread *entry, struct sem_entry *sem)
|
||||||
struct thread_queue *queue, status_t acquireStatus, bool hasThreadLock)
|
|
||||||
{
|
{
|
||||||
// remove the thread from the queue and place it in the supplied queue
|
if (!entry->queued)
|
||||||
if (thread_dequeue_id(&sem->u.used.queue, thread->id) != thread)
|
return;
|
||||||
return B_ENTRY_NOT_FOUND;
|
|
||||||
|
|
||||||
sem->u.used.count += thread->sem.acquire_count;
|
sem->queue.Remove(entry);
|
||||||
thread->sem.acquire_status = acquireStatus;
|
entry->queued = false;
|
||||||
thread_enqueue(thread, queue);
|
sem->u.used.count += entry->count;
|
||||||
|
|
||||||
// now see if more threads need to be woken up
|
// We're done with this entry. We only have to check, if other threads
|
||||||
while (sem->u.used.count > 0
|
// need unblocking, too.
|
||||||
&& thread_lookat_queue(&sem->u.used.queue) != NULL) {
|
|
||||||
int32 delta = min_c(thread->sem.count, sem->u.used.count);
|
|
||||||
|
|
||||||
thread->sem.count -= delta;
|
// Now see if more threads need to be woken up. We get the thread lock for
|
||||||
if (thread->sem.count <= 0) {
|
// that time, so the blocking state of threads won't change. We need that
|
||||||
thread = thread_dequeue(&sem->u.used.queue);
|
// lock anyway when unblocking a thread.
|
||||||
thread_enqueue(thread, queue);
|
GRAB_THREAD_LOCK();
|
||||||
|
|
||||||
|
while ((entry = sem->queue.Head()) != NULL) {
|
||||||
|
if (thread_is_blocked(entry->thread)) {
|
||||||
|
// The thread is still waiting. If its count is satisfied, unblock
|
||||||
|
// it. Otherwise we can't unblock any other thread.
|
||||||
|
if (entry->count > sem->u.used.net_count)
|
||||||
|
break;
|
||||||
|
|
||||||
|
thread_unblock_locked(entry->thread, B_OK);
|
||||||
|
sem->u.used.net_count -= entry->count;
|
||||||
|
} else {
|
||||||
|
// The thread is no longer waiting, but still queued, which means
|
||||||
|
// acquiration failed and we can just remove it.
|
||||||
|
sem->u.used.count += entry->count;
|
||||||
}
|
}
|
||||||
sem->u.used.count -= delta;
|
|
||||||
|
sem->queue.Remove(entry);
|
||||||
|
entry->queued = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sem->u.used.count > 0 && sem->u.used.select_infos != NULL) {
|
RELEASE_THREAD_LOCK();
|
||||||
if (hasThreadLock)
|
|
||||||
RELEASE_THREAD_LOCK();
|
|
||||||
|
|
||||||
|
// select notification, if the semaphore is now acquirable
|
||||||
|
if (sem->u.used.count > 0)
|
||||||
notify_sem_select_events(sem, B_EVENT_ACQUIRE_SEMAPHORE);
|
notify_sem_select_events(sem, B_EVENT_ACQUIRE_SEMAPHORE);
|
||||||
|
|
||||||
if (hasThreadLock)
|
|
||||||
GRAB_THREAD_LOCK();
|
|
||||||
}
|
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -732,21 +633,14 @@ create_sem(int32 count, const char *name)
|
|||||||
status_t
|
status_t
|
||||||
delete_sem(sem_id id)
|
delete_sem(sem_id id)
|
||||||
{
|
{
|
||||||
struct thread_queue releaseQueue;
|
|
||||||
int32 releasedThreads;
|
|
||||||
struct thread *thread;
|
|
||||||
cpu_status state;
|
|
||||||
int32 slot;
|
|
||||||
char *name;
|
|
||||||
|
|
||||||
if (sSemsActive == false)
|
if (sSemsActive == false)
|
||||||
return B_NO_MORE_SEMS;
|
return B_NO_MORE_SEMS;
|
||||||
if (id < 0)
|
if (id < 0)
|
||||||
return B_BAD_SEM_ID;
|
return B_BAD_SEM_ID;
|
||||||
|
|
||||||
slot = id % sMaxSems;
|
int32 slot = id % sMaxSems;
|
||||||
|
|
||||||
state = disable_interrupts();
|
cpu_status state = disable_interrupts();
|
||||||
GRAB_SEM_LOCK(sSems[slot]);
|
GRAB_SEM_LOCK(sSems[slot]);
|
||||||
|
|
||||||
if (sSems[slot].id != id) {
|
if (sSems[slot].id != id) {
|
||||||
@@ -756,22 +650,21 @@ delete_sem(sem_id id)
|
|||||||
return B_BAD_SEM_ID;
|
return B_BAD_SEM_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KTRACE("delete_sem(sem: %ld)", id);
|
||||||
|
|
||||||
notify_sem_select_events(&sSems[slot], B_EVENT_INVALID);
|
notify_sem_select_events(&sSems[slot], B_EVENT_INVALID);
|
||||||
sSems[slot].u.used.select_infos = NULL;
|
sSems[slot].u.used.select_infos = NULL;
|
||||||
|
|
||||||
releasedThreads = 0;
|
|
||||||
clear_thread_queue(&releaseQueue);
|
|
||||||
|
|
||||||
// free any threads waiting for this semaphore
|
// free any threads waiting for this semaphore
|
||||||
while ((thread = thread_dequeue(&sSems[slot].u.used.queue)) != NULL) {
|
GRAB_THREAD_LOCK();
|
||||||
thread->sem.acquire_status = B_BAD_SEM_ID;
|
while (queued_thread* entry = sSems[slot].queue.RemoveHead()) {
|
||||||
thread->sem.count = 0;
|
entry->queued = false;
|
||||||
thread_enqueue(thread, &releaseQueue);
|
thread_unblock_locked(entry->thread, B_BAD_SEM_ID);
|
||||||
releasedThreads++;
|
|
||||||
}
|
}
|
||||||
|
RELEASE_THREAD_LOCK();
|
||||||
|
|
||||||
sSems[slot].id = -1;
|
sSems[slot].id = -1;
|
||||||
name = sSems[slot].u.used.name;
|
char *name = sSems[slot].u.used.name;
|
||||||
sSems[slot].u.used.name = NULL;
|
sSems[slot].u.used.name = NULL;
|
||||||
|
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
RELEASE_SEM_LOCK(sSems[slot]);
|
||||||
@@ -782,15 +675,6 @@ delete_sem(sem_id id)
|
|||||||
atomic_add(&sUsedSems, -1);
|
atomic_add(&sUsedSems, -1);
|
||||||
RELEASE_SEM_LIST_LOCK();
|
RELEASE_SEM_LIST_LOCK();
|
||||||
|
|
||||||
if (releasedThreads > 0) {
|
|
||||||
GRAB_THREAD_LOCK();
|
|
||||||
while ((thread = thread_dequeue(&releaseQueue)) != NULL)
|
|
||||||
scheduler_enqueue_in_run_queue(thread);
|
|
||||||
|
|
||||||
scheduler_reschedule();
|
|
||||||
RELEASE_THREAD_LOCK();
|
|
||||||
}
|
|
||||||
|
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
free(name);
|
free(name);
|
||||||
@@ -876,21 +760,20 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KTRACE("switch_sem_etc(semToBeReleased: %ld, sem: %ld, count: %ld, "
|
||||||
|
"flags: 0x%lx, timeout: %lld)", semToBeReleased, id, count, flags,
|
||||||
|
timeout);
|
||||||
|
|
||||||
if ((sSems[slot].u.used.count -= count) < 0) {
|
if ((sSems[slot].u.used.count -= count) < 0) {
|
||||||
// we need to block
|
// we need to block
|
||||||
struct thread *thread = thread_get_current_thread();
|
struct thread *thread = thread_get_current_thread();
|
||||||
timer timeoutTimer;
|
|
||||||
// stick it on the stack, since we may be blocking here
|
|
||||||
struct sem_timeout_args args;
|
|
||||||
|
|
||||||
TRACE(("switch_sem_etc(id = %ld): block name = %s, thread = %p,"
|
TRACE(("switch_sem_etc(id = %ld): block name = %s, thread = %p,"
|
||||||
" name = %s\n", id, sSems[slot].u.used.name, thread, thread->name));
|
" name = %s\n", id, sSems[slot].u.used.name, thread, thread->name));
|
||||||
|
|
||||||
// do a quick check to see if the thread has any pending signals
|
// do a quick check to see if the thread has any pending signals
|
||||||
// this should catch most of the cases where the thread had a signal
|
// this should catch most of the cases where the thread had a signal
|
||||||
if (((flags & B_CAN_INTERRUPT) && (thread->sig_pending & ~thread->sig_block_mask) != 0)
|
if (thread_is_interrupted(thread, flags)) {
|
||||||
|| ((flags & B_KILL_CAN_INTERRUPT)
|
|
||||||
&& (thread->sig_pending & KILL_SIGNALS))) {
|
|
||||||
sSems[slot].u.used.count += count;
|
sSems[slot].u.used.count += count;
|
||||||
status = B_INTERRUPTED;
|
status = B_INTERRUPTED;
|
||||||
// the other semaphore will be released later
|
// the other semaphore will be released later
|
||||||
@@ -900,90 +783,55 @@ switch_sem_etc(sem_id semToBeReleased, sem_id id, int32 count,
|
|||||||
if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == 0)
|
if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) == 0)
|
||||||
timeout = B_INFINITE_TIMEOUT;
|
timeout = B_INFINITE_TIMEOUT;
|
||||||
|
|
||||||
thread->next_state = B_THREAD_WAITING;
|
// enqueue in the semaphore queue and get ready to wait
|
||||||
thread->sem.flags = flags;
|
queued_thread queueEntry(thread, count);
|
||||||
thread->sem.blocking = id;
|
sSems[slot].queue.Add(&queueEntry);
|
||||||
thread->sem.acquire_count = count;
|
queueEntry.queued = true;
|
||||||
thread->sem.count = min_c(-sSems[slot].u.used.count, count);
|
|
||||||
// store the count we need to restore upon release
|
|
||||||
thread->sem.acquire_status = B_NO_ERROR;
|
|
||||||
thread_enqueue(thread, &sSems[slot].u.used.queue);
|
|
||||||
|
|
||||||
if (timeout != B_INFINITE_TIMEOUT) {
|
thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SEMAPHORE,
|
||||||
TRACE(("switch_sem_etc: setting timeout sem for %Ld usecs, sem %ld, thread %ld\n",
|
(void*)(addr_t)id);
|
||||||
timeout, id, thread->id));
|
|
||||||
|
|
||||||
// set up an event to go off with the thread struct as the data
|
|
||||||
args.blocked_sem_id = id;
|
|
||||||
args.blocked_thread = thread->id;
|
|
||||||
args.sem_count = count;
|
|
||||||
|
|
||||||
timeoutTimer.user_data = &args;
|
|
||||||
add_timer(&timeoutTimer, &sem_timeout, timeout,
|
|
||||||
flags & B_RELATIVE_TIMEOUT ?
|
|
||||||
B_ONE_SHOT_RELATIVE_TIMER : B_ONE_SHOT_ABSOLUTE_TIMER);
|
|
||||||
}
|
|
||||||
|
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
RELEASE_SEM_LOCK(sSems[slot]);
|
||||||
|
|
||||||
if (semToBeReleased >= B_OK) {
|
// release the other semaphore, if any
|
||||||
|
if (semToBeReleased >= 0) {
|
||||||
release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE);
|
release_sem_etc(semToBeReleased, 1, B_DO_NOT_RESCHEDULE);
|
||||||
semToBeReleased = -1;
|
semToBeReleased = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
GRAB_THREAD_LOCK();
|
GRAB_THREAD_LOCK();
|
||||||
// check again to see if a signal is pending.
|
|
||||||
// it may have been delivered while setting up the sem, though it's pretty unlikely
|
|
||||||
if (((flags & B_CAN_INTERRUPT)
|
|
||||||
&& (thread->sig_pending & ~thread->sig_block_mask) != 0)
|
|
||||||
|| ((flags & B_KILL_CAN_INTERRUPT)
|
|
||||||
&& (thread->sig_pending & KILL_SIGNALS))) {
|
|
||||||
struct thread_queue wakeupQueue;
|
|
||||||
// ok, so a tiny race happened where a signal was delivered to this thread while
|
|
||||||
// it was setting up the sem. We can only be sure a signal wasn't delivered
|
|
||||||
// here, since the threadlock is held. The previous check would have found most
|
|
||||||
// instances, but there was a race, so we have to handle it. It'll be more messy...
|
|
||||||
clear_thread_queue(&wakeupQueue);
|
|
||||||
GRAB_SEM_LOCK(sSems[slot]);
|
|
||||||
if (sSems[slot].id == id) {
|
|
||||||
remove_thread_from_sem(thread, &sSems[slot], &wakeupQueue,
|
|
||||||
B_INTERRUPTED, true);
|
|
||||||
}
|
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
|
||||||
|
|
||||||
struct thread *wakeupThread;
|
status_t acquireStatus = timeout == B_INFINITE_TIMEOUT
|
||||||
while ((wakeupThread = thread_dequeue(&wakeupQueue)) != NULL)
|
? thread_block_locked(thread)
|
||||||
scheduler_enqueue_in_run_queue(wakeupThread);
|
: thread_block_with_timeout_locked(flags, timeout);
|
||||||
|
|
||||||
// fall through and reschedule since another thread with a higher priority may have been woken up
|
|
||||||
}
|
|
||||||
scheduler_reschedule();
|
|
||||||
RELEASE_THREAD_LOCK();
|
RELEASE_THREAD_LOCK();
|
||||||
|
GRAB_SEM_LOCK(sSems[slot]);
|
||||||
|
|
||||||
if (timeout != B_INFINITE_TIMEOUT) {
|
// If we're still queued, this means the acquiration failed, and we
|
||||||
if (thread->sem.acquire_status != B_TIMED_OUT) {
|
// need to remove our entry and (potentially) wake up other threads.
|
||||||
// cancel the timer event, the sem may have been deleted or interrupted
|
if (queueEntry.queued)
|
||||||
// with the timer still active
|
remove_thread_from_sem(&queueEntry, &sSems[slot]);
|
||||||
cancel_timer(&timeoutTimer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
if (acquireStatus >= B_OK) {
|
||||||
if (thread->sem.acquire_status >= B_OK) {
|
|
||||||
sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
|
sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
|
||||||
|
#ifdef DEBUG_LAST_ACQUIRER
|
||||||
sSems[slot].u.used.last_acquire_count = count;
|
sSems[slot].u.used.last_acquire_count = count;
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
RELEASE_SEM_LOCK(sSems[slot]);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
|
|
||||||
TRACE(("switch_sem_etc(sem %ld): exit block name %s, "
|
TRACE(("switch_sem_etc(sem %ld): exit block name %s, "
|
||||||
"thread %ld (%s)\n", id, sSems[slot].u.used.name, thread->id,
|
"thread %ld (%s)\n", id, sSems[slot].u.used.name, thread->id,
|
||||||
thread->name));
|
thread->name));
|
||||||
return thread->sem.acquire_status;
|
KTRACE("switch_sem_etc() done: 0x%lx", acquireStatus);
|
||||||
|
return acquireStatus;
|
||||||
} else {
|
} else {
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
sSems[slot].u.used.net_count -= count;
|
||||||
sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
|
sSems[slot].u.used.last_acquirer = thread_get_current_thread_id();
|
||||||
|
#ifdef DEBUG_LAST_ACQUIRER
|
||||||
sSems[slot].u.used.last_acquire_count = count;
|
sSems[slot].u.used.last_acquire_count = count;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -1004,6 +852,8 @@ err:
|
|||||||
_user_debugger("Thread tried to acquire kernel semaphore.");
|
_user_debugger("Thread tried to acquire kernel semaphore.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
KTRACE("switch_sem_etc() done: 0x%lx", status);
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1018,10 +868,7 @@ release_sem(sem_id id)
|
|||||||
status_t
|
status_t
|
||||||
release_sem_etc(sem_id id, int32 count, uint32 flags)
|
release_sem_etc(sem_id id, int32 count, uint32 flags)
|
||||||
{
|
{
|
||||||
struct thread_queue releaseQueue;
|
|
||||||
int32 slot = id % sMaxSems;
|
int32 slot = id % sMaxSems;
|
||||||
cpu_status state;
|
|
||||||
status_t status = B_OK;
|
|
||||||
|
|
||||||
if (kernel_startup)
|
if (kernel_startup)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
@@ -1032,13 +879,12 @@ release_sem_etc(sem_id id, int32 count, uint32 flags)
|
|||||||
if (count <= 0 && (flags & B_RELEASE_ALL) == 0)
|
if (count <= 0 && (flags & B_RELEASE_ALL) == 0)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
state = disable_interrupts();
|
InterruptsLocker _;
|
||||||
GRAB_SEM_LOCK(sSems[slot]);
|
SpinLocker semLocker(sSems[slot].lock);
|
||||||
|
|
||||||
if (sSems[slot].id != id) {
|
if (sSems[slot].id != id) {
|
||||||
TRACE(("sem_release_etc: invalid sem_id %ld\n", id));
|
TRACE(("sem_release_etc: invalid sem_id %ld\n", id));
|
||||||
status = B_BAD_SEM_ID;
|
return B_BAD_SEM_ID;
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToDo: the B_CHECK_PERMISSION flag should be made private, as it
|
// ToDo: the B_CHECK_PERMISSION flag should be made private, as it
|
||||||
@@ -1047,81 +893,80 @@ release_sem_etc(sem_id id, int32 count, uint32 flags)
|
|||||||
&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
|
&& sSems[slot].u.used.owner == team_get_kernel_team_id()) {
|
||||||
dprintf("thread %ld tried to release kernel semaphore.\n",
|
dprintf("thread %ld tried to release kernel semaphore.\n",
|
||||||
thread_get_current_thread_id());
|
thread_get_current_thread_id());
|
||||||
status = B_NOT_ALLOWED;
|
return B_NOT_ALLOWED;
|
||||||
goto err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_LAST_ACQUIRER
|
KTRACE("release_sem_etc(sem: %ld, count: %ld, flags: 0x%lx)", id, count,
|
||||||
|
flags);
|
||||||
|
|
||||||
sSems[slot].u.used.last_acquirer = -sSems[slot].u.used.last_acquirer;
|
sSems[slot].u.used.last_acquirer = -sSems[slot].u.used.last_acquirer;
|
||||||
|
#ifdef DEBUG_LAST_ACQUIRER
|
||||||
sSems[slot].u.used.last_releaser = thread_get_current_thread_id();
|
sSems[slot].u.used.last_releaser = thread_get_current_thread_id();
|
||||||
sSems[slot].u.used.last_release_count = count;
|
sSems[slot].u.used.last_release_count = count;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// clear out a queue we will use to hold all of the threads that we will have to
|
|
||||||
// put back into the run list. This is done so the thread lock wont be held
|
|
||||||
// while this sems lock is held since the two locks are grabbed in the other
|
|
||||||
// order in sem_interrupt_thread.
|
|
||||||
clear_thread_queue(&releaseQueue);
|
|
||||||
|
|
||||||
if (flags & B_RELEASE_ALL) {
|
if (flags & B_RELEASE_ALL) {
|
||||||
count = -sSems[slot].u.used.count;
|
count = sSems[slot].u.used.net_count - sSems[slot].u.used.count;
|
||||||
|
|
||||||
// is there anything to do for us at all?
|
// is there anything to do for us at all?
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
goto err;
|
return B_OK;
|
||||||
|
|
||||||
|
// Don't release more than necessary -- there might be interrupted/
|
||||||
|
// timed out threads in the queue.
|
||||||
|
flags |= B_RELEASE_IF_WAITING_ONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpinLocker threadLocker(thread_spinlock);
|
||||||
|
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
int delta = count;
|
queued_thread* entry = sSems[slot].queue.Head();
|
||||||
if (sSems[slot].u.used.count < 0) {
|
if (entry == NULL) {
|
||||||
struct thread *thread = thread_lookat_queue(&sSems[slot].u.used.queue);
|
if ((flags & B_RELEASE_IF_WAITING_ONLY) == 0) {
|
||||||
|
sSems[slot].u.used.count += count;
|
||||||
delta = min_c(count, thread->sem.count);
|
sSems[slot].u.used.net_count += count;
|
||||||
thread->sem.count -= delta;
|
|
||||||
if (thread->sem.count <= 0) {
|
|
||||||
// release this thread
|
|
||||||
thread = thread_dequeue(&sSems[slot].u.used.queue);
|
|
||||||
thread_enqueue(thread, &releaseQueue);
|
|
||||||
thread->sem.count = 0;
|
|
||||||
}
|
}
|
||||||
} else if (flags & B_RELEASE_IF_WAITING_ONLY)
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
sSems[slot].u.used.count += delta;
|
if (thread_is_blocked(entry->thread)) {
|
||||||
count -= delta;
|
// The thread is still waiting. If its count is satisfied,
|
||||||
|
// unblock it. Otherwise we can't unblock any other thread.
|
||||||
|
if (entry->count > sSems[slot].u.used.net_count + count) {
|
||||||
|
sSems[slot].u.used.count += count;
|
||||||
|
sSems[slot].u.used.net_count += count;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_unblock_locked(entry->thread, B_OK);
|
||||||
|
|
||||||
|
int delta = min_c(count, entry->count);
|
||||||
|
sSems[slot].u.used.count += delta;
|
||||||
|
sSems[slot].u.used.net_count += delta - entry->count;
|
||||||
|
count -= delta;
|
||||||
|
} else {
|
||||||
|
// The thread is no longer waiting, but still queued, which
|
||||||
|
// means acquiration failed and we can just remove it.
|
||||||
|
sSems[slot].u.used.count += entry->count;
|
||||||
|
}
|
||||||
|
|
||||||
|
sSems[slot].queue.Remove(entry);
|
||||||
|
entry->queued = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
threadLocker.Unlock();
|
||||||
|
|
||||||
if (sSems[slot].u.used.count > 0)
|
if (sSems[slot].u.used.count > 0)
|
||||||
notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE);
|
notify_sem_select_events(&sSems[slot], B_EVENT_ACQUIRE_SEMAPHORE);
|
||||||
|
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
// reschedule, if we've not explicitly been told not to
|
||||||
|
if ((flags & B_DO_NOT_RESCHEDULE) == 0) {
|
||||||
// pull off any items in the release queue and put them in the run queue
|
semLocker.Unlock();
|
||||||
if (releaseQueue.head != NULL) {
|
threadLocker.Lock();
|
||||||
struct thread *thread;
|
scheduler_reschedule();
|
||||||
|
|
||||||
GRAB_THREAD_LOCK();
|
|
||||||
while ((thread = thread_dequeue(&releaseQueue)) != NULL) {
|
|
||||||
#if 0
|
|
||||||
// temporarily place thread in a run queue with a higher priority to boost it up
|
|
||||||
thread->next_priority = thread->priority >= B_FIRST_REAL_TIME_PRIORITY ?
|
|
||||||
thread->priority : thread->priority + 1;
|
|
||||||
#endif
|
|
||||||
scheduler_enqueue_in_run_queue(thread);
|
|
||||||
}
|
|
||||||
if ((flags & B_DO_NOT_RESCHEDULE) == 0)
|
|
||||||
scheduler_reschedule();
|
|
||||||
|
|
||||||
RELEASE_THREAD_LOCK();
|
|
||||||
}
|
}
|
||||||
goto outnolock;
|
|
||||||
|
|
||||||
err:
|
return B_OK;
|
||||||
RELEASE_SEM_LOCK(sSems[slot]);
|
|
||||||
outnolock:
|
|
||||||
restore_interrupts(state);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -459,20 +459,6 @@ is_signal_blocked(int signal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*! Tries to interrupt a thread waiting for a semaphore or a condition variable.
|
|
||||||
Interrupts must be disabled, the thread lock be held.
|
|
||||||
*/
|
|
||||||
static status_t
|
|
||||||
signal_interrupt_thread(struct thread* thread)
|
|
||||||
{
|
|
||||||
if (thread->sem.blocking >= 0)
|
|
||||||
return sem_interrupt_thread(thread);
|
|
||||||
else if (thread->condition_variable_entry)
|
|
||||||
return condition_variable_interrupt_thread(thread);
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*! Delivers the \a signal to the \a thread, but doesn't handle the signal -
|
/*! Delivers the \a signal to the \a thread, but doesn't handle the signal -
|
||||||
it just makes sure the thread gets the signal, ie. unblocks it if needed.
|
it just makes sure the thread gets the signal, ie. unblocks it if needed.
|
||||||
This function must be called with interrupts disabled and the
|
This function must be called with interrupts disabled and the
|
||||||
@@ -505,19 +491,13 @@ deliver_signal(struct thread *thread, uint signal, uint32 flags)
|
|||||||
|
|
||||||
mainThread->sig_pending |= SIGNAL_TO_MASK(SIGKILLTHR);
|
mainThread->sig_pending |= SIGNAL_TO_MASK(SIGKILLTHR);
|
||||||
// Wake up main thread
|
// Wake up main thread
|
||||||
if (mainThread->state == B_THREAD_SUSPENDED)
|
thread_interrupt(mainThread, true);
|
||||||
scheduler_enqueue_in_run_queue(mainThread);
|
|
||||||
else if (mainThread->state == B_THREAD_WAITING)
|
|
||||||
signal_interrupt_thread(mainThread);
|
|
||||||
|
|
||||||
// Supposed to fall through
|
// Supposed to fall through
|
||||||
}
|
}
|
||||||
case SIGKILLTHR:
|
case SIGKILLTHR:
|
||||||
// Wake up suspended threads and interrupt waiting ones
|
// Wake up suspended threads and interrupt waiting ones
|
||||||
if (thread->state == B_THREAD_SUSPENDED)
|
thread_interrupt(thread, true);
|
||||||
scheduler_enqueue_in_run_queue(thread);
|
|
||||||
else if (thread->state == B_THREAD_WAITING)
|
|
||||||
signal_interrupt_thread(thread);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SIGCONT:
|
case SIGCONT:
|
||||||
@@ -536,8 +516,7 @@ deliver_signal(struct thread *thread, uint signal, uint32 flags)
|
|||||||
if (thread->sig_pending
|
if (thread->sig_pending
|
||||||
& (~thread->sig_block_mask | SIGNAL_TO_MASK(SIGCHLD))) {
|
& (~thread->sig_block_mask | SIGNAL_TO_MASK(SIGCHLD))) {
|
||||||
// Interrupt thread if it was waiting
|
// Interrupt thread if it was waiting
|
||||||
if (thread->state == B_THREAD_WAITING)
|
thread_interrupt(thread, false);
|
||||||
signal_interrupt_thread(thread);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1745,7 +1745,7 @@ wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
|||||||
ConditionVariableEntry<team_dead_children> deadWaitEntry;
|
ConditionVariableEntry<team_dead_children> deadWaitEntry;
|
||||||
|
|
||||||
if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0)
|
if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0)
|
||||||
deadWaitEntry.Add(team->dead_children);
|
deadWaitEntry.Add(team->dead_children, B_CAN_INTERRUPT);
|
||||||
|
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
|
|
||||||
@@ -1766,7 +1766,7 @@ wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
status = deadWaitEntry.Wait(B_CAN_INTERRUPT);
|
status = deadWaitEntry.Wait();
|
||||||
if (status == B_INTERRUPTED) {
|
if (status == B_INTERRUPTED) {
|
||||||
T(WaitForChildDone(status));
|
T(WaitForChildDone(status));
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
+143
-36
@@ -78,8 +78,6 @@ static thread_id sNextThreadID = 1;
|
|||||||
static int32 sMaxThreads = 4096;
|
static int32 sMaxThreads = 4096;
|
||||||
static int32 sUsedThreads = 0;
|
static int32 sUsedThreads = 0;
|
||||||
|
|
||||||
static sem_id sSnoozeSem = -1;
|
|
||||||
|
|
||||||
// death stacks - used temporarily as a thread cleans itself up
|
// death stacks - used temporarily as a thread cleans itself up
|
||||||
struct death_stack {
|
struct death_stack {
|
||||||
area_id area;
|
area_id area;
|
||||||
@@ -226,8 +224,6 @@ create_thread_struct(struct thread *inthread, const char *name,
|
|||||||
thread->id = threadID >= 0 ? threadID : allocate_thread_id();
|
thread->id = threadID >= 0 ? threadID : allocate_thread_id();
|
||||||
thread->team = NULL;
|
thread->team = NULL;
|
||||||
thread->cpu = cpu;
|
thread->cpu = cpu;
|
||||||
thread->sem.blocking = -1;
|
|
||||||
thread->condition_variable_entry = NULL;
|
|
||||||
thread->fault_handler = 0;
|
thread->fault_handler = 0;
|
||||||
thread->page_faults_allowed = 1;
|
thread->page_faults_allowed = 1;
|
||||||
thread->kernel_stack_area = -1;
|
thread->kernel_stack_area = -1;
|
||||||
@@ -659,6 +655,28 @@ thread_exit2(void *_args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static sem_id
|
||||||
|
get_thread_wait_sem(struct thread* thread)
|
||||||
|
{
|
||||||
|
if (thread->state == B_THREAD_WAITING
|
||||||
|
&& thread->wait.type == THREAD_BLOCK_TYPE_SEMAPHORE) {
|
||||||
|
return (sem_id)(addr_t)thread->wait.object;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PrivateConditionVariable*
|
||||||
|
get_thread_wait_cvar(struct thread* thread)
|
||||||
|
{
|
||||||
|
if (thread->state == B_THREAD_WAITING
|
||||||
|
&& thread->wait.type == THREAD_BLOCK_TYPE_CONDITION_VARIABLE) {
|
||||||
|
return (PrivateConditionVariable*)thread->wait.object;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Fills the thread_info structure with information from the specified
|
Fills the thread_info structure with information from the specified
|
||||||
thread.
|
thread.
|
||||||
@@ -673,25 +691,37 @@ fill_thread_info(struct thread *thread, thread_info *info, size_t size)
|
|||||||
strlcpy(info->name, thread->name, B_OS_NAME_LENGTH);
|
strlcpy(info->name, thread->name, B_OS_NAME_LENGTH);
|
||||||
|
|
||||||
if (thread->state == B_THREAD_WAITING) {
|
if (thread->state == B_THREAD_WAITING) {
|
||||||
if (thread->sem.blocking == sSnoozeSem)
|
info->state = B_THREAD_WAITING;
|
||||||
info->state = B_THREAD_ASLEEP;
|
|
||||||
else if (thread->sem.blocking == thread->msg.read_sem)
|
switch (thread->wait.type) {
|
||||||
info->state = B_THREAD_RECEIVING;
|
case THREAD_BLOCK_TYPE_SNOOZE:
|
||||||
else
|
info->state = B_THREAD_ASLEEP;
|
||||||
info->state = B_THREAD_WAITING;
|
break;
|
||||||
|
|
||||||
|
case THREAD_BLOCK_TYPE_SEMAPHORE:
|
||||||
|
{
|
||||||
|
sem_id sem = (sem_id)(addr_t)thread->wait.object;
|
||||||
|
if (sem == thread->msg.read_sem)
|
||||||
|
info->state = B_THREAD_RECEIVING;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
info->state = (thread_state)thread->state;
|
info->state = (thread_state)thread->state;
|
||||||
|
|
||||||
info->priority = thread->priority;
|
info->priority = thread->priority;
|
||||||
info->sem = thread->sem.blocking;
|
|
||||||
info->user_time = thread->user_time;
|
info->user_time = thread->user_time;
|
||||||
info->kernel_time = thread->kernel_time;
|
info->kernel_time = thread->kernel_time;
|
||||||
info->stack_base = (void *)thread->user_stack_base;
|
info->stack_base = (void *)thread->user_stack_base;
|
||||||
info->stack_end = (void *)(thread->user_stack_base
|
info->stack_end = (void *)(thread->user_stack_base
|
||||||
+ thread->user_stack_size);
|
+ thread->user_stack_size);
|
||||||
|
info->sem = get_thread_wait_sem(thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
send_data_etc(thread_id id, int32 code, const void *buffer,
|
send_data_etc(thread_id id, int32 code, const void *buffer,
|
||||||
size_t bufferSize, int32 flags)
|
size_t bufferSize, int32 flags)
|
||||||
@@ -989,12 +1019,26 @@ state_to_text(struct thread *thread, int32 state)
|
|||||||
return "running";
|
return "running";
|
||||||
|
|
||||||
case B_THREAD_WAITING:
|
case B_THREAD_WAITING:
|
||||||
if (thread->sem.blocking == sSnoozeSem)
|
{
|
||||||
return "zzz";
|
switch (thread->wait.type) {
|
||||||
if (thread->sem.blocking == thread->msg.read_sem)
|
case THREAD_BLOCK_TYPE_SNOOZE:
|
||||||
return "receive";
|
return "zzz";
|
||||||
|
|
||||||
|
case THREAD_BLOCK_TYPE_SEMAPHORE:
|
||||||
|
{
|
||||||
|
sem_id sem = (sem_id)(addr_t)thread->wait.object;
|
||||||
|
if (sem == thread->msg.read_sem)
|
||||||
|
return "receive";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return "waiting";
|
return "waiting";
|
||||||
|
}
|
||||||
|
|
||||||
case B_THREAD_SUSPENDED:
|
case B_THREAD_SUSPENDED:
|
||||||
return "suspended";
|
return "suspended";
|
||||||
@@ -1029,14 +1073,8 @@ _dump_thread_info(struct thread *thread)
|
|||||||
kprintf("sig_pending: %#lx (blocked: %#lx)\n", thread->sig_pending,
|
kprintf("sig_pending: %#lx (blocked: %#lx)\n", thread->sig_pending,
|
||||||
thread->sig_block_mask);
|
thread->sig_block_mask);
|
||||||
kprintf("in_kernel: %d\n", thread->in_kernel);
|
kprintf("in_kernel: %d\n", thread->in_kernel);
|
||||||
kprintf(" sem.blocking: %ld\n", thread->sem.blocking);
|
kprintf("sem blocking: %ld\n", get_thread_wait_sem(thread));
|
||||||
kprintf(" sem.count: %ld\n", thread->sem.count);
|
kprintf("condition variable: %p\n", get_thread_wait_cvar(thread));
|
||||||
kprintf(" sem.acquire_status: %#lx\n", thread->sem.acquire_status);
|
|
||||||
kprintf(" sem.flags: %#lx\n", thread->sem.flags);
|
|
||||||
|
|
||||||
PrivateConditionVariableEntry* entry = thread->condition_variable_entry;
|
|
||||||
kprintf("condition variable: %p\n", entry ? entry->Variable() : NULL);
|
|
||||||
|
|
||||||
kprintf("fault_handler: %p\n", (void *)thread->fault_handler);
|
kprintf("fault_handler: %p\n", (void *)thread->fault_handler);
|
||||||
kprintf("args: %p %p\n", thread->args1, thread->args2);
|
kprintf("args: %p %p\n", thread->args1, thread->args2);
|
||||||
kprintf("entry: %p\n", (void *)thread->entry);
|
kprintf("entry: %p\n", (void *)thread->entry);
|
||||||
@@ -1165,7 +1203,7 @@ dump_thread_list(int argc, char **argv)
|
|||||||
if ((requiredState && thread->state != requiredState)
|
if ((requiredState && thread->state != requiredState)
|
||||||
|| (calling && !arch_debug_contains_call(thread, callSymbol,
|
|| (calling && !arch_debug_contains_call(thread, callSymbol,
|
||||||
callStart, callEnd))
|
callStart, callEnd))
|
||||||
|| (sem > 0 && thread->sem.blocking != sem)
|
|| (sem > 0 && get_thread_wait_sem(thread) != sem)
|
||||||
|| (team > 0 && thread->team->id != team)
|
|| (team > 0 && thread->team->id != team)
|
||||||
|| (realTimeOnly && thread->priority < B_REAL_TIME_DISPLAY_PRIORITY))
|
|| (realTimeOnly && thread->priority < B_REAL_TIME_DISPLAY_PRIORITY))
|
||||||
continue;
|
continue;
|
||||||
@@ -1175,10 +1213,10 @@ dump_thread_list(int argc, char **argv)
|
|||||||
|
|
||||||
// does it block on a semaphore or a condition variable?
|
// does it block on a semaphore or a condition variable?
|
||||||
if (thread->state == B_THREAD_WAITING) {
|
if (thread->state == B_THREAD_WAITING) {
|
||||||
if (thread->condition_variable_entry)
|
if (get_thread_wait_cvar(thread))
|
||||||
kprintf("%p ", thread->condition_variable_entry->Variable());
|
kprintf("%p ", get_thread_wait_cvar(thread));
|
||||||
else
|
else
|
||||||
kprintf("%10ld ", thread->sem.blocking);
|
kprintf("%10ld ", get_thread_wait_sem(thread));
|
||||||
} else
|
} else
|
||||||
kprintf(" - ");
|
kprintf(" - ");
|
||||||
|
|
||||||
@@ -1881,13 +1919,6 @@ thread_init(kernel_args *args)
|
|||||||
// zero out the dead thread structure q
|
// zero out the dead thread structure q
|
||||||
memset(&dead_q, 0, sizeof(dead_q));
|
memset(&dead_q, 0, sizeof(dead_q));
|
||||||
|
|
||||||
// allocate snooze sem
|
|
||||||
sSnoozeSem = create_sem(0, "snooze sem");
|
|
||||||
if (sSnoozeSem < 0) {
|
|
||||||
panic("error creating snooze sem\n");
|
|
||||||
return sSnoozeSem;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arch_thread_init(args) < B_OK)
|
if (arch_thread_init(args) < B_OK)
|
||||||
panic("arch_thread_init() failed!\n");
|
panic("arch_thread_init() failed!\n");
|
||||||
|
|
||||||
@@ -2039,6 +2070,77 @@ thread_preboot_init_percpu(struct kernel_args *args, int32 cpuNum)
|
|||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - thread blocking API
|
||||||
|
|
||||||
|
|
||||||
|
static status_t
|
||||||
|
thread_block_timeout(timer* timer)
|
||||||
|
{
|
||||||
|
// The timer has been installed with B_TIMER_ACQUIRE_THREAD_LOCK, so
|
||||||
|
// we're holding the thread lock already. This makes things comfortably
|
||||||
|
// easy.
|
||||||
|
|
||||||
|
struct thread* thread = (struct thread*)timer->user_data;
|
||||||
|
if (thread_unblock_locked(thread, B_TIMED_OUT))
|
||||||
|
return B_INVOKE_SCHEDULER;
|
||||||
|
|
||||||
|
return B_HANDLED_INTERRUPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
thread_block()
|
||||||
|
{
|
||||||
|
InterruptsSpinLocker _(thread_spinlock);
|
||||||
|
return thread_block_locked(thread_get_current_thread());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
thread_unblock(status_t threadID, status_t status)
|
||||||
|
{
|
||||||
|
InterruptsSpinLocker _(thread_spinlock);
|
||||||
|
|
||||||
|
struct thread* thread = thread_get_thread_struct_locked(threadID);
|
||||||
|
if (thread == NULL)
|
||||||
|
return false;
|
||||||
|
return thread_unblock_locked(thread, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
thread_block_with_timeout_locked(uint32 timeoutFlags, bigtime_t timeout)
|
||||||
|
{
|
||||||
|
struct thread* thread = thread_get_current_thread();
|
||||||
|
|
||||||
|
if (thread->wait.status != 1)
|
||||||
|
return thread->wait.status;
|
||||||
|
|
||||||
|
// Timer flags: absolute/relative + "acquire thread lock". The latter
|
||||||
|
// avoids nasty race conditions and deadlock problems that could otherwise
|
||||||
|
// occur between our cancel_timer() and a concurrently executing
|
||||||
|
// thread_block_timeout().
|
||||||
|
uint32 timerFlags = (timeoutFlags & B_RELATIVE_TIMEOUT)
|
||||||
|
? B_ONE_SHOT_RELATIVE_TIMER : B_ONE_SHOT_ABSOLUTE_TIMER;
|
||||||
|
timerFlags |= B_TIMER_ACQUIRE_THREAD_LOCK;
|
||||||
|
|
||||||
|
// install the timer
|
||||||
|
thread->wait.unblock_timer.user_data = thread;
|
||||||
|
add_timer(&thread->wait.unblock_timer, &thread_block_timeout, timeout,
|
||||||
|
timerFlags);
|
||||||
|
|
||||||
|
// block
|
||||||
|
status_t error = thread_block_locked(thread);
|
||||||
|
|
||||||
|
// cancel timer, if it didn't fire
|
||||||
|
if (error != B_TIMED_OUT)
|
||||||
|
cancel_timer(&thread->wait.unblock_timer);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - public kernel API
|
// #pragma mark - public kernel API
|
||||||
|
|
||||||
|
|
||||||
@@ -2297,7 +2399,12 @@ snooze_etc(bigtime_t timeout, int timebase, uint32 flags)
|
|||||||
if (timebase != B_SYSTEM_TIMEBASE)
|
if (timebase != B_SYSTEM_TIMEBASE)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
status = acquire_sem_etc(sSnoozeSem, 1, flags, timeout);
|
InterruptsSpinLocker _(thread_spinlock);
|
||||||
|
struct thread* thread = thread_get_current_thread();
|
||||||
|
|
||||||
|
thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_SNOOZE, NULL);
|
||||||
|
status = thread_block_with_timeout_locked(flags, timeout);
|
||||||
|
|
||||||
if (status == B_TIMED_OUT || status == B_WOULD_BLOCK)
|
if (status == B_TIMED_OUT || status == B_WOULD_BLOCK)
|
||||||
return B_OK;
|
return B_OK;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user