From 3dfe682f55250bfe5309902ec217fea71b50c7bb Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 8 May 2008 01:39:49 +0000 Subject: [PATCH] * 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 --- headers/posix/semaphore.h | 4 +- headers/private/kernel/syscalls.h | 6 +- src/system/kernel/posix/realtime_sem.cpp | 456 +++++++++++++++++++---- src/system/libroot/posix/semaphore.cpp | 4 +- 4 files changed, 382 insertions(+), 88 deletions(-) diff --git a/headers/posix/semaphore.h b/headers/posix/semaphore.h index e8013f5ddb..bcf5d359dd 100644 --- a/headers/posix/semaphore.h +++ b/headers/posix/semaphore.h @@ -5,12 +5,14 @@ #ifndef _SEMAPHORE_H_ #define _SEMAPHORE_H_ +#include #include #include typedef struct { - int id; + int32_t id; + int32_t _padding[3]; } sem_t; #define SEM_FAILED ((sem_t*)(long)-1) diff --git a/headers/private/kernel/syscalls.h b/headers/private/kernel/syscalls.h index 227ac2bfb7..9637b7e100 100644 --- a/headers/private/kernel/syscalls.h +++ b/headers/private/kernel/syscalls.h @@ -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); diff --git a/src/system/kernel/posix/realtime_sem.cpp b/src/system/kernel/posix/realtime_sem.cpp index 36ff480610..054418125f 100644 --- a/src/system/kernel/posix/realtime_sem.cpp +++ b/src/system/kernel/posix/realtime_sem.cpp @@ -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* HashTableLink() + virtual sem_id ID() const + { + return SemaphoreID(); + } + + virtual SemInfo* Clone() + { + AcquireReference(); + return this; + } + + virtual void Delete() + { + ReleaseReference(); + } + + HashTableLink* 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 fHashLink; + ::HashTableLink 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* HashTableLink() + { + return &fHashLink; + } + +private: + ::HashTableLink 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* GetLink(SemInfo* semaphore) const + HashTableLink* 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* 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 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 NamedSemTable; + typedef OpenHashTable 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* 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* 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 semDeleter(sem); + ObjectDeleter 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 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); } diff --git a/src/system/libroot/posix/semaphore.cpp b/src/system/libroot/posix/semaphore.cpp index 09fffca661..4b40242fb5 100644 --- a/src/system/libroot/posix/semaphore.cpp +++ b/src/system/libroot/posix/semaphore.cpp @@ -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)); }