diff --git a/headers/private/kernel/lock.h b/headers/private/kernel/lock.h index efd770b270..e5d9d186c3 100644 --- a/headers/private/kernel/lock.h +++ b/headers/private/kernel/lock.h @@ -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 #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 diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index ac3240e783..f6a774250d 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -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 diff --git a/headers/private/kernel/util/AutoLock.h b/headers/private/kernel/util/AutoLock.h index e51cd88509..96339d2e30 100644 --- a/headers/private/kernel/util/AutoLock.h +++ b/headers/private/kernel/util/AutoLock.h @@ -67,6 +67,24 @@ public: // BenaphoreLocker typedef AutoLocker 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 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; diff --git a/src/system/kernel/Jamfile b/src/system/kernel/Jamfile index 15d41e8fc6..bc311f0fd1 100644 --- a/src/system/kernel/Jamfile +++ b/src/system/kernel/Jamfile @@ -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 diff --git a/src/system/kernel/lock.c b/src/system/kernel/lock.cpp similarity index 56% rename from src/system/kernel/lock.c rename to src/system/kernel/lock.cpp index 158a995342..cbce4119ad 100644 --- a/src/system/kernel/lock.c +++ b/src/system/kernel/lock.cpp @@ -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 #include -#include -#include -#include #include +#include +#include #include +#include + + +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", + "\n" + "Prints info about the specified cutex.\n" + " - pointer to the cutex to print the info for.\n", 0); +} diff --git a/src/system/kernel/main.c b/src/system/kernel/main.c index 5c1212debf..4170cfe1ac 100644 --- a/src/system/kernel/main.c +++ b/src/system/kernel/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index ad397dba2c..303b8e603f 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -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)