* Added some padding to the sem_t structure.

* Changed the semantics of unnamed semaphores. Before parent and child
  of a fork() would always share an earlier created semaphore. Now we do
  that only, if the "shared" parameter of sem_init() was true. That's
  still not quite the behavior Linux and Solaris have, but should be
  perfectly fine with how reasonable code would use the API.
* There's a global table for shared unnamed semaphores now. ATM a
  semaphore is leaked when no one explicitly destroys it (just as with
  named sems).
* Enforce per-team and global semaphore number limits.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25362 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold 2008-05-08 01:39:49 +00:00
parent 01b1098795
commit 3dfe682f55
4 changed files with 382 additions and 88 deletions

View File

@ -5,12 +5,14 @@
#ifndef _SEMAPHORE_H_
#define _SEMAPHORE_H_
#include <stdint.h>
#include <sys/cdefs.h>
#include <time.h>
typedef struct {
int id;
int32_t id;
int32_t _padding[3];
} sem_t;
#define SEM_FAILED ((sem_t*)(long)-1)

View File

@ -77,9 +77,9 @@ extern status_t _kern_get_next_sem_info(team_id team, int32 *cookie,
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_open(const char* name,
int openFlagsOrShared, 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);

View File

@ -29,33 +29,61 @@ class SemInfo {
public:
SemInfo()
:
fName(NULL),
fRefCount(1),
fID(-1)
fSemaphoreID(-1)
{
}
~SemInfo()
virtual ~SemInfo()
{
if (fSemaphoreID >= 0)
delete_sem(fSemaphoreID);
}
sem_id SemaphoreID() const { return fSemaphoreID; }
status_t Init(int32 semCount, const char* name)
{
fSemaphoreID = create_sem(semCount, name);
if (fSemaphoreID < 0)
return fSemaphoreID;
return B_OK;
}
virtual sem_id ID() const = 0;
virtual SemInfo* Clone() = 0;
virtual void Delete() = 0;
private:
sem_id fSemaphoreID;
};
class NamedSem : public SemInfo {
public:
NamedSem()
:
fName(NULL),
fRefCount(1)
{
}
virtual ~NamedSem()
{
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)
{
if (name != NULL) {
fName = strdup(name);
if (fName == NULL)
return B_NO_MEMORY;
}
status_t error = SemInfo::Init(semCount, name);
if (error != B_OK)
return error;
fID = create_sem(semCount, name);
if (fID < 0)
return fID;
fName = strdup(name);
if (fName == NULL)
return B_NO_MEMORY;
fUID = geteuid();
fGID = getegid();
@ -91,7 +119,23 @@ public:
return false;
}
HashTableLink<SemInfo>* HashTableLink()
virtual sem_id ID() const
{
return SemaphoreID();
}
virtual SemInfo* Clone()
{
AcquireReference();
return this;
}
virtual void Delete()
{
ReleaseReference();
}
HashTableLink<NamedSem>* HashTableLink()
{
return &fHashLink;
}
@ -99,64 +143,192 @@ public:
private:
char* fName;
vint32 fRefCount;
sem_id fID;
uid_t fUID;
gid_t fGID;
mode_t fPermissions;
::HashTableLink<SemInfo> fHashLink;
::HashTableLink<NamedSem> fHashLink;
};
class UnnamedSem : public SemInfo {
public:
UnnamedSem()
:
fID(0)
{
}
virtual ~UnnamedSem()
{
}
status_t Init(int32 semCount, const char* name)
{
return SemInfo::Init(semCount, name);
}
void SetID(sem_id id)
{
fID = id;
}
virtual sem_id ID() const
{
return fID;
}
virtual SemInfo* Clone()
{
sem_info info;
if (get_sem_info(SemaphoreID(), &info) != B_OK)
return NULL;
UnnamedSem* clone = new(std::nothrow) UnnamedSem;
if (clone == NULL)
return NULL;
if (clone->Init(info.count, info.name) != B_OK) {
delete clone;
return NULL;
}
clone->SetID(fID);
return clone;
}
virtual void Delete()
{
delete this;
}
private:
sem_id fID;
};
class UnnamedSharedSem : public SemInfo {
public:
UnnamedSharedSem()
{
}
virtual ~UnnamedSharedSem()
{
}
status_t Init(int32 semCount, const char* name)
{
return SemInfo::Init(semCount, name);
}
virtual sem_id ID() const
{
return SemaphoreID();
}
virtual SemInfo* Clone()
{
// Can't be cloned.
return NULL;
}
virtual void Delete()
{
delete this;
}
HashTableLink<UnnamedSharedSem>* HashTableLink()
{
return &fHashLink;
}
private:
::HashTableLink<UnnamedSharedSem> fHashLink;
};
struct NamedSemHashDefinition {
typedef const char* KeyType;
typedef SemInfo ValueType;
typedef NamedSem ValueType;
size_t HashKey(const KeyType& key) const
{
return hash_hash_string(key);
}
size_t Hash(SemInfo* semaphore) const
size_t Hash(NamedSem* semaphore) const
{
return HashKey(semaphore->Name());
}
bool Compare(const KeyType& key, SemInfo* semaphore) const
bool Compare(const KeyType& key, NamedSem* semaphore) const
{
return strcmp(key, semaphore->Name()) == 0;
}
HashTableLink<SemInfo>* GetLink(SemInfo* semaphore) const
HashTableLink<NamedSem>* GetLink(NamedSem* semaphore) const
{
return semaphore->HashTableLink();
}
};
class GlobalRealtimeSemTable {
public:
GlobalRealtimeSemTable()
struct UnnamedSemHashDefinition {
typedef sem_id KeyType;
typedef UnnamedSharedSem ValueType;
size_t HashKey(const KeyType& key) const
{
mutex_init(&fLock, "global realtime sem table");
return (size_t)key;
}
~GlobalRealtimeSemTable()
size_t Hash(UnnamedSharedSem* semaphore) const
{
return HashKey(semaphore->SemaphoreID());
}
bool Compare(const KeyType& key, UnnamedSharedSem* semaphore) const
{
return key == semaphore->SemaphoreID();
}
HashTableLink<UnnamedSharedSem>* GetLink(UnnamedSharedSem* semaphore) const
{
return semaphore->HashTableLink();
}
};
class GlobalSemTable {
public:
GlobalSemTable()
:
fSemaphoreCount(0)
{
mutex_init(&fLock, "global named sem table");
}
~GlobalSemTable()
{
mutex_destroy(&fLock);
}
status_t Init()
{
return fSemaphores.InitCheck();
status_t error = fNamedSemaphores.InitCheck();
if (error != B_OK)
return error;
return fUnnamedSemaphores.InitCheck();
}
status_t OpenSem(const char* name, int openFlags, mode_t mode,
uint32 semCount, SemInfo*& _sem, bool& _created)
status_t OpenNamedSem(const char* name, int openFlags, mode_t mode,
uint32 semCount, NamedSem*& _sem, bool& _created)
{
MutexLocker _(fLock);
SemInfo* sem = fSemaphores.Lookup(name);
NamedSem* sem = fNamedSemaphores.Lookup(name);
if (sem != NULL) {
if ((openFlags & O_EXCL) != 0)
return EEXIST;
@ -174,8 +346,10 @@ public:
return ENOENT;
// does not exist yet -- create
// TODO: Enforce per team semaphore limit!
sem = new(std::nothrow) SemInfo;
if (fSemaphoreCount >= MAX_POSIX_SEMS)
return ENOSPC;
sem = new(std::nothrow) NamedSem;
if (sem == NULL)
return B_NO_MEMORY;
@ -185,7 +359,7 @@ public:
return error;
}
error = fSemaphores.Insert(sem);
error = fNamedSemaphores.Insert(sem);
if (error != B_OK) {
delete sem;
return error;
@ -194,38 +368,92 @@ public:
// add one reference for the table
sem->AcquireReference();
fSemaphoreCount++;
_sem = sem;
_created = true;
return B_OK;
}
status_t UnlinkSem(const char* name)
status_t UnlinkNamedSem(const char* name)
{
MutexLocker _(fLock);
SemInfo* sem = fSemaphores.Lookup(name);
NamedSem* sem = fNamedSemaphores.Lookup(name);
if (sem == NULL)
return ENOENT;
if (!sem->HasPermissions())
return EACCES;
fSemaphores.Remove(sem);
fNamedSemaphores.Remove(sem);
sem->ReleaseReference();
// release the table reference
fSemaphoreCount--;
return B_OK;
}
private:
typedef OpenHashTable<NamedSemHashDefinition, true> SemTable;
status_t CreateUnnamedSem(uint32 semCount, int32_t& _id)
{
MutexLocker _(fLock);
mutex fLock;
SemTable fSemaphores;
if (fSemaphoreCount >= MAX_POSIX_SEMS)
return ENOSPC;
UnnamedSharedSem* sem = new(std::nothrow) UnnamedSharedSem;
if (sem == NULL)
return B_NO_MEMORY;
status_t error = sem->Init(semCount, "unnamed shared sem");
if (error == B_OK)
error = fUnnamedSemaphores.Insert(sem);
if (error != B_OK) {
delete sem;
return error;
}
fSemaphoreCount++;
_id = sem->SemaphoreID();
return B_OK;
}
status_t DeleteUnnamedSem(sem_id id)
{
MutexLocker _(fLock);
UnnamedSharedSem* sem = fUnnamedSemaphores.Lookup(id);
if (sem == NULL)
return B_BAD_VALUE;
fUnnamedSemaphores.Remove(sem);
delete sem;
fSemaphoreCount--;
return B_OK;
}
bool IsUnnamedValidSem(sem_id id)
{
MutexLocker _(fLock);
return fUnnamedSemaphores.Lookup(id) != NULL;
}
private:
typedef OpenHashTable<NamedSemHashDefinition, true> NamedSemTable;
typedef OpenHashTable<UnnamedSemHashDefinition, true> UnnamedSemTable;
mutex fLock;
NamedSemTable fNamedSemaphores;
UnnamedSemTable fUnnamedSemaphores;
int32 fSemaphoreCount;
};
static GlobalRealtimeSemTable sSemTable;
static GlobalSemTable sSemTable;
class TeamSemInfo {
@ -241,10 +469,11 @@ public:
~TeamSemInfo()
{
if (fSemaphore != NULL)
fSemaphore->ReleaseReference();
fSemaphore->Delete();
}
SemInfo* Semaphore() const { return fSemaphore; }
sem_id ID() const { return fSemaphore->ID(); }
sem_id SemaphoreID() const { return fSemaphore->SemaphoreID(); }
sem_t* UserSemaphore() const { return fUserSemaphore; }
void Open()
@ -259,15 +488,19 @@ public:
TeamSemInfo* Clone() const
{
TeamSemInfo* sem = new(std::nothrow) TeamSemInfo(fSemaphore,
fUserSemaphore);
SemInfo* sem = fSemaphore->Clone();
if (sem == NULL)
return NULL;
sem->fOpenCount = fOpenCount;
fSemaphore->AcquireReference();
TeamSemInfo* clone = new(std::nothrow) TeamSemInfo(sem, fUserSemaphore);
if (clone == NULL) {
sem->Delete();
return NULL;
}
return sem;
clone->fOpenCount = fOpenCount;
return clone;
}
HashTableLink<TeamSemInfo>* HashTableLink()
@ -295,12 +528,12 @@ struct TeamSemHashDefinition {
size_t Hash(TeamSemInfo* semaphore) const
{
return HashKey(semaphore->Semaphore()->ID());
return HashKey(semaphore->ID());
}
bool Compare(const KeyType& key, TeamSemInfo* semaphore) const
{
return key == semaphore->Semaphore()->ID();
return key == semaphore->ID();
}
HashTableLink<TeamSemInfo>* GetLink(TeamSemInfo* semaphore) const
@ -312,6 +545,8 @@ struct TeamSemHashDefinition {
struct realtime_sem_context {
realtime_sem_context()
:
fSemaphoreCount(0)
{
mutex_init(&fLock, "realtime sem context");
}
@ -334,6 +569,7 @@ struct realtime_sem_context {
status_t Init()
{
fNextPrivateSemID = -1;
return fSemaphores.InitCheck();
}
@ -347,6 +583,8 @@ struct realtime_sem_context {
MutexLocker _(fLock);
context->fNextPrivateSemID = fNextPrivateSemID;
// clone all semaphores
SemTable::Iterator it = fSemaphores.GetIterator();
while (TeamSemInfo* sem = it.Next()) {
@ -358,21 +596,24 @@ struct realtime_sem_context {
delete clonedSem;
return NULL;
}
context->fSemaphoreCount++;
}
contextDeleter.Detach();
return context;
}
status_t CreateAnonymousSem(uint32 semCount, int& _id)
status_t CreateUnnamedSem(uint32 semCount, bool shared, int32_t& _id)
{
// TODO: Enforce per team semaphore limit!
SemInfo* sem = new(std::nothrow) SemInfo;
if (shared)
return sSemTable.CreateUnnamedSem(semCount, _id);
UnnamedSem* sem = new(std::nothrow) UnnamedSem;
if (sem == NULL)
return B_NO_MEMORY;
ObjectDeleter<SemInfo> semDeleter(sem);
ObjectDeleter<UnnamedSem> semDeleter(sem);
status_t error = sem->Init(NULL, 0, semCount);
status_t error = sem->Init(semCount, "unnamed sem");
if (error != B_OK)
return error;
@ -383,23 +624,31 @@ struct realtime_sem_context {
MutexLocker _(fLock);
if (fSemaphoreCount >= MAX_POSIX_SEMS_PER_TEAM) {
delete teamSem;
return ENOSPC;
}
sem->SetID(_NextPrivateSemID());
error = fSemaphores.Insert(teamSem);
if (error != B_OK) {
delete teamSem;
return error;
}
_id = teamSem->Semaphore()->ID();
fSemaphoreCount++;
_id = teamSem->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,
uint32 semCount, sem_t* userSem, sem_t*& _usedUserSem, int32_t& _id,
bool& _created)
{
SemInfo* sem;
status_t error = sSemTable.OpenSem(name, openFlags, mode, semCount,
NamedSem* sem;
status_t error = sSemTable.OpenNamedSem(name, openFlags, mode, semCount,
sem, _created);
if (error != B_OK)
return error;
@ -412,16 +661,25 @@ struct realtime_sem_context {
teamSem->Open();
sem->ReleaseReference();
_usedUserSem = teamSem->UserSemaphore();
_id = teamSem->Semaphore()->ID();
_id = teamSem->ID();
return B_OK;
}
// not open yet -- create a new team sem
// first check the semaphore limit, though
if (fSemaphoreCount >= MAX_POSIX_SEMS_PER_TEAM) {
sem->ReleaseReference();
if (_created)
sSemTable.UnlinkNamedSem(name);
return ENOSPC;
}
teamSem = new(std::nothrow) TeamSemInfo(sem, userSem);
if (teamSem == NULL) {
sem->ReleaseReference();
if (_created)
sSemTable.UnlinkSem(name);
sSemTable.UnlinkNamedSem(name);
return B_NO_MEMORY;
}
@ -429,12 +687,14 @@ struct realtime_sem_context {
if (error != B_OK) {
delete teamSem;
if (_created)
sSemTable.UnlinkSem(name);
sSemTable.UnlinkNamedSem(name);
return error;
}
fSemaphoreCount++;
_usedUserSem = teamSem->UserSemaphore();
_id = teamSem->Semaphore()->ID();
_id = teamSem->ID();
return B_OK;
}
@ -447,11 +707,12 @@ struct realtime_sem_context {
TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL)
return B_BAD_VALUE;
return sSemTable.DeleteUnnamedSem(id);
if (sem->Close()) {
// last reference closed
fSemaphores.Remove(sem);
fSemaphoreCount--;
deleteUserSem = sem->UserSemaphore();
delete sem;
}
@ -463,8 +724,12 @@ struct realtime_sem_context {
{
MutexLocker locker(fLock);
if (fSemaphores.Lookup(id) == NULL)
return B_BAD_VALUE;
TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL) {
if (!sSemTable.IsUnnamedValidSem(id))
return B_BAD_VALUE;
} else
id = sem->SemaphoreID();
locker.Unlock();
@ -487,8 +752,12 @@ struct realtime_sem_context {
{
MutexLocker locker(fLock);
if (fSemaphores.Lookup(id) == NULL)
return B_BAD_VALUE;
TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL) {
if (!sSemTable.IsUnnamedValidSem(id))
return B_BAD_VALUE;
} else
id = sem->SemaphoreID();
locker.Unlock();
@ -500,8 +769,12 @@ struct realtime_sem_context {
{
MutexLocker locker(fLock);
if (fSemaphores.Lookup(id) == NULL)
return B_BAD_VALUE;
TeamSemInfo* sem = fSemaphores.Lookup(id);
if (sem == NULL) {
if (!sSemTable.IsUnnamedValidSem(id))
return B_BAD_VALUE;
} else
id = sem->SemaphoreID();
locker.Unlock();
@ -514,11 +787,26 @@ struct realtime_sem_context {
return B_OK;
}
private:
sem_id _NextPrivateSemID()
{
while (true) {
if (fNextPrivateSemID >= 0)
fNextPrivateSemID = -1;
sem_id id = fNextPrivateSemID--;
if (fSemaphores.Lookup(id) == NULL)
return id;
}
}
private:
typedef OpenHashTable<TeamSemHashDefinition, true> SemTable;
mutex fLock;
SemTable fSemaphores;
int32 fSemaphoreCount;
sem_id fNextPrivateSemID;
};
@ -585,9 +873,9 @@ copy_sem_name_to_kernel(const char* userName, KPath& buffer, char*& name)
void
realtime_sem_init()
{
new(&sSemTable) GlobalRealtimeSemTable;
new(&sSemTable) GlobalSemTable;
if (sSemTable.Init() != B_OK)
panic("realtime_sem_init() failed to init global table");
panic("realtime_sem_init() failed to init global sem table");
}
@ -612,23 +900,27 @@ clone_realtime_sem_context(realtime_sem_context* context)
status_t
_user_realtime_sem_open(const char* userName, int openFlags, mode_t mode,
uint32 semCount, sem_t* userSem, sem_t** _usedUserSem)
_user_realtime_sem_open(const char* userName, int openFlagsOrShared,
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;
if (semCount > MAX_POSIX_SEM_VALUE)
return B_BAD_VALUE;
// 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
// unnamed semaphores are less work -- deal with them first
if (userName == NULL) {
int id;
status_t error = context->CreateAnonymousSem(semCount, id);
int32_t id;
status_t error = context->CreateUnnamedSem(semCount, openFlagsOrShared,
id);
if (error != B_OK)
return error;
@ -657,8 +949,8 @@ _user_realtime_sem_open(const char* userName, int openFlags, mode_t mode,
// open the semaphore
sem_t* usedUserSem;
bool created;
int id;
error = context->OpenSem(name, openFlags, mode, semCount, userSem,
int32_t id;
error = context->OpenSem(name, openFlagsOrShared, mode, semCount, userSem,
usedUserSem, id, created);
if (error != B_OK)
return error;
@ -667,7 +959,7 @@ _user_realtime_sem_open(const char* userName, int openFlags, mode_t mode,
if (user_memcpy(&userSem->id, &id, sizeof(int)) != B_OK
|| user_memcpy(_usedUserSem, &usedUserSem, sizeof(sem_t*)) != B_OK) {
if (created)
sSemTable.UnlinkSem(name);
sSemTable.UnlinkNamedSem(name);
sem_t* dummy;
context->CloseSem(id, dummy);
return B_BAD_ADDRESS;
@ -714,7 +1006,7 @@ _user_realtime_sem_unlink(const char* userName)
if (error != B_OK)
return error;
return sSemTable.UnlinkSem(name);
return sSemTable.UnlinkNamedSem(name);
}

View File

@ -89,8 +89,8 @@ sem_unlink(const char* 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));
RETURN_AND_SET_ERRNO(_kern_realtime_sem_open(NULL, shared, 0, value,
semaphore, NULL));
}