* 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:
+143
-36
@@ -78,8 +78,6 @@ static thread_id sNextThreadID = 1;
|
||||
static int32 sMaxThreads = 4096;
|
||||
static int32 sUsedThreads = 0;
|
||||
|
||||
static sem_id sSnoozeSem = -1;
|
||||
|
||||
// death stacks - used temporarily as a thread cleans itself up
|
||||
struct death_stack {
|
||||
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->team = NULL;
|
||||
thread->cpu = cpu;
|
||||
thread->sem.blocking = -1;
|
||||
thread->condition_variable_entry = NULL;
|
||||
thread->fault_handler = 0;
|
||||
thread->page_faults_allowed = 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
|
||||
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);
|
||||
|
||||
if (thread->state == B_THREAD_WAITING) {
|
||||
if (thread->sem.blocking == sSnoozeSem)
|
||||
info->state = B_THREAD_ASLEEP;
|
||||
else if (thread->sem.blocking == thread->msg.read_sem)
|
||||
info->state = B_THREAD_RECEIVING;
|
||||
else
|
||||
info->state = B_THREAD_WAITING;
|
||||
info->state = B_THREAD_WAITING;
|
||||
|
||||
switch (thread->wait.type) {
|
||||
case THREAD_BLOCK_TYPE_SNOOZE:
|
||||
info->state = B_THREAD_ASLEEP;
|
||||
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
|
||||
info->state = (thread_state)thread->state;
|
||||
|
||||
info->priority = thread->priority;
|
||||
info->sem = thread->sem.blocking;
|
||||
info->user_time = thread->user_time;
|
||||
info->kernel_time = thread->kernel_time;
|
||||
info->stack_base = (void *)thread->user_stack_base;
|
||||
info->stack_end = (void *)(thread->user_stack_base
|
||||
+ thread->user_stack_size);
|
||||
info->sem = get_thread_wait_sem(thread);
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
send_data_etc(thread_id id, int32 code, const void *buffer,
|
||||
size_t bufferSize, int32 flags)
|
||||
@@ -989,12 +1019,26 @@ state_to_text(struct thread *thread, int32 state)
|
||||
return "running";
|
||||
|
||||
case B_THREAD_WAITING:
|
||||
if (thread->sem.blocking == sSnoozeSem)
|
||||
return "zzz";
|
||||
if (thread->sem.blocking == thread->msg.read_sem)
|
||||
return "receive";
|
||||
{
|
||||
switch (thread->wait.type) {
|
||||
case THREAD_BLOCK_TYPE_SNOOZE:
|
||||
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";
|
||||
}
|
||||
|
||||
case B_THREAD_SUSPENDED:
|
||||
return "suspended";
|
||||
@@ -1029,14 +1073,8 @@ _dump_thread_info(struct thread *thread)
|
||||
kprintf("sig_pending: %#lx (blocked: %#lx)\n", thread->sig_pending,
|
||||
thread->sig_block_mask);
|
||||
kprintf("in_kernel: %d\n", thread->in_kernel);
|
||||
kprintf(" sem.blocking: %ld\n", thread->sem.blocking);
|
||||
kprintf(" sem.count: %ld\n", thread->sem.count);
|
||||
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("sem blocking: %ld\n", get_thread_wait_sem(thread));
|
||||
kprintf("condition variable: %p\n", get_thread_wait_cvar(thread));
|
||||
kprintf("fault_handler: %p\n", (void *)thread->fault_handler);
|
||||
kprintf("args: %p %p\n", thread->args1, thread->args2);
|
||||
kprintf("entry: %p\n", (void *)thread->entry);
|
||||
@@ -1165,7 +1203,7 @@ dump_thread_list(int argc, char **argv)
|
||||
if ((requiredState && thread->state != requiredState)
|
||||
|| (calling && !arch_debug_contains_call(thread, callSymbol,
|
||||
callStart, callEnd))
|
||||
|| (sem > 0 && thread->sem.blocking != sem)
|
||||
|| (sem > 0 && get_thread_wait_sem(thread) != sem)
|
||||
|| (team > 0 && thread->team->id != team)
|
||||
|| (realTimeOnly && thread->priority < B_REAL_TIME_DISPLAY_PRIORITY))
|
||||
continue;
|
||||
@@ -1175,10 +1213,10 @@ dump_thread_list(int argc, char **argv)
|
||||
|
||||
// does it block on a semaphore or a condition variable?
|
||||
if (thread->state == B_THREAD_WAITING) {
|
||||
if (thread->condition_variable_entry)
|
||||
kprintf("%p ", thread->condition_variable_entry->Variable());
|
||||
if (get_thread_wait_cvar(thread))
|
||||
kprintf("%p ", get_thread_wait_cvar(thread));
|
||||
else
|
||||
kprintf("%10ld ", thread->sem.blocking);
|
||||
kprintf("%10ld ", get_thread_wait_sem(thread));
|
||||
} else
|
||||
kprintf(" - ");
|
||||
|
||||
@@ -1881,13 +1919,6 @@ thread_init(kernel_args *args)
|
||||
// zero out the dead thread structure 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)
|
||||
panic("arch_thread_init() failed!\n");
|
||||
|
||||
@@ -2039,6 +2070,77 @@ thread_preboot_init_percpu(struct kernel_args *args, int32 cpuNum)
|
||||
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
|
||||
|
||||
|
||||
@@ -2297,7 +2399,12 @@ snooze_etc(bigtime_t timeout, int timebase, uint32 flags)
|
||||
if (timebase != B_SYSTEM_TIMEBASE)
|
||||
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)
|
||||
return B_OK;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user