Added support for POSIX semaphores (the ones from the XSI extension
Realtime option group). The implementation should be complete, but is totally untested yet. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25326 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2008, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _SEMAPHORE_H_
|
||||
#define _SEMAPHORE_H_
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
int id;
|
||||
} sem_t;
|
||||
|
||||
#define SEM_FAILED ((sem_t*)(long)-1)
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
sem_t* sem_open(const char* name, int openFlags,...);
|
||||
int sem_close(sem_t* semaphore);
|
||||
int sem_unlink(const char* name);
|
||||
|
||||
int sem_init(sem_t* semaphore, int shared, unsigned value);
|
||||
int sem_destroy(sem_t* semaphore);
|
||||
|
||||
int sem_post(sem_t* semaphore);
|
||||
int sem_timedwait(sem_t* semaphore, const struct timespec* timeout);
|
||||
int sem_trywait(sem_t* semaphore);
|
||||
int sem_wait(sem_t* semaphore);
|
||||
int sem_getvalue(sem_t* semaphore, int* value);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
||||
#endif /* _SEMAPHORE_H_ */
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef KERNEL_REALTIME_SEM_H
|
||||
#define KERNEL_REALTIME_SEM_H
|
||||
|
||||
#include <semaphore.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
|
||||
struct realtime_sem_context;
|
||||
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
void realtime_sem_init();
|
||||
void delete_realtime_sem_context(struct realtime_sem_context* context);
|
||||
struct realtime_sem_context* clone_realtime_sem_context(
|
||||
struct realtime_sem_context* context);
|
||||
|
||||
status_t _user_realtime_sem_open(const char* name, int openFlags,
|
||||
mode_t mode, uint32 semCount, sem_t* userSem,
|
||||
sem_t** _usedUserSem);
|
||||
status_t _user_realtime_sem_close(sem_id semID, sem_t** _deleteUserSem);
|
||||
status_t _user_realtime_sem_unlink(const char* name);
|
||||
|
||||
status_t _user_realtime_sem_get_value(sem_id semID, int* value);
|
||||
status_t _user_realtime_sem_post(sem_id semID);
|
||||
status_t _user_realtime_sem_wait(sem_id semID, bigtime_t timeout);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
|
||||
#endif // KERNEL_REALTIME_SEM_H
|
||||
@@ -20,7 +20,7 @@ struct select_info;
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern status_t sem_init(struct kernel_args *args);
|
||||
extern status_t haiku_sem_init(struct kernel_args *args);
|
||||
extern int sem_delete_owned_sems(team_id owner);
|
||||
extern int32 sem_used_sems(void);
|
||||
extern int32 sem_max_sems(void);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <OS.h>
|
||||
#include <DiskDeviceDefs.h>
|
||||
|
||||
#include <semaphore.h>
|
||||
#include <signal.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
@@ -75,6 +76,18 @@ extern status_t _kern_get_next_sem_info(team_id team, int32 *cookie,
|
||||
struct sem_info *info, size_t size);
|
||||
extern status_t _kern_set_sem_owner(sem_id id, team_id proc);
|
||||
|
||||
/* POSIX realtime sem syscalls */
|
||||
extern status_t _kern_realtime_sem_open(const char* name, int openFlags,
|
||||
mode_t mode, uint32 semCount, sem_t* userSem,
|
||||
sem_t** _usedUserSem);
|
||||
extern status_t _kern_realtime_sem_close(sem_id semID,
|
||||
sem_t** _deleteUserSem);
|
||||
extern status_t _kern_realtime_sem_unlink(const char* name);
|
||||
|
||||
extern status_t _kern_realtime_sem_get_value(sem_id semID, int* value);
|
||||
extern status_t _kern_realtime_sem_post(sem_id semID);
|
||||
extern status_t _kern_realtime_sem_wait(sem_id semID, bigtime_t timeout);
|
||||
|
||||
/* team & thread syscalls */
|
||||
|
||||
extern thread_id _kern_load_image(int32 argCount, const char **args,
|
||||
|
||||
@@ -68,8 +68,8 @@ enum {
|
||||
THREAD_BLOCK_TYPE_USER_BASE = 10000
|
||||
};
|
||||
|
||||
struct image;
|
||||
// defined in image.c
|
||||
struct image; // defined in image.c
|
||||
struct realtime_sem_context; // defined in realtime_sem.cpp
|
||||
struct select_info;
|
||||
|
||||
struct death_entry {
|
||||
@@ -181,6 +181,7 @@ struct team {
|
||||
int state; // current team state, see above
|
||||
int32 flags;
|
||||
void *io_context;
|
||||
struct realtime_sem_context *realtime_sem_context;
|
||||
sem_id death_sem; // semaphore to wait on for dying threads
|
||||
struct list dead_threads;
|
||||
int dead_threads_count;
|
||||
|
||||
@@ -33,6 +33,7 @@ KernelMergeObject kernel_core.o :
|
||||
Notifications.cpp
|
||||
port.cpp
|
||||
real_time_clock.c
|
||||
realtime_sem.cpp
|
||||
scheduler.cpp
|
||||
sem.cpp
|
||||
shutdown.c
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include <Notifications.h>
|
||||
#include <port.h>
|
||||
#include <real_time_clock.h>
|
||||
#include <realtime_sem.h>
|
||||
#include <sem.h>
|
||||
#include <smp.h>
|
||||
#include <system_info.h>
|
||||
@@ -137,7 +138,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
|
||||
rtc_init(&sKernelArgs);
|
||||
|
||||
TRACE("init semaphores\n");
|
||||
sem_init(&sKernelArgs);
|
||||
haiku_sem_init(&sKernelArgs);
|
||||
condition_variable_init();
|
||||
|
||||
// now we can create and use semaphores
|
||||
@@ -158,6 +159,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
|
||||
TRACE("init kernel daemons\n");
|
||||
kernel_daemon_init();
|
||||
arch_platform_init_post_thread(&sKernelArgs);
|
||||
realtime_sem_init();
|
||||
|
||||
TRACE("init VM threads\n");
|
||||
vm_init_post_thread(&sKernelArgs);
|
||||
|
||||
@@ -0,0 +1,758 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <realtime_sem.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <fs/KPath.h>
|
||||
#include <kernel.h>
|
||||
#include <lock.h>
|
||||
#include <syscall_restart.h>
|
||||
#include <team.h>
|
||||
#include <thread.h>
|
||||
#include <util/atomic.h>
|
||||
#include <util/AutoLock.h>
|
||||
#include <util/khash.h>
|
||||
#include <util/OpenHashTable.h>
|
||||
|
||||
|
||||
class SemInfo {
|
||||
public:
|
||||
SemInfo()
|
||||
:
|
||||
fName(NULL),
|
||||
fRefCount(1),
|
||||
fID(-1)
|
||||
{
|
||||
}
|
||||
|
||||
~SemInfo()
|
||||
{
|
||||
free(fName);
|
||||
if (fID >= 0)
|
||||
delete_sem(fID);
|
||||
}
|
||||
|
||||
const char* Name() const { return fName; }
|
||||
sem_id ID() const { return fID; }
|
||||
|
||||
status_t Init(const char* name, mode_t mode, int32 semCount)
|
||||
{
|
||||
fName = strdup(name);
|
||||
if (fName == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
fID = create_sem(semCount, name);
|
||||
if (fID < 0)
|
||||
return fID;
|
||||
|
||||
fUID = geteuid();
|
||||
fGID = getegid();
|
||||
fPermissions = mode;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
void AcquireReference()
|
||||
{
|
||||
atomic_add(&fRefCount, 1);
|
||||
}
|
||||
|
||||
void ReleaseReference()
|
||||
{
|
||||
if (atomic_add(&fRefCount, -1) == 1)
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool HasPermissions() const
|
||||
{
|
||||
if ((fPermissions & S_IWOTH) != 0)
|
||||
return true;
|
||||
|
||||
uid_t uid = geteuid();
|
||||
if (uid == 0 || (uid == fUID && (fPermissions & S_IWUSR) != 0))
|
||||
return true;
|
||||
|
||||
gid_t gid = getegid();
|
||||
if (gid == fGID && (fPermissions & S_IWGRP) != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HashTableLink<SemInfo>* HashTableLink()
|
||||
{
|
||||
return &fHashLink;
|
||||
}
|
||||
|
||||
private:
|
||||
char* fName;
|
||||
vint32 fRefCount;
|
||||
sem_id fID;
|
||||
uid_t fUID;
|
||||
gid_t fGID;
|
||||
mode_t fPermissions;
|
||||
|
||||
::HashTableLink<SemInfo> fHashLink;
|
||||
};
|
||||
|
||||
|
||||
struct NamedSemHashDefinition {
|
||||
typedef const char* KeyType;
|
||||
typedef SemInfo ValueType;
|
||||
|
||||
size_t HashKey(const KeyType& key) const
|
||||
{
|
||||
return hash_hash_string(key);
|
||||
}
|
||||
|
||||
size_t Hash(SemInfo* semaphore) const
|
||||
{
|
||||
return HashKey(semaphore->Name());
|
||||
}
|
||||
|
||||
bool Compare(const KeyType& key, SemInfo* semaphore) const
|
||||
{
|
||||
return strcmp(key, semaphore->Name()) == 0;
|
||||
}
|
||||
|
||||
HashTableLink<SemInfo>* GetLink(SemInfo* semaphore) const
|
||||
{
|
||||
return semaphore->HashTableLink();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class GlobalRealtimeSemTable {
|
||||
public:
|
||||
GlobalRealtimeSemTable()
|
||||
{
|
||||
mutex_init(&fLock, "global realtime sem table");
|
||||
}
|
||||
|
||||
~GlobalRealtimeSemTable()
|
||||
{
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
status_t Init()
|
||||
{
|
||||
return fSemaphores.InitCheck();
|
||||
}
|
||||
|
||||
status_t OpenSem(const char* name, int openFlags, mode_t mode,
|
||||
uint32 semCount, SemInfo*& _sem, bool& _created)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
|
||||
SemInfo* sem = fSemaphores.Lookup(name);
|
||||
if (sem != NULL) {
|
||||
if ((openFlags & O_EXCL) != 0)
|
||||
return EEXIST;
|
||||
|
||||
if (!sem->HasPermissions())
|
||||
return EACCES;
|
||||
|
||||
sem->AcquireReference();
|
||||
_sem = sem;
|
||||
_created = false;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if ((openFlags & O_CREAT) == 0)
|
||||
return ENOENT;
|
||||
|
||||
// does not exist yet -- create
|
||||
sem = new(std::nothrow) SemInfo;
|
||||
if (sem == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t error = sem->Init(name, mode, semCount);
|
||||
if (error != B_OK) {
|
||||
delete sem;
|
||||
return error;
|
||||
}
|
||||
|
||||
error = fSemaphores.Insert(sem);
|
||||
if (error != B_OK) {
|
||||
delete sem;
|
||||
return error;
|
||||
}
|
||||
|
||||
// add one reference for the table
|
||||
sem->AcquireReference();
|
||||
|
||||
_sem = sem;
|
||||
_created = true;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t UnlinkSem(const char* name)
|
||||
{
|
||||
MutexLocker _(fLock);
|
||||
|
||||
SemInfo* sem = fSemaphores.Lookup(name);
|
||||
if (sem == NULL)
|
||||
return ENOENT;
|
||||
|
||||
if (!sem->HasPermissions())
|
||||
return EACCES;
|
||||
|
||||
fSemaphores.Remove(sem);
|
||||
sem->ReleaseReference();
|
||||
// release the table reference
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef OpenHashTable<NamedSemHashDefinition, true> SemTable;
|
||||
|
||||
mutex fLock;
|
||||
SemTable fSemaphores;
|
||||
};
|
||||
|
||||
|
||||
static GlobalRealtimeSemTable sSemTable;
|
||||
|
||||
|
||||
class TeamSemInfo {
|
||||
public:
|
||||
TeamSemInfo(SemInfo* semaphore, sem_t* userSem)
|
||||
:
|
||||
fSemaphore(semaphore),
|
||||
fUserSemaphore(userSem),
|
||||
fOpenCount(1)
|
||||
{
|
||||
}
|
||||
|
||||
~TeamSemInfo()
|
||||
{
|
||||
if (fSemaphore != NULL)
|
||||
fSemaphore->ReleaseReference();
|
||||
}
|
||||
|
||||
SemInfo* Semaphore() const { return fSemaphore; }
|
||||
sem_t* UserSemaphore() const { return fUserSemaphore; }
|
||||
|
||||
void Open()
|
||||
{
|
||||
fOpenCount++;
|
||||
}
|
||||
|
||||
bool Close()
|
||||
{
|
||||
return --fOpenCount == 0;
|
||||
}
|
||||
|
||||
TeamSemInfo* Clone() const
|
||||
{
|
||||
TeamSemInfo* sem = new(std::nothrow) TeamSemInfo(fSemaphore,
|
||||
fUserSemaphore);
|
||||
if (sem == NULL)
|
||||
return NULL;
|
||||
|
||||
sem->fOpenCount = fOpenCount;
|
||||
fSemaphore->AcquireReference();
|
||||
|
||||
return sem;
|
||||
}
|
||||
|
||||
HashTableLink<TeamSemInfo>* HashTableLink()
|
||||
{
|
||||
return &fHashLink;
|
||||
}
|
||||
|
||||
private:
|
||||
SemInfo* fSemaphore;
|
||||
sem_t* fUserSemaphore;
|
||||
int32 fOpenCount;
|
||||
|
||||
::HashTableLink<TeamSemInfo> fHashLink;
|
||||
};
|
||||
|
||||
|
||||
struct TeamSemHashDefinition {
|
||||
typedef sem_id KeyType;
|
||||
typedef TeamSemInfo ValueType;
|
||||
|
||||
size_t HashKey(const KeyType& key) const
|
||||
{
|
||||
return (size_t)key;
|
||||
}
|
||||
|
||||
size_t Hash(TeamSemInfo* semaphore) const
|
||||
{
|
||||
return HashKey(semaphore->Semaphore()->ID());
|
||||
}
|
||||
|
||||
bool Compare(const KeyType& key, TeamSemInfo* semaphore) const
|
||||
{
|
||||
return key == semaphore->Semaphore()->ID();
|
||||
}
|
||||
|
||||
HashTableLink<TeamSemInfo>* GetLink(TeamSemInfo* semaphore) const
|
||||
{
|
||||
return semaphore->HashTableLink();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct realtime_sem_context {
|
||||
realtime_sem_context()
|
||||
{
|
||||
mutex_init(&fLock, "realtime sem context");
|
||||
}
|
||||
|
||||
~realtime_sem_context()
|
||||
{
|
||||
mutex_lock(&fLock);
|
||||
|
||||
// delete all semaphores.
|
||||
SemTable::Iterator it = fSemaphores.GetIterator();
|
||||
while (TeamSemInfo* sem = it.Next()) {
|
||||
// Note, this uses internal knowledge about how the iterator works.
|
||||
// Ugly, but there's no good alternative.
|
||||
fSemaphores.RemoveUnchecked(sem);
|
||||
delete sem;
|
||||
}
|
||||
|
||||
mutex_destroy(&fLock);
|
||||
}
|
||||
|
||||
status_t Init()
|
||||
{
|
||||
return fSemaphores.InitCheck();
|
||||
}
|
||||
|
||||
realtime_sem_context* Clone()
|
||||
{
|
||||
// create new context
|
||||
realtime_sem_context* context = new(std::nothrow) realtime_sem_context;
|
||||
if (context == NULL)
|
||||
return NULL;
|
||||
ObjectDeleter<realtime_sem_context> contextDeleter(context);
|
||||
|
||||
MutexLocker _(fLock);
|
||||
|
||||
// clone all semaphores
|
||||
SemTable::Iterator it = fSemaphores.GetIterator();
|
||||
while (TeamSemInfo* sem = it.Next()) {
|
||||
TeamSemInfo* clonedSem = sem->Clone();
|
||||
if (clonedSem == NULL)
|
||||
return NULL;
|
||||
|
||||
if (context->fSemaphores.Insert(clonedSem) != B_OK) {
|
||||
delete clonedSem;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
contextDeleter.Detach();
|
||||
return context;
|
||||
}
|
||||
|
||||
status_t CreateAnonymousSem(uint32 semCount, int& _id)
|
||||
{
|
||||
SemInfo* sem = new(std::nothrow) SemInfo;
|
||||
if (sem == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ObjectDeleter<SemInfo> semDeleter(sem);
|
||||
|
||||
status_t error = sem->Init(NULL, 0, semCount);
|
||||
if (error != B_OK) {
|
||||
delete sem;
|
||||
return error;
|
||||
}
|
||||
|
||||
TeamSemInfo* teamSem = new(std::nothrow) TeamSemInfo(sem, NULL);
|
||||
if (teamSem == NULL)
|
||||
return B_NO_MEMORY;
|
||||
semDeleter.Detach();
|
||||
|
||||
MutexLocker _(fLock);
|
||||
|
||||
error = fSemaphores.Insert(teamSem);
|
||||
if (error != B_OK) {
|
||||
delete teamSem;
|
||||
return error;
|
||||
}
|
||||
|
||||
_id = teamSem->Semaphore()->ID();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t OpenSem(const char* name, int openFlags, mode_t mode,
|
||||
uint32 semCount, sem_t* userSem, sem_t*& _usedUserSem, int& _id,
|
||||
bool& _created)
|
||||
{
|
||||
SemInfo* sem;
|
||||
status_t error = sSemTable.OpenSem(name, openFlags, mode, semCount,
|
||||
sem, _created);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
MutexLocker _(fLock);
|
||||
|
||||
TeamSemInfo* teamSem = fSemaphores.Lookup(sem->ID());
|
||||
if (teamSem != NULL) {
|
||||
// already open -- just increment the open count
|
||||
teamSem->Open();
|
||||
sem->ReleaseReference();
|
||||
_usedUserSem = teamSem->UserSemaphore();
|
||||
_id = teamSem->Semaphore()->ID();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// not open yet -- create a new team sem
|
||||
teamSem = new(std::nothrow) TeamSemInfo(sem, NULL);
|
||||
if (teamSem == NULL) {
|
||||
sem->ReleaseReference();
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
error = fSemaphores.Insert(teamSem);
|
||||
if (error != B_OK) {
|
||||
delete teamSem;
|
||||
return error;
|
||||
}
|
||||
|
||||
_usedUserSem = teamSem->UserSemaphore();
|
||||
_id = teamSem->Semaphore()->ID();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t CloseSem(sem_id id, sem_t*& deleteUserSem)
|
||||
{
|
||||
deleteUserSem = NULL;
|
||||
|
||||
MutexLocker _(fLock);
|
||||
|
||||
TeamSemInfo* sem = fSemaphores.Lookup(id);
|
||||
if (sem == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (sem->Close()) {
|
||||
// last reference closed
|
||||
fSemaphores.Remove(sem);
|
||||
deleteUserSem = sem->UserSemaphore();
|
||||
delete sem;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t AcquireSem(sem_id id, bigtime_t timeout)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
if (fSemaphores.Lookup(id) == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
status_t error;
|
||||
if (timeout == 0) {
|
||||
error = acquire_sem_etc(id, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,
|
||||
0);
|
||||
} else if (timeout == B_INFINITE_TIMEOUT) {
|
||||
error = acquire_sem_etc(id, 1, B_CAN_INTERRUPT, 0);
|
||||
} else {
|
||||
error = acquire_sem_etc(id, 1, B_CAN_INTERRUPT | B_ABSOLUTE_TIMEOUT,
|
||||
timeout);
|
||||
}
|
||||
|
||||
return error == B_BAD_SEM_ID ? B_BAD_VALUE : error;
|
||||
}
|
||||
|
||||
status_t ReleaseSem(sem_id id)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
if (fSemaphores.Lookup(id) == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
status_t error = release_sem(id);
|
||||
return error == B_BAD_SEM_ID ? B_BAD_VALUE : error;
|
||||
}
|
||||
|
||||
status_t GetSemCount(sem_id id, int& _count)
|
||||
{
|
||||
MutexLocker locker(fLock);
|
||||
|
||||
if (fSemaphores.Lookup(id) == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
int32 count;
|
||||
status_t error = get_sem_count(id, &count);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
_count = count;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef OpenHashTable<TeamSemHashDefinition, true> SemTable;
|
||||
|
||||
mutex fLock;
|
||||
SemTable fSemaphores;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - implementation private
|
||||
|
||||
|
||||
static realtime_sem_context*
|
||||
get_current_team_context()
|
||||
{
|
||||
struct team* team = thread_get_current_thread()->team;
|
||||
|
||||
// get context
|
||||
realtime_sem_context* context = atomic_pointer_get(
|
||||
&team->realtime_sem_context);
|
||||
if (context != NULL)
|
||||
return context;
|
||||
|
||||
// no context yet -- create a new one
|
||||
context = new(std::nothrow) realtime_sem_context;
|
||||
if (context == NULL || context->Init() != B_OK) {
|
||||
delete context;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// set the allocated context
|
||||
realtime_sem_context* oldContext = atomic_pointer_test_and_set(
|
||||
&team->realtime_sem_context, context, (realtime_sem_context*)NULL);
|
||||
if (oldContext == NULL)
|
||||
return context;
|
||||
|
||||
// someone else was quicker
|
||||
delete context;
|
||||
return oldContext;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
copy_sem_name_to_kernel(const char* userName, KPath& buffer, char*& name)
|
||||
{
|
||||
if (userName == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (!IS_USER_ADDRESS(userName))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
if (buffer.InitCheck() != B_OK)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// copy userland path to kernel
|
||||
name = buffer.LockBuffer();
|
||||
ssize_t actualLength = user_strlcpy(name, userName, buffer.BufferSize());
|
||||
|
||||
if (actualLength < 0)
|
||||
return B_BAD_ADDRESS;
|
||||
if ((size_t)actualLength >= buffer.BufferSize())
|
||||
return ENAMETOOLONG;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - kernel internal
|
||||
|
||||
|
||||
void
|
||||
realtime_sem_init()
|
||||
{
|
||||
new(&sSemTable) GlobalRealtimeSemTable;
|
||||
if (sSemTable.Init() != B_OK)
|
||||
panic("realtime_sem_init() failed to init global table");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
delete_realtime_sem_context(realtime_sem_context* context)
|
||||
{
|
||||
delete context;
|
||||
}
|
||||
|
||||
|
||||
realtime_sem_context*
|
||||
clone_realtime_sem_context(realtime_sem_context* context)
|
||||
{
|
||||
if (context == NULL)
|
||||
return NULL;
|
||||
|
||||
return context->Clone();
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - syscalls
|
||||
|
||||
|
||||
status_t
|
||||
_user_realtime_sem_open(const char* userName, int openFlags, mode_t mode,
|
||||
uint32 semCount, sem_t* userSem, sem_t** _usedUserSem)
|
||||
{
|
||||
realtime_sem_context* context = get_current_team_context();
|
||||
if (context == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// userSem must always be given
|
||||
if (userSem == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (!IS_USER_ADDRESS(userSem))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
// anonymous semaphores are less work -- deal with them first
|
||||
if (userName == NULL) {
|
||||
int id;
|
||||
status_t error = context->CreateAnonymousSem(semCount, id);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
if (user_memcpy(&userSem->id, &id, sizeof(int)) != B_OK) {
|
||||
sem_t* dummy;
|
||||
context->CloseSem(id, dummy);
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
// check user pointers
|
||||
if (_usedUserSem == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (!IS_USER_ADDRESS(_usedUserSem) || !IS_USER_ADDRESS(userName))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
// copy name to kernel
|
||||
KPath nameBuffer(B_PATH_NAME_LENGTH);
|
||||
char* name;
|
||||
status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// open the semaphore
|
||||
sem_t* usedUserSem;
|
||||
bool created;
|
||||
int id;
|
||||
error = context->OpenSem(name, openFlags, mode, semCount, userSem,
|
||||
usedUserSem, id, created);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// copy results back to userland
|
||||
if (user_memcpy(&userSem->id, &id, sizeof(int)) != B_OK
|
||||
|| user_memcpy(_usedUserSem, &usedUserSem, sizeof(sem_t*)) != B_OK) {
|
||||
if (created)
|
||||
sSemTable.UnlinkSem(name);
|
||||
sem_t* dummy;
|
||||
context->CloseSem(id, dummy);
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_realtime_sem_close(sem_id semID, sem_t** _deleteUserSem)
|
||||
{
|
||||
if (_deleteUserSem != NULL && !IS_USER_ADDRESS(_deleteUserSem))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
realtime_sem_context* context = get_current_team_context();
|
||||
if (context == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// close sem
|
||||
sem_t* deleteUserSem;
|
||||
status_t error = context->CloseSem(semID, deleteUserSem);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// copy back result to userland
|
||||
if (_deleteUserSem != NULL
|
||||
&& user_memcpy(_deleteUserSem, &deleteUserSem, sizeof(sem_t*))
|
||||
!= B_OK) {
|
||||
return B_BAD_ADDRESS;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_realtime_sem_unlink(const char* userName)
|
||||
{
|
||||
// copy name to kernel
|
||||
KPath nameBuffer(B_PATH_NAME_LENGTH);
|
||||
char* name;
|
||||
status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return sSemTable.UnlinkSem(name);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_realtime_sem_get_value(sem_id semID, int* _value)
|
||||
{
|
||||
if (_value == NULL)
|
||||
return B_BAD_VALUE;
|
||||
if (!IS_USER_ADDRESS(_value))
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
realtime_sem_context* context = get_current_team_context();
|
||||
if (context == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// get sem count
|
||||
int count;
|
||||
status_t error = context->GetSemCount(semID, count);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// copy back result to userland
|
||||
if (user_memcpy(_value, &count, sizeof(int)) != B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_realtime_sem_post(sem_id semID)
|
||||
{
|
||||
realtime_sem_context* context = get_current_team_context();
|
||||
if (context == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
return context->ReleaseSem(semID);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_realtime_sem_wait(sem_id semID, bigtime_t timeout)
|
||||
{
|
||||
realtime_sem_context* context = get_current_team_context();
|
||||
if (context == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
return syscall_restart_handle_post(context->AcquireSem(semID, timeout));
|
||||
}
|
||||
@@ -301,7 +301,7 @@ fill_sem_info(struct sem_entry *sem, sem_info *info, size_t size)
|
||||
|
||||
|
||||
status_t
|
||||
sem_init(kernel_args *args)
|
||||
haiku_sem_init(kernel_args *args)
|
||||
{
|
||||
area_id area;
|
||||
int32 i;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include <vfs.h>
|
||||
#include <vm.h>
|
||||
#include <thread.h>
|
||||
#include <realtime_sem.h>
|
||||
#include <sem.h>
|
||||
#include <port.h>
|
||||
#include <cpu.h>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <kscheduler.h>
|
||||
#include <ksignal.h>
|
||||
#include <port.h>
|
||||
#include <realtime_sem.h>
|
||||
#include <sem.h>
|
||||
#include <syscall_process_info.h>
|
||||
#include <syscall_restart.h>
|
||||
@@ -822,6 +823,7 @@ create_team_struct(const char *name, bool kernel)
|
||||
team->num_threads = 0;
|
||||
team->io_context = NULL;
|
||||
team->address_space = NULL;
|
||||
team->realtime_sem_context = NULL;
|
||||
team->thread_list = NULL;
|
||||
team->main_thread = NULL;
|
||||
team->loading_info = NULL;
|
||||
@@ -1365,6 +1367,8 @@ exec_team(const char *path, int32 argCount, char * const *args,
|
||||
sem_delete_owned_sems(team->id);
|
||||
remove_images(team);
|
||||
vfs_exec_io_context(team->io_context);
|
||||
delete_realtime_sem_context(team->realtime_sem_context);
|
||||
team->realtime_sem_context = NULL;
|
||||
|
||||
user_debug_finish_after_exec();
|
||||
|
||||
@@ -1485,6 +1489,16 @@ fork_team(void)
|
||||
goto err2;
|
||||
}
|
||||
|
||||
// duplicate the realtime sem context
|
||||
if (parentTeam->realtime_sem_context) {
|
||||
team->realtime_sem_context = clone_realtime_sem_context(
|
||||
parentTeam->realtime_sem_context);
|
||||
if (team->realtime_sem_context == NULL) {
|
||||
status = B_NO_MEMORY;
|
||||
goto err25;
|
||||
}
|
||||
}
|
||||
|
||||
// create an address space for this team
|
||||
status = vm_create_address_space(team->id, USER_BASE, USER_SIZE, false,
|
||||
&team->address_space);
|
||||
@@ -1540,6 +1554,8 @@ fork_team(void)
|
||||
err4:
|
||||
vm_delete_address_space(team->address_space);
|
||||
err3:
|
||||
delete_realtime_sem_context(team->realtime_sem_context);
|
||||
err25:
|
||||
vfs_free_io_context(team->io_context);
|
||||
err2:
|
||||
free(forkArgs);
|
||||
@@ -2327,6 +2343,7 @@ team_delete_team(struct team *team)
|
||||
// free team resources
|
||||
|
||||
vfs_free_io_context(team->io_context);
|
||||
delete_realtime_sem_context(team->realtime_sem_context);
|
||||
delete_owned_ports(teamID);
|
||||
sem_delete_owned_sems(teamID);
|
||||
remove_images(team);
|
||||
|
||||
@@ -18,6 +18,7 @@ MergeObject posix_main.o :
|
||||
inttypes.c
|
||||
poll.c
|
||||
$(PWD_BACKEND)
|
||||
semaphore.cpp
|
||||
syslog.cpp
|
||||
termios.c
|
||||
utime.c
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <semaphore.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <realtime_sem.h>
|
||||
#include <syscall_utils.h>
|
||||
#include <syscalls.h>
|
||||
|
||||
|
||||
sem_t*
|
||||
sem_open(const char* name, int openFlags,...)
|
||||
{
|
||||
if (name == NULL) {
|
||||
errno = B_BAD_VALUE;
|
||||
return SEM_FAILED;
|
||||
}
|
||||
|
||||
// get the mode and semaphore count parameters, if O_CREAT is specified
|
||||
mode_t mode = 0;
|
||||
unsigned semCount = 0;
|
||||
|
||||
if ((openFlags & O_CREAT) != 0) {
|
||||
va_list args;
|
||||
va_start(args, openFlags);
|
||||
mode = va_arg(args, mode_t);
|
||||
semCount = va_arg(args, unsigned);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
// Allocate a sem_t structure -- we don't know, whether this is the first
|
||||
// call of this process to open the semaphore. If it is, we will keep the
|
||||
// structure, otherwise we will delete it later.
|
||||
sem_t* sem = (sem_t*)malloc(sizeof(sem_t));
|
||||
if (sem == NULL) {
|
||||
errno = B_NO_MEMORY;
|
||||
return SEM_FAILED;
|
||||
}
|
||||
MemoryDeleter semDeleter(sem);
|
||||
|
||||
// ask the kernel to open the semaphore
|
||||
sem_t* usedSem;
|
||||
status_t error = _kern_realtime_sem_open(name, openFlags, mode, semCount,
|
||||
sem, &usedSem);
|
||||
if (error != B_OK) {
|
||||
errno = error;
|
||||
return SEM_FAILED;
|
||||
}
|
||||
|
||||
if (usedSem == sem)
|
||||
semDeleter.Detach();
|
||||
|
||||
return usedSem;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_close(sem_t* semaphore)
|
||||
{
|
||||
sem_t* deleteSem = NULL;
|
||||
status_t error = _kern_realtime_sem_close(semaphore->id, &deleteSem);
|
||||
if (error == B_OK)
|
||||
free(deleteSem);
|
||||
|
||||
RETURN_AND_SET_ERRNO(error);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_unlink(const char* name)
|
||||
{
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_unlink(name));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_init(sem_t* semaphore, int shared, unsigned value)
|
||||
{
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_open(NULL, 0, 0, value, semaphore,
|
||||
NULL));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_destroy(sem_t* semaphore)
|
||||
{
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_close(semaphore->id, NULL));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_post(sem_t* semaphore)
|
||||
{
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_post(semaphore->id));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_timedwait(sem_t* semaphore, const struct timespec* timeout)
|
||||
{
|
||||
bigtime_t timeoutMicros = ((bigtime_t)timeout->tv_sec) * 1000000
|
||||
+ timeout->tv_nsec / 1000;
|
||||
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_wait(semaphore->id, timeoutMicros));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_trywait(sem_t* semaphore)
|
||||
{
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_wait(semaphore->id, 0));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_wait(sem_t* semaphore)
|
||||
{
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_wait(semaphore->id,
|
||||
B_INFINITE_TIMEOUT));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
sem_getvalue(sem_t* semaphore, int* value)
|
||||
{
|
||||
RETURN_AND_SET_ERRNO(_kern_realtime_sem_get_value(semaphore->id, value));
|
||||
}
|
||||
Reference in New Issue
Block a user