* Moved created subdirectory src/system/kernel/lock.cpp to new subdirectory

locks.
* Added syscalls for a new kind of mutex. A mutex consists only of an int32 and
  doesn't require any kernel resources. So it's initialization cannot fail
  (it consists only of setting the mutex value to 0). An uncontended lock or
  unlock operation can basically consist of an atomic_*() in userland. The
  syscalls (when the mutex is contended) are a bit more expensive than semaphore
  operations, though.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36158 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2010-04-11 16:07:39 +00:00
parent 216073afd7
commit 813d4cbe94
8 changed files with 369 additions and 1 deletions

View File

@ -0,0 +1,29 @@
/*
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_USER_MUTEX_H
#define _KERNEL_USER_MUTEX_H
#include <SupportDefs.h>
#ifdef __cplusplus
extern "C" {
#endif
void user_mutex_init();
status_t _user_mutex_lock(int32* mutex, const char* name, uint32 flags,
bigtime_t timeout);
status_t _user_mutex_unlock(int32* mutex, uint32 flags);
status_t _user_mutex_switch_lock(int32* fromMutex, int32* toMutex,
const char* name, uint32 flags, bigtime_t timeout);
#ifdef __cplusplus
}
#endif
#endif /* _KERNEL_USER_MUTEX_H */

View File

@ -71,6 +71,13 @@ extern status_t _kern_get_safemode_option(const char *parameter,
extern ssize_t _kern_wait_for_objects(object_wait_info* infos, int numInfos,
uint32 flags, bigtime_t timeout);
/* user mutex functions */
extern status_t _kern_mutex_lock(int32* mutex, const char* name,
uint32 flags, bigtime_t timeout);
extern status_t _kern_mutex_unlock(int32* mutex, uint32 flags);
extern status_t _kern_mutex_switch_lock(int32* fromMutex, int32* toMutex,
const char* name, uint32 flags, bigtime_t timeout);
/* sem functions */
extern sem_id _kern_create_sem(int count, const char *name);
extern status_t _kern_delete_sem(sem_id id);

View File

@ -0,0 +1,21 @@
/*
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#ifndef _SYSTEM_USER_MUTEX_DEFS_H
#define _SYSTEM_USER_MUTEX_DEFS_H
// user mutex specific flags passed to _kern_user_mutex_unlock()
#define B_USER_MUTEX_UNBLOCK_ALL 0x80000000
// All threads currently waiting on the mutex will be unblocked. The mutex
// state will be locked.
// mutex value flags
#define B_USER_MUTEX_LOCKED 0x01
#define B_USER_MUTEX_WAITING 0x02
#define B_USER_MUTEX_DISABLED 0x04
#endif /* _SYSTEM_USER_MUTEX_DEFS_H */

View File

@ -10,6 +10,7 @@ SubDir HAIKU_TOP src system kernel ;
SubDirC++Flags $(defines) ;
}
SEARCH_SOURCE += [ FDirName $(SUBDIR) locks ] ;
SEARCH_SOURCE += [ FDirName $(SUBDIR) scheduler ] ;
UsePrivateHeaders libroot ;
@ -34,7 +35,6 @@ KernelMergeObject kernel_core.o :
kernel_daemon.cpp
linkhack.c
listeners.cpp
lock.cpp
low_resource_manager.cpp
main.cpp
module.cpp
@ -53,6 +53,10 @@ KernelMergeObject kernel_core.o :
usergroup.cpp
wait_for_objects.cpp
# locks
lock.cpp
user_mutex.cpp
# scheduler
scheduler.cpp
scheduler_affine.cpp

View File

@ -0,0 +1,302 @@
/*
* Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <user_mutex.h>
#include <user_mutex_defs.h>
#include <condition_variable.h>
#include <kernel.h>
#include <lock.h>
#include <smp.h>
#include <syscall_restart.h>
#include <util/AutoLock.h>
#include <util/OpenHashTable.h>
#include <vm/vm.h>
#include <vm/VMArea.h>
struct UserMutexEntry;
typedef DoublyLinkedList<UserMutexEntry> UserMutexEntryList;
struct UserMutexEntry : public DoublyLinkedListLinkImpl<UserMutexEntry> {
addr_t address;
ConditionVariable condition;
bool locked;
UserMutexEntryList otherEntries;
UserMutexEntry* hashNext;
};
struct UserMutexHashDefinition {
typedef addr_t KeyType;
typedef UserMutexEntry ValueType;
size_t HashKey(addr_t key) const
{
return key >> 2;
}
size_t Hash(const UserMutexEntry* value) const
{
return HashKey(value->address);
}
bool Compare(addr_t key, const UserMutexEntry* value) const
{
return value->address == key;
}
UserMutexEntry*& GetLink(UserMutexEntry* value) const
{
return value->hashNext;
}
};
typedef BOpenHashTable<UserMutexHashDefinition> UserMutexTable;
static UserMutexTable sUserMutexTable;
static mutex sUserMutexTableLock = MUTEX_INITIALIZER("user mutex table");
static void
add_user_mutex_entry(UserMutexEntry* entry)
{
UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address);
if (firstEntry != NULL)
firstEntry->otherEntries.Add(entry);
else
sUserMutexTable.Insert(entry);
}
static bool
remove_user_mutex_entry(UserMutexEntry* entry)
{
UserMutexEntry* firstEntry = sUserMutexTable.Lookup(entry->address);
if (firstEntry != entry) {
// The entry is not the first entry in the table. Just remove it from
// the first entry's list.
firstEntry->otherEntries.Remove(entry);
return true;
}
// The entry is the first entry in the table. Remove it from the table and,
// if any, add the next entry to the table.
sUserMutexTable.Remove(entry);
firstEntry = entry->otherEntries.Head();
if (firstEntry != NULL) {
firstEntry->otherEntries.MoveFrom(&entry->otherEntries);
sUserMutexTable.Insert(firstEntry);
return true;
}
return false;
}
static status_t
user_mutex_lock_locked(vint32* mutex, addr_t physicalAddress, const char* name,
uint32 flags, bigtime_t timeout, MutexLocker& locker)
{
// mark the mutex locked + waiting
int32 oldValue = atomic_or(mutex,
B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING);
// The mutex might have been unlocked (or disabled) in the meantime.
if ((oldValue & (B_USER_MUTEX_LOCKED | B_USER_MUTEX_WAITING)) == 0
|| (oldValue & B_USER_MUTEX_DISABLED) != 0) {
// clear the waiting flag and be done
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
return B_OK;
}
// we have to wait
// add the entry to the table
UserMutexEntry entry;
entry.address = physicalAddress;
entry.locked = false;
add_user_mutex_entry(&entry);
// wait
ConditionVariableEntry waitEntry;
entry.condition.Init((void*)physicalAddress, "user mutex");
entry.condition.Add(&waitEntry);
locker.Unlock();
status_t error = waitEntry.Wait(flags, timeout);
locker.Lock();
// dequeue
if (!remove_user_mutex_entry(&entry)) {
// no one is waiting anymore -- clear the waiting flag
atomic_and(mutex, ~(int32)B_USER_MUTEX_WAITING);
}
if (error != B_OK
&& (entry.locked || (*mutex & B_USER_MUTEX_DISABLED) != 0)) {
// timeout or interrupt, but the mutex was unlocked or disabled in time
error = B_OK;
}
return error;
}
static void
user_mutex_unlock_locked(vint32* mutex, addr_t physicalAddress, uint32 flags)
{
if (UserMutexEntry* entry = sUserMutexTable.Lookup(physicalAddress)) {
// unblock the first thread
entry->locked = true;
entry->condition.NotifyOne();
if ((flags & B_USER_MUTEX_UNBLOCK_ALL) != 0
&& (*mutex & B_USER_MUTEX_DISABLED) != 0) {
// unblock all the other waiting threads as well
for (UserMutexEntryList::Iterator it
= entry->otherEntries.GetIterator();
UserMutexEntry* otherEntry = it.Next();) {
otherEntry->locked = true;
otherEntry->condition.NotifyOne();
}
}
}
}
static status_t
user_mutex_lock(int32* mutex, const char* name, uint32 flags, bigtime_t timeout)
{
// wire the page and get the physical address
VMPageWiringInfo wiringInfo;
status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true,
&wiringInfo);
if (error != B_OK)
return error;
// get the lock
{
MutexLocker locker(sUserMutexTableLock);
error = user_mutex_lock_locked(mutex, wiringInfo.physicalAddress, name,
flags, timeout, locker);
}
// unwire the page
vm_unwire_page(&wiringInfo);
return error;
}
static status_t
user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
uint32 flags, bigtime_t timeout)
{
// wire the pages and get the physical addresses
VMPageWiringInfo fromWiringInfo;
status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)fromMutex, true,
&fromWiringInfo);
if (error != B_OK)
return error;
VMPageWiringInfo toWiringInfo;
error = vm_wire_page(B_CURRENT_TEAM, (addr_t)toMutex, true, &toWiringInfo);
if (error != B_OK) {
vm_unwire_page(&fromWiringInfo);
return error;
}
// unlock the first mutex and lock the second one
{
MutexLocker locker(sUserMutexTableLock);
user_mutex_unlock_locked(fromMutex, fromWiringInfo.physicalAddress,
flags);
error = user_mutex_lock_locked(toMutex, toWiringInfo.physicalAddress,
name, flags, timeout, locker);
}
// unwire the pages
vm_unwire_page(&toWiringInfo);
vm_unwire_page(&fromWiringInfo);
return error;
}
// #pragma mark - kernel private
void
user_mutex_init()
{
if (sUserMutexTable.Init() != B_OK)
panic("user_mutex_init(): Failed to init table!");
}
// #pragma mark - syscalls
status_t
_user_mutex_lock(int32* mutex, const char* name, uint32 flags,
bigtime_t timeout)
{
if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
return B_BAD_ADDRESS;
syscall_restart_handle_timeout_pre(flags, timeout);
status_t error = user_mutex_lock(mutex, name, flags | B_CAN_INTERRUPT,
timeout);
return syscall_restart_handle_timeout_post(error, timeout);
}
status_t
_user_mutex_unlock(int32* mutex, uint32 flags)
{
if (mutex == NULL || !IS_USER_ADDRESS(mutex) || (addr_t)mutex % 4 != 0)
return B_BAD_ADDRESS;
// wire the page and get the physical address
VMPageWiringInfo wiringInfo;
status_t error = vm_wire_page(B_CURRENT_TEAM, (addr_t)mutex, true,
&wiringInfo);
if (error != B_OK)
return error;
{
MutexLocker locker(sUserMutexTableLock);
user_mutex_unlock_locked(mutex, wiringInfo.physicalAddress, flags);
}
vm_unwire_page(&wiringInfo);
return B_OK;
}
status_t
_user_mutex_switch_lock(int32* fromMutex, int32* toMutex, const char* name,
uint32 flags, bigtime_t timeout)
{
if (fromMutex == NULL || !IS_USER_ADDRESS(fromMutex)
|| (addr_t)fromMutex % 4 != 0 || toMutex == NULL
|| !IS_USER_ADDRESS(toMutex) || (addr_t)toMutex % 4 != 0) {
return B_BAD_ADDRESS;
}
syscall_restart_handle_timeout_pre(flags, timeout);
status_t error = user_mutex_switch_lock(fromMutex, toMutex, name,
flags | B_CAN_INTERRUPT, timeout);
return syscall_restart_handle_timeout_post(error, timeout);
}

View File

@ -47,6 +47,7 @@
#include <team.h>
#include <timer.h>
#include <user_debugger.h>
#include <user_mutex.h>
#include <vfs.h>
#include <vm/vm.h>
#include <boot/kernel_args.h>
@ -257,6 +258,9 @@ main2(void *unused)
TRACE("init ports\n");
port_init(&sKernelArgs);
TRACE("init user mutex\n");
user_mutex_init();
TRACE("Init modules\n");
boot_splash_set_stage(BOOT_SPLASH_STAGE_1_INIT_MODULES);
module_init_post_threads();

View File

@ -44,6 +44,7 @@
#include <thread.h>
#include <tracing.h>
#include <user_atomic.h>
#include <user_mutex.h>
#include <usergroup.h>
#include <util/AutoLock.h>
#include <vfs.h>