* Introduced a new locking primitive I called "cutex" (sorry for the

name, couldn't resist :-P). It's semantically equivalent to a mutex,
  but doesn't need a semaphore (it uses thread blocking and a simple
  queue instead). Initialization can't fail. In fact it is ready to use
  without initialization when living in the bss segment, also in the
  early boot process. It's as fast as a benaphore in cases of low lock
  contention, and faster otherwise.  Only disadvantage is the higher
  immediate memory footprint of 16 bytes.
* Changed how the "thread" and "threads" debugger commands list the
  objects they are waiting for. Cutexes are also included.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25276 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-05-01 01:53:07 +00:00
parent 4e6b38af6c
commit 8562499f44
7 changed files with 347 additions and 29 deletions

View File

@ -39,13 +39,29 @@ typedef struct rw_lock {
#define RW_MAX_READERS 1000000
struct cutex_waiter;
typedef struct cutex {
const char* name;
struct cutex_waiter* waiters;
#ifdef KDEBUG
thread_id holder;
#else
int32 count;
#endif
int32 release_count;
} cutex;
#if 0 && KDEBUG // XXX disable this for now, it causes problems when including thread.h here
# include <thread.h>
#define ASSERT_LOCKED_RECURSIVE(r) { ASSERT(thread_get_current_thread_id() == (r)->holder); }
#define ASSERT_LOCKED_MUTEX(m) { ASSERT(thread_get_current_thread_id() == (m)->holder); }
#define ASSERT_LOCKED_CUTEX(m) { ASSERT(thread_get_current_thread_id() == (m)->holder); }
#else
#define ASSERT_LOCKED_RECURSIVE(r)
#define ASSERT_LOCKED_MUTEX(m)
#define ASSERT_LOCKED_CUTEX(m)
#endif
@ -103,6 +119,54 @@ extern status_t rw_lock_read_unlock(rw_lock *lock);
extern status_t rw_lock_write_lock(rw_lock *lock);
extern status_t rw_lock_write_unlock(rw_lock *lock);
extern void cutex_init(cutex* lock, const char *name);
// name is *not* cloned nor freed in cutex_destroy()
extern void cutex_destroy(cutex* lock);
// implementation private:
extern void _cutex_lock(cutex* lock);
extern void _cutex_unlock(cutex* lock);
extern status_t _cutex_trylock(cutex* lock);
static inline void
cutex_lock(cutex* lock)
{
#ifdef KDEBUG
_cutex_lock(lock);
#else
if (atomic_add(&lock->count, -1) < 0)
_cutex_lock(lock);
#endif
}
static inline status_t
cutex_trylock(cutex* lock)
{
#ifdef KDEBUG
return _cutex_trylock(lock);
#else
if (atomic_test_and_set(&lock->count, -1, 0) != 0)
return B_WOULD_BLOCK;
#endif
}
static inline void
cutex_unlock(cutex* lock)
{
#ifdef KDEBUG
_cutex_unlock(lock);
#else
if (atomic_add(&lock->count, 1) < -1)
_cutex_unlock(lock);
#endif
}
extern void lock_debug_init();
#ifdef __cplusplus
}
#endif

View File

@ -62,6 +62,7 @@ enum {
THREAD_BLOCK_TYPE_CONDITION_VARIABLE = 1,
THREAD_BLOCK_TYPE_SNOOZE = 2,
THREAD_BLOCK_TYPE_SIGNAL = 3,
THREAD_BLOCK_TYPE_CUTEX = 4,
THREAD_BLOCK_TYPE_OTHER = 9999,
THREAD_BLOCK_TYPE_USER_BASE = 10000

View File

@ -67,6 +67,24 @@ public:
// BenaphoreLocker
typedef AutoLocker<benaphore, BenaphoreLocking> BenaphoreLocker;
// CutexLocking
class CutexLocking {
public:
inline bool Lock(cutex *lockable)
{
cutex_lock(lockable);
return true;
}
inline void Unlock(cutex *lockable)
{
cutex_unlock(lockable);
}
};
// CutexLocker
typedef AutoLocker<cutex, CutexLocking> CutexLocker;
// InterruptsLocking
class InterruptsLocking {
public:
@ -153,6 +171,7 @@ using BPrivate::AutoLocker;
using BPrivate::MutexLocker;
using BPrivate::RecursiveLocker;
using BPrivate::BenaphoreLocker;
using BPrivate::CutexLocker;
using BPrivate::InterruptsLocker;
using BPrivate::SpinLocker;
using BPrivate::InterruptsSpinLocker;

View File

@ -27,7 +27,7 @@ KernelMergeObject kernel_core.o :
int.c
kernel_daemon.c
linkhack.c
lock.c
lock.cpp
main.c
module.cpp
Notifications.cpp

View File

@ -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.
* Distributed under the terms of the MIT License.
*
@ -8,14 +9,22 @@
/* Mutex and recursive_lock code */
#include <lock.h>
#include <OS.h>
#include <lock.h>
#include <kernel.h>
#include <int.h>
#include <debug.h>
#include <int.h>
#include <kernel.h>
#include <thread.h>
#include <util/AutoLock.h>
struct cutex_waiter {
struct thread* thread;
cutex_waiter* next; // next in queue
cutex_waiter* last; // last in queue (valid for the first in queue)
};
int32
@ -279,3 +288,171 @@ rw_lock_write_unlock(rw_lock *lock)
return release_sem_etc(lock->sem, RW_MAX_READERS, 0);
}
// #pragma mark -
void
cutex_init(cutex* lock, const char *name)
{
lock->name = name;
lock->waiters = NULL;
#ifdef KDEBUG
lock->holder = -1;
#else
lock->count = 0;
#endif
lock->release_count = 0;
}
void
cutex_destroy(cutex* lock)
{
// no-op
}
void
_cutex_lock(cutex* lock)
{
#ifdef KDEBUG
if (!kernel_startup && !are_interrupts_enabled()) {
panic("_cutex_unlock: called with interrupts disabled for lock %p",
lock);
}
#endif
InterruptsSpinLocker _(thread_spinlock);
// Might have been released after we decremented the count, but before
// we acquired the spinlock.
#ifdef KDEBUG
if (lock->release_count >= 0) {
lock->holder = thread_get_current_thread_id();
#else
if (lock->release_count > 0) {
#endif
lock->release_count--;
return;
}
// enqueue in waiter list
cutex_waiter waiter;
waiter.thread = thread_get_current_thread();
waiter.next = NULL;
if (lock->waiters != NULL) {
lock->waiters->last->next = &waiter;
} else
lock->waiters = &waiter;
lock->waiters->last = &waiter;
// block
thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_CUTEX, lock);
thread_block_locked(waiter.thread);
#ifdef KDEBUG
lock->holder = waiter.thread->id;
#endif
}
void
_cutex_unlock(cutex* lock)
{
InterruptsSpinLocker _(thread_spinlock);
#ifdef KDEBUG
if (thread_get_current_thread_id() != lock->holder) {
panic("_cutex_unlock() failure: thread %ld is trying to release "
"cutex %p (current holder %ld)\n", thread_get_current_thread_id(),
lock, lock->holder);
return;
}
lock->holder = -1;
#endif
cutex_waiter* waiter = lock->waiters;
if (waiter != NULL) {
// dequeue the first waiter
lock->waiters = waiter->next;
if (lock->waiters != NULL)
lock->waiters->last = waiter->last;
// unblock thread
thread_unblock_locked(waiter->thread, B_OK);
} else {
// We acquired the spinlock before the locker that is going to wait.
// Just increment the release count.
lock->release_count++;
}
}
status_t
_cutex_trylock(cutex* lock)
{
#ifdef KDEBUG
InterruptsSpinLocker _(thread_spinlock);
if (lock->release_count >= 0) {
lock->holder = thread_get_current_thread_id();
lock->release_count--;
return B_OK;
}
#endif
return B_WOULD_BLOCK;
}
static int
dump_cutex_info(int argc, char** argv)
{
if (argc < 2) {
print_debugger_command_usage(argv[0]);
return 0;
}
cutex* lock = (cutex*)strtoul(argv[1], NULL, 0);
if (!IS_KERNEL_ADDRESS(lock)) {
kprintf("invalid address: %p\n", lock);
return 0;
}
kprintf("cutex %p:\n", lock);
kprintf(" name: %s\n", lock->name);
kprintf(" release count: %ld\n", lock->release_count);
#ifdef KDEBUG
kprintf(" holder: %ld\n", lock->holder);
#else
kprintf(" count: %ld\n", lock->count);
#endif
kprintf(" waiting threads:");
cutex_waiter* waiter = lock->waiters;
while (waiter != NULL) {
kprintf(" %ld", waiter->thread->id);
waiter = waiter->next;
}
kputs("\n");
return 0;
}
// #pragma mark -
void
lock_debug_init()
{
add_debugger_command_etc("cutex", &dump_cutex_info,
"Dump info about a cutex",
"<cutex>\n"
"Prints info about the specified cutex.\n"
" <cutex> - pointer to the cutex to print the info for.\n", 0);
}

View File

@ -28,6 +28,7 @@
#include <kmodule.h>
#include <kscheduler.h>
#include <ksyscalls.h>
#include <lock.h>
#include <messaging.h>
#include <Notifications.h>
#include <port.h>
@ -117,6 +118,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
// now we can use the heap and create areas
arch_platform_init_post_vm(&sKernelArgs);
lock_debug_init();
TRACE("init driver_settings\n");
boot_item_init();
driver_settings_init(&sKernelArgs);

View File

@ -667,17 +667,6 @@ get_thread_wait_sem(struct thread* thread)
}
static ConditionVariable*
get_thread_wait_cvar(struct thread* thread)
{
if (thread->state == B_THREAD_WAITING
&& thread->wait.type == THREAD_BLOCK_TYPE_CONDITION_VARIABLE) {
return (ConditionVariable*)thread->wait.object;
}
return NULL;
}
/*!
Fills the thread_info structure with information from the specified
thread.
@ -1091,10 +1080,6 @@ state_to_text(struct thread *thread, int32 state)
return "receive";
break;
}
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
default:
break;
}
return "waiting";
@ -1133,8 +1118,47 @@ _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", get_thread_wait_sem(thread));
kprintf("condition variable: %p\n", get_thread_wait_cvar(thread));
kprintf("waiting for: ");
if (thread->state == B_THREAD_WAITING) {
switch (thread->wait.type) {
case THREAD_BLOCK_TYPE_SEMAPHORE:
{
sem_id sem = (sem_id)(addr_t)thread->wait.object;
if (sem == thread->msg.read_sem)
kprintf("data\n");
else
kprintf("semaphore %ld\n", sem);
break;
}
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
kprintf("condition variable %p\n", thread->wait.object);
break;
case THREAD_BLOCK_TYPE_SNOOZE:
kprintf("snooze()\n");
break;
case THREAD_BLOCK_TYPE_SIGNAL:
kprintf("signal\n");
break;
case THREAD_BLOCK_TYPE_CUTEX:
kprintf("cutex %p\n", thread->wait.object);
break;
case THREAD_BLOCK_TYPE_OTHER:
kprintf("other (%s)\n", (char*)thread->wait.object);
break;
default:
kprintf("unknown (%p)\n", thread->wait.object);
break;
}
}
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);
@ -1254,8 +1278,8 @@ dump_thread_list(int argc, char **argv)
kprintf("ignoring invalid team argument.\n");
}
kprintf("thread id state sem/cv cpu pri stack team "
"name\n");
kprintf("thread id state wait for object cpu pri stack "
" team name\n");
hash_open(sThreadHash, &i);
while ((thread = (struct thread*)hash_next(sThreadHash, &i)) != NULL) {
@ -1268,17 +1292,48 @@ dump_thread_list(int argc, char **argv)
|| (realTimeOnly && thread->priority < B_REAL_TIME_DISPLAY_PRIORITY))
continue;
kprintf("%p %6ld %-9s", thread, thread->id, state_to_text(thread,
kprintf("%p %6ld %-10s", thread, thread->id, state_to_text(thread,
thread->state));
// does it block on a semaphore or a condition variable?
if (thread->state == B_THREAD_WAITING) {
if (get_thread_wait_cvar(thread))
kprintf("%p ", get_thread_wait_cvar(thread));
else
kprintf("%10ld ", get_thread_wait_sem(thread));
switch (thread->wait.type) {
case THREAD_BLOCK_TYPE_SEMAPHORE:
{
sem_id sem = (sem_id)(addr_t)thread->wait.object;
if (sem == thread->msg.read_sem)
kprintf(" ");
else
kprintf("sem %12ld ", sem);
break;
}
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
kprintf("cvar %p ", thread->wait.object);
break;
case THREAD_BLOCK_TYPE_SNOOZE:
kprintf(" ");
break;
case THREAD_BLOCK_TYPE_SIGNAL:
kprintf("signal ");
break;
case THREAD_BLOCK_TYPE_CUTEX:
kprintf("cutex %p ", thread->wait.object);
break;
case THREAD_BLOCK_TYPE_OTHER:
kprintf("other ");
break;
default:
kprintf("??? %p ", thread->wait.object);
break;
}
} else
kprintf(" - ");
kprintf(" - ");
// on which CPU does it run?
if (thread->cpu)