2008/7/18 Axel Dörfler <axeld@xxxxxxxxxxxxxxxx>: > >> +union semun { >> + int val; >> + struct semid_ds *buf; >> + unsigned short *array; >> +}; > > I thought you wanted to use better names? It' s a private structure after all. Actually I'd like to keep it like this in order not to create confusion with the standard. >> +struct seminfo { >> + int semmni; /* Number of semaphore identifies */ > > Tab between "int" and the names? At least it looks a bit inconsistent with > the structures above. This struct is probably going away soon. I though I needed for the ipcs and ipcrm command lines but I think I can do without it. >> if (isPrivate) >> semaphoreSet->SetIpcKey((key_t)-1); >> else { >> semaphoreSet->SetIpcKey(key); >> ipcKey->SetSemaphoreSetID(semaphoreSet); >> sSemaphoreHashTable.Insert(semaphoreSet); >> } > > So private semaphores only get leaked and cannot be accessed anymore > afterwards? :-) Ups! :-) > >> case IPC_RMID: { > > This doesn't give an error for private sem sets - is that intended? Actually, I think I'm doing this one wrong because the standard doesn't say anything about removing ipc obects, but only xsi semaphores set. In fact, as I said in previous email, the standards is not very clear about the fact the same ipc object (key) can be used for other ipc subsystem (shared memory, message queues). Anyway, it's fixed and for now I'd keep it like this. > In general, I can't see (or maybe just don't understand) how this loop is > supposed to work. Do you really need to execute the operations over and over > again? Why wouldn't it lock the second time? After all, you are reverting all > the ops before waiting (and then re-executing). > Basically, in the for loop you are supposed to execute an operation on each semaphore. However, if one of those operation happens to block you (makes you wait), you must undo all the previously done operation on the other semaphores of the set because that could cause some sort of unwanted deadlock among threads fighting for the same set of semaphore. That's also how OpenBSD does it. > Anyway, nice to see you progressing! In general, I would prefer if you would > use the in-class function declarations less, since they make the code a bit > clumsy if they are long, and separated (like for the SetID() case). Honestly I don't like in-class functions declarations either, but I started by looking at Ingo semaphores implementation and ended up "copying" his style. Anyway, I think it's pretty understandable even this way. > I think writing test apps makes a lot sense right now; you can probably find > most of the remaining problems better than by having the code reviewed. Unfortunately there is not test apps in the open posix test suite, but I found something in the linux test project, which can come handy even for other posix standards we might implement. http://ltp.sourceforge.net/ I've already run the simpliest test provided by ltp and it ran succefully. > > One more comment though: what about the lifetime of a semaphore set? I would > guess the private ones should be bound to the team, and should be deleted > with it. Yes, I think they should, but that's only to prevent some sorts of leak, because the threads itself is actually supposed to do that with IPC_RMID. Regards, -- Salvatore Benedetto (a.k.a. emitrax) Student of Computer Engineer University of Pisa www.haiku-os.it
Index: src/system/kernel/main.cpp =================================================================== --- src/system/kernel/main.cpp (revision 26564) +++ src/system/kernel/main.cpp (working copy) @@ -36,6 +36,7 @@ #include <Notifications.h> #include <port.h> #include <posix/realtime_sem.h> +#include <posix/xsi_semaphore.h> #include <real_time_clock.h> #include <sem.h> #include <smp.h> @@ -170,7 +171,9 @@ TRACE("init kernel daemons\n"); kernel_daemon_init(); arch_platform_init_post_thread(&sKernelArgs); + TRACE("init posix semaphores\n"); realtime_sem_init(); + xsi_ipc_init(); TRACE("init VM threads\n"); vm_init_post_thread(&sKernelArgs); Index: src/system/kernel/posix/Jamfile =================================================================== --- src/system/kernel/posix/Jamfile (revision 26564) +++ src/system/kernel/posix/Jamfile (working copy) @@ -4,6 +4,7 @@ KernelMergeObject kernel_posix.o : realtime_sem.cpp + xsi_semaphore.cpp : $(TARGET_KERNEL_PIC_CCFLAGS) ; Index: src/system/kernel/posix/xsi_semaphore.cpp =================================================================== --- src/system/kernel/posix/xsi_semaphore.cpp (revision 0) +++ src/system/kernel/posix/xsi_semaphore.cpp (revision 0) @@ -0,0 +1,872 @@ +/* + * Copyright 2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Salvatore Benedetto <salvatore.benedetto@xxxxxxxxx> + */ + +#include <posix/xsi_semaphore.h> + +#include <new> + +#include <sys/ipc.h> +#include <sys/types.h> + +#include <OS.h> + +#include <kernel.h> +#include <syscall_restart.h> + +#include <util/AutoLock.h> +#include <util/DoublyLinkedList.h> +#include <util/OpenHashTable.h> +#include <util/Vector.h> + + +//#define TRACE_XSI_SEM +#ifdef TRACE_XSI_SEM +# define TRACE(x) dprintf x +# define TRACE_ERROR(x) dprintf x +#else +# define TRACE(x) /* nothing */ +# define TRACE_ERROR(x) dprintf x +#endif + +//#define KTRACE_XSI_SEM +#ifdef KTRACE_XSI_SEM +# define KTRACE(x...) ktrace_printf(x) +#else +# define KTRACE(x...) +#endif + +// Queue for holding blocked threads +struct queued_thread : DoublyLinkedListLinkImpl<queued_thread> { + queued_thread(struct thread *thread, int32 count) + : + thread(thread), + count(count), + queued(false) + { + } + + struct thread *thread; + int32 count; + bool queued; +}; + +typedef DoublyLinkedList<queued_thread> ThreadQueue; + + +// Xsi semaphore definition +class XsiSemaphore { +public: + XsiSemaphore() + : fLastPidOperation(0), + fThreadsWaitingToIncrease(0), + fThreadsWaitingToBeZero(0), + fValue(0) + { + } + + ~XsiSemaphore() + { + // For some reason the semaphore is getting destroyed. + // Wake up any remaing awaiting threads + InterruptsSpinLocker _(thread_spinlock); + while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) { + entry->queued = false; + thread_unblock_locked(entry->thread, EIDRM); + } + while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) { + entry->queued = false; + thread_unblock_locked(entry->thread, EIDRM); + } + } + + // We return true in case the operation causes the + // caller to wait, so it can undo all the operations + // previously done + bool Add(short value) + { + if ((int)(fValue + value) < 0) { + TRACE(("XsiSemaphore::Add: going to sleep\n")); + return true; + } else { + fValue += value; + if (fValue == 0 && fThreadsWaitingToBeZero > 0) + WakeUpThread(true); + else if (fValue > 0 && fThreadsWaitingToIncrease > 0) + WakeUpThread(false); + return false; + } + } + + pid_t LastPid() const + { + return fLastPidOperation; + } + + void Revert(short value) + { + fValue -= value; + } + + ushort ThreadsWaitingToIncrease() const + { + return fThreadsWaitingToIncrease; + } + + ushort ThreadsWaitingToBeZero() const + { + return fThreadsWaitingToBeZero; + } + + ushort Value() const + { + return fValue; + } + + void SetPid(pid_t pid) + { + fLastPidOperation = pid; + } + + void SetValue(ushort value) + { + // TODO: the semadj value corresponding to the + // specified semaphore in all processes is cleared + fValue = value; + } + + status_t Wait(int32 count, bool waitForZero) + { + // enqueue the thread in the appropriate + // queue and get ready to wait + struct thread *thread = thread_get_current_thread(); + queued_thread queueEntry(thread, count); + if (waitForZero) { + fWaitingToBeZeroQueue.Add(&queueEntry); + fThreadsWaitingToBeZero++; + } else { + fWaitingToIncreaseQueue.Add(&queueEntry); + fThreadsWaitingToIncrease++; + } + queueEntry.queued = true; + + thread_prepare_to_block(thread, B_CAN_INTERRUPT, + THREAD_BLOCK_TYPE_OTHER, (void*)"xsi semaphore"); + + InterruptsSpinLocker _(thread_spinlock); + status_t result = thread_block_locked(thread); + + return result; + } + + void WakeUpThread(bool waitingForZero) + { + queued_thread *entry; + bool unblock = false; + + InterruptsSpinLocker _(thread_spinlock); + if (waitingForZero) { + unblock = true; + entry = fWaitingToBeZeroQueue.RemoveHead(); + } else { + entry = fWaitingToIncreaseQueue.Head(); + if ((entry->count + fValue) >= 0) { + unblock = true; + entry = fWaitingToIncreaseQueue.RemoveHead(); + } + } + if (unblock) { + entry->queued = false; + thread_unblock_locked(entry->thread, 0); + } + } + +private: + pid_t fLastPidOperation; // sempid + ushort fThreadsWaitingToIncrease; // semncnt + ushort fThreadsWaitingToBeZero; // semzcnt + short fValue; // semval + + ThreadQueue fWaitingToIncreaseQueue; + ThreadQueue fWaitingToBeZeroQueue; +}; + +#define MAX_XSI_SEMS_PER_TEAM 128 + +// Xsi semaphore set definition (semid_ds) +class XsiSemaphoreSet { +public: + XsiSemaphoreSet(int numberOfSemaphores, int flags) + : fInitOK(false), + fLastSemctlTime((time_t)real_time_clock()), + fLastSemopTime(0), + fNumberOfSemaphores(numberOfSemaphores), + fSemaphores(0) + { + SetIpcKey((key_t)-1); + SetPermissions(flags); + fSemaphores = new(std::nothrow) XsiSemaphore[numberOfSemaphores]; + if (fSemaphores == NULL) { + TRACE_ERROR(("XsiSemaphoreSet::XsiSemaphore(): failed to allocate " + "XsiSemaphore object\n")); + } else + fInitOK = true; + } + + bool InitOK() + { + return fInitOK; + } + + ~XsiSemaphoreSet() + { + delete []fSemaphores; + } + + void DoIpcSet(struct semid_ds *result) + { + fPermissions.uid = result->sem_perm.uid; + fPermissions.gid = result->sem_perm.gid; + fPermissions.mode = (fPermissions.mode & ~0x01ff) + | (result->sem_perm.mode & 0x01ff); + } + + int ID() const + { + return fID; + } + + key_t IpcKey() const + { + return fPermissions.key; + } + + struct ipc_perm IpcPermission() const + { + return fPermissions; + } + + time_t LastSemctlTime() const + { + return fLastSemctlTime; + } + + time_t LastSemopTime() const + { + return fLastSemopTime; + } + + ushort NumberOfSemaphores() const + { + return fNumberOfSemaphores; + } + + XsiSemaphore* Semaphore(int nth) const + { + return &fSemaphores[nth]; + } + + // Implemented after sSemaphoreHashTable is declared + void SetID(); + + void SetIpcKey(key_t key) + { + fPermissions.key = key; + } + + void SetLastSemctlTime() + { + fLastSemctlTime = real_time_clock(); + } + + void SetLastSemopTime() + { + fLastSemopTime = real_time_clock(); + } + + void SetPermissions(int flags) + { + fPermissions.uid = fPermissions.cuid = geteuid(); + fPermissions.gid = fPermissions.cgid = getegid(); + fPermissions.mode = (flags & 0x01ff); + } + + bool HasPermission() const + { + if ((fPermissions.mode & S_IWOTH) != 0) + return true; + + uid_t uid = geteuid(); + if (uid == 0 || (uid == fPermissions.uid + && (fPermissions.mode & S_IWUSR) != 0)) + return true; + + gid_t gid = getegid(); + if (gid == fPermissions.gid && (fPermissions.mode & S_IWGRP) != 0) + return true; + + return false; + } + + bool HasReadPermission() const + { + // TODO: fix this + return HasPermission(); + } + + HashTableLink<XsiSemaphoreSet>* Link() + { + return &fLink; + } + +private: + bool fInitOK; + int fID; // semaphore set id + time_t fLastSemctlTime; // sem_ctime + time_t fLastSemopTime; // sem_otime + ushort fNumberOfSemaphores; // sem_nsems + struct ipc_perm fPermissions; // sem_perm + XsiSemaphore *fSemaphores; + + ::HashTableLink<XsiSemaphoreSet> fLink; +}; + +// Xsi semaphore set hash table +struct SemaphoreHashTableDefinition { + typedef int KeyType; + typedef XsiSemaphoreSet ValueType; + + size_t HashKey (const int key) const + { + return (size_t)key; + } + size_t Hash(XsiSemaphoreSet *variable) const + { + return (size_t)variable->ID(); + } + bool Compare(const int key, XsiSemaphoreSet *variable) const + { + return (int)key == (int)variable->ID(); + } + HashTableLink<XsiSemaphoreSet>* GetLink(XsiSemaphoreSet *variable) const + { + return variable->Link(); + } +}; + + +// IPC class +class Ipc { +public: + Ipc(key_t key) + : fKey(key), + fSemaphoreSetId(-1) + { + } + + key_t Key() const + { + return fKey; + } + + int SemaphoreSetID() const + { + return fSemaphoreSetId; + } + + void SetSemaphoreSetID(XsiSemaphoreSet *semaphoreSet) + { + fSemaphoreSetId = semaphoreSet->ID(); + } + + bool HasSemaphoreSet() + { + if (fSemaphoreSetId != -1) + return true; + return false; + } + + HashTableLink<Ipc>* Link() + { + return &fLink; + } + +private: + key_t fKey; + int fSemaphoreSetId; + HashTableLink<Ipc> fLink; +}; + + +struct IpcHashTableDefinition { + typedef key_t KeyType; + typedef Ipc ValueType; + + size_t HashKey (const key_t key) const + { + return (size_t)(key); + } + size_t Hash(Ipc *variable) const + { + return (size_t)HashKey(variable->Key()); + } + bool Compare(const key_t key, Ipc *variable) const + { + return (key_t)key == (key_t)variable->Key(); + } + HashTableLink<Ipc>* GetLink(Ipc *variable) const + { + return variable->Link(); + } +}; + +// Arbitrary limit +#define MAX_XSI_SEMAPHORE 512 +static OpenHashTable<IpcHashTableDefinition> sIpcHashTable; +static OpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable; + +static mutex sXsiSemaphoreSetLock; +static mutex sIpcLock; +static vint32 sNextAvailableID = 0; +static vint32 sXsiSemaphoreCount = 0; + + +void +XsiSemaphoreSet::SetID() +{ + // The lock is held upon creation of the object + while (true) { + if (sSemaphoreHashTable.Lookup(sNextAvailableID) == NULL) + break; + sNextAvailableID++; + } + fID = sNextAvailableID++; +} + + +void +xsi_ipc_init() +{ + // Initialize hash tables + status_t status = sIpcHashTable.Init(); + if (status != B_OK) + panic("xsi_ipc_init() failed to initialized ipc hash table\n"); + status = sSemaphoreHashTable.Init(); + if (status != B_OK) + panic("xsi_ipc_init() failed to initialized semaphore hash table\n"); + + mutex_init(&sIpcLock, "global Posix IPC table"); + mutex_init(&sXsiSemaphoreSetLock, "global Posix xsi sem table"); +} + + +int +_user_xsi_semget(key_t key, int numberOfSemaphores, int flags) +{ + XsiSemaphoreSet *semaphoreSet = NULL; + Ipc *ipcKey = NULL; + // Default assumptions + bool isPrivate = true; + bool create = true; + + MutexLocker _(sIpcLock); + if (key != IPC_PRIVATE) { + isPrivate = false; + // Check if key already has a semaphore associated with it + ipcKey = sIpcHashTable.Lookup(key); + if (ipcKey == NULL) { + // The ipc key have probably just been created + // by the caller, add it to the system + ipcKey = new(std::nothrow) Ipc(key); + if (ipcKey == NULL) { + TRACE_ERROR(("xsi_semget: failed to create new Ipc object " + "for key %d\n", (int)key)); + return ENOMEM; + } + sIpcHashTable.Insert(ipcKey); + } else if (ipcKey->HasSemaphoreSet()) { + // The IPC key exist and it already has a semaphore + if ((flags & IPC_CREAT) && (flags & IPC_EXCL)) { + TRACE_ERROR(("xsi_semget: key %d already exist\n", (int)key)); + return EEXIST; + } + int semaphoreSetID = ipcKey->SemaphoreSetID(); + + MutexLocker _(sXsiSemaphoreSetLock); + semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreSetID); + if (!semaphoreSet->HasPermission()) { + TRACE_ERROR(("xsi_semget: calling process has not permission " + "on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + if (semaphoreSet->NumberOfSemaphores() >= numberOfSemaphores + && numberOfSemaphores != 0) { + TRACE_ERROR(("xsi_semget: numberOfSemaphores greater than the " + "one associated with semaphore %d, key %d\n", + semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); + return EINVAL; + } + create = false; + } else { + // The IPC key exist but it has not semaphore associated with it + if (!(flags & IPC_CREAT)) { + TRACE_ERROR(("xsi_semget: key %d has not semaphore associated " + "with it and caller did not ask for creation\n",(int)key)); + return ENOENT; + } + } + } + + if (create) { + // Create a new sempahore set for this key + if (numberOfSemaphores < 0 + || numberOfSemaphores >= MAX_XSI_SEMS_PER_TEAM) { + TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n")); + return EINVAL; + } + if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE) { + TRACE_ERROR(("xsi_semget: reached limit of maximum number of " + "semaphores allowed\n")); + return ENOSPC; + } + atomic_add(&sXsiSemaphoreCount, 1); + + semaphoreSet = new(std::nothrow) XsiSemaphoreSet(numberOfSemaphores, + flags); + if (semaphoreSet == NULL || !semaphoreSet->InitOK()) { + TRACE_ERROR(("xsi_semget: failed to allocate a new xsi " + "semaphore set\n")); + atomic_add(&sXsiSemaphoreCount, -1); + return ENOMEM; + } + + MutexLocker _(sXsiSemaphoreSetLock); + semaphoreSet->SetID(); + if (isPrivate) + semaphoreSet->SetIpcKey((key_t)-1); + else { + semaphoreSet->SetIpcKey(key); + ipcKey->SetSemaphoreSetID(semaphoreSet); + } + sSemaphoreHashTable.Insert(semaphoreSet); + } + + return semaphoreSet->ID(); +} + + +int +_user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command, + union semun *args) +{ + MutexLocker _(sXsiSemaphoreSetLock); + XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); + if (semaphoreSet == NULL) { + TRACE_ERROR(("xsi_semctl: semaphore set id %d not valid\n", + semaphoreID)); + return EINVAL; + } + if (semaphoreNumber < 0 + || semaphoreNumber >= semaphoreSet->NumberOfSemaphores()) { + TRACE_ERROR(("xsi_semctl: semaphore number %d not valid for " + "semaphore %d\n", semaphoreNumber, semaphoreID)); + return EINVAL; + } + + if (args != 0 && !IS_USER_ADDRESS(args)) { + TRACE_ERROR(("xsi_semctl: semun address is not valid\n")); + return B_BAD_ADDRESS; + } + + XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber); + switch (command) { + case GETVAL: + if (!semaphoreSet->HasReadPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not permission " + "on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + return semaphore->Value(); + + case SETVAL: + if (!semaphoreSet->HasPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not permission " + "on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + int value; + if (user_memcpy(&value, &args->val, sizeof(int) < B_OK)) { + TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); + return B_BAD_ADDRESS; + } + if (value > USHRT_MAX) { + TRACE_ERROR(("xsi_semctl: value %d out of range\n", value)); + return ERANGE; + } + semaphore->SetValue(value); + return 0; + + case GETPID: + if (!semaphoreSet->HasReadPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not permission " + "on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + return semaphore->LastPid(); + + case GETNCNT: + if (!semaphoreSet->HasReadPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not permission " + "on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + return semaphore->ThreadsWaitingToIncrease(); + + case GETZCNT: + if (!semaphoreSet->HasReadPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not permission " + "on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + return semaphore->ThreadsWaitingToBeZero(); + + case GETALL: { + if (!semaphoreSet->HasReadPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not read " + "permission on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { + semaphore = semaphoreSet->Semaphore(i); + unsigned short value = semaphore->Value(); + if (user_memcpy(&args->array[i], &value, sizeof(unsigned short)) + < B_OK) { + TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); + return B_BAD_ADDRESS; + } + } + return 0; + } + + case SETALL: { + if (!semaphoreSet->HasPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not permission " + "on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) { + semaphore = semaphoreSet->Semaphore(i); + unsigned short value; + if (user_memcpy(&value, &args->array[i], sizeof(unsigned short)) + < B_OK) { + TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); + return B_BAD_ADDRESS; + } + semaphore->SetValue(value); + } + return 0; + } + + case IPC_STAT: { + if (!semaphoreSet->HasReadPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not read " + "permission on semaphore %d, key %d\n", semaphoreSet->ID(), + (int)semaphoreSet->IpcKey())); + return EACCES; + } + struct semid_ds result; + result.sem_perm = semaphoreSet->IpcPermission(); + result.sem_nsems = semaphoreSet->NumberOfSemaphores(); + result.sem_otime = semaphoreSet->LastSemopTime(); + result.sem_ctime = semaphoreSet->LastSemctlTime(); + if (user_memcpy(args->buf, &result, sizeof(struct semid_ds)) + < B_OK) { + TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); + return B_BAD_ADDRESS; + } + return 0; + } + + case IPC_SET: { + if (!semaphoreSet->HasPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not " + "permission on semaphore %d, key %d\n", + semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); + return EACCES; + } + struct semid_ds result; + if (user_memcpy(&result, args->buf, sizeof(struct semid_ds)) + < B_OK) { + TRACE_ERROR(("xsi_semctl: user_memcpy failed\n")); + return B_BAD_ADDRESS; + } + semaphoreSet->DoIpcSet(&result); + return 0; + } + + case IPC_RMID: { + if (!semaphoreSet->HasPermission()) { + TRACE_ERROR(("xsi_semctl: calling process has not " + "permission on semaphore %d, key %d\n", + semaphoreSet->ID(), (int)semaphoreSet->IpcKey())); + return EACCES; + } + key_t key = semaphoreSet->IpcKey(); + Ipc *ipcKey = NULL; + if (key != -1) { + MutexLocker _(sIpcLock); + ipcKey = sIpcHashTable.Lookup(key); + sIpcHashTable.Remove(ipcKey); + } + sSemaphoreHashTable.Remove(semaphoreSet); + atomic_add(&sXsiSemaphoreCount, + semaphoreSet->NumberOfSemaphores()); + // Wake up of threads waiting on this set + // happens in the destructor + delete semaphoreSet; + if (key != -1) + delete ipcKey; + return 0; + } + + default: + TRACE_ERROR(("xsi_semctl: command %d not valid\n", command)); + return EINVAL; + } +} + + +status_t +_user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps) +{ + MutexLocker lock(sXsiSemaphoreSetLock); + XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); + lock.Unlock(); + if (semaphoreSet == NULL) { + TRACE_ERROR(("xsi_semop: semaphore set id %d not valid\n", + semaphoreID)); + return EINVAL; + } + + if (!IS_USER_ADDRESS(ops)) { + TRACE_ERROR(("xsi_semop: sembuf address is not valid\n")); + return B_BAD_ADDRESS; + } + + if (numOps < 0 || numOps >= MAX_XSI_SEMS_PER_TEAM) { + TRACE_ERROR(("xsi_semop: numOps out of range\n")); + return EINVAL; + } + + struct sembuf *operations + = (struct sembuf *)malloc(sizeof(struct sembuf) * numOps); + if (operations == NULL) { + TRACE_ERROR(("xsi_semop: failed to allocate sembuf struct\n")); + return B_NO_MEMORY; + } + + if (user_memcpy(operations, ops, + (sizeof(struct sembuf) * numOps)) < B_OK) { + TRACE_ERROR(("xsi_semop: user_memcpy failed\n")); + return B_BAD_ADDRESS; + } + + /* + * We won't do partial request, that is operations + * only on some sempahores belonging to the set and then + * going to sleep. If we must wait on a semaphore, we undo + * all the operations already done and go to sleep, otherwise + * we may caused some unwanted deadlock among threads + * fighting for the same set. + */ + bool notDone = true; + bool goToSleep = false; + status_t result = 0; + while (notDone) { + XsiSemaphore *semaphore = NULL; + short numberOfSemaphores = semaphoreSet->NumberOfSemaphores(); + + uint32 i = 0; + for (; i < numOps; i++) { + short semaphoreNumber = operations[i].sem_num; + if (semaphoreNumber >= numberOfSemaphores) { + TRACE_ERROR(("xsi_semop: %d invalid semaphore number\n", i)); + result = EINVAL; + break; + } + semaphore = semaphoreSet->Semaphore(semaphoreNumber); + unsigned short value = semaphore->Value(); + short operation = operations[i].sem_op; + if (operation < 0) { + if (semaphore->Add(operation)) { + goToSleep = true; + break; + } + } else if (operation == 0) { + if (value == 0) + continue; + else if (operations[i].sem_flg & IPC_NOWAIT) { + result = EAGAIN; + break; + } else { + goToSleep = true; + break; + } + } else { + // Operation must be greater than zero, + // just add the value and continue + semaphore->Add(operation); + } + } + + // Either we have to wait or an error occured + if (goToSleep || result != 0) { + // Undo all previously done operations + for (int j = 0; j < i; j++) { + short semaphoreNumber = operations[j].sem_num; + semaphore = semaphoreSet->Semaphore(semaphoreNumber); + short operation = operations[j].sem_op; + if (operation != 0) + semaphore->Revert(operation); + } + if (result != 0) + return result; + + bool waitOnZero = true; + if (operations[i].sem_op != 0) + waitOnZero = false; + + result = semaphore->Wait((int32)operations[i].sem_op, waitOnZero); + + // We are back to life. + // Find out why! + lock.Lock(); + semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID); + lock.Unlock(); + if (semaphoreSet == NULL || result == EIDRM + || result == B_INTERRUPTED) { + TRACE_ERROR(("xsi_semop: semaphore set id %d got destroyed\n", + semaphoreID)); + result = EIDRM; + notDone = false; + } + } else + // everything worked like a charm + notDone = false; + // TODO: Handle SEM_UNDO requests + // TODO: Also set last PID for this semaphore set + } + return result; +} Index: src/system/kernel/syscalls.cpp =================================================================== --- src/system/kernel/syscalls.cpp (revision 26564) +++ src/system/kernel/syscalls.cpp (working copy) @@ -17,6 +17,7 @@ #include <vm.h> #include <thread.h> #include <posix/realtime_sem.h> +#include <posix/xsi_semaphore.h> #include <sem.h> #include <port.h> #include <cpu.h> Index: src/system/libroot/posix/sys/ftok.c =================================================================== --- src/system/libroot/posix/sys/ftok.c (revision 0) +++ src/system/libroot/posix/sys/ftok.c (revision 0) @@ -0,0 +1,22 @@ +/* + * Copyright 2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Salvatore Benedetto <salvatore.benedetto@xxxxxxxxx> + */ + +#include <sys/ipc.h> +#include <sys/types.h> +#include <sys/stat.h> + +key_t +ftok(const char *path, int id) +{ + struct stat st; + + if (stat(path,&st) < 0) + return (key_t)-1; + + return (key_t)(id << 24 | (st.st_dev & 0xff) << 16 | (st.st_ino & 0xffff)); +} Index: src/system/libroot/posix/sys/xsi_sem.cpp =================================================================== --- src/system/libroot/posix/sys/xsi_sem.cpp (revision 0) +++ src/system/libroot/posix/sys/xsi_sem.cpp (revision 0) @@ -0,0 +1,75 @@ +/* + * Copyright 2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * Salvatore Benedetto <salvatore.benedetto@xxxxxxxxx> + */ + +#include <sys/sem.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdlib.h> + +#include <OS.h> + +#include <posix/realtime_sem_defs.h> +#include <syscall_utils.h> +#include <syscalls.h> + + +/* + * For the semctl option argument, the user + * should declare explicitly the following union + */ +union semun { + int val; + struct semid_ds *buf; + unsigned short *array; +}; + + +int +semget(key_t key, int num_sems, int sem_flags) +{ + RETURN_AND_SET_ERRNO(_kern_xsi_semget(key, num_sems, sem_flags)); +} + + +int +semctl(int semId, int sem_num, int command, ...) +{ + union semun arg; + va_list args; + + switch (command) { + case GETVAL: + case GETPID: + case GETNCNT: + case GETZCNT: + case IPC_RMID: + RETURN_AND_SET_ERRNO(_kern_xsi_semctl(semId, sem_num, command, 0)); + + case SETVAL: + case GETALL: + case SETALL: + case IPC_STAT: + case IPC_SET: + va_start(args, command); + arg = va_arg(args, union semun); + va_end(args); + RETURN_AND_SET_ERRNO(_kern_xsi_semctl(semId, sem_num, command, + &arg)); + default: + return EINVAL; + } +} + + +int +semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops) +{ + RETURN_AND_SET_ERRNO(_kern_xsi_semop(sem_id, sem_ops, num_sem_ops)); +} Index: src/system/libroot/posix/sys/Jamfile =================================================================== --- src/system/libroot/posix/sys/Jamfile (revision 26564) +++ src/system/libroot/posix/sys/Jamfile (working copy) @@ -7,6 +7,7 @@ chmod.c flock.c ftime.c + ftok.c getrusage.c gettimeofday.c itimer.c @@ -23,4 +24,5 @@ uname.c utimes.c wait.c + xsi_sem.cpp ; Index: headers/posix/sys/ipc.h =================================================================== --- headers/posix/sys/ipc.h (revision 26564) +++ headers/posix/sys/ipc.h (working copy) @@ -6,8 +6,8 @@ #define _SYS_IPC_H +#include <sys/cdefs.h> #include <sys/types.h> -#error functionality has not yet been implemented /* Mode bits for msgget(), semget(), and shmget() */ @@ -17,15 +17,15 @@ /* Control commands for msgctl(), semctl(), and shmctl() */ #define IPC_RMID 0 /* remove identifier */ -#define IPC_SET 1 -#define IPC_STAT 2 +#define IPC_SET 1 /* set options */ +#define IPC_STAT 2 /* get options */ /* Private key */ -#define IPC_PRIVATE 0 +#define IPC_PRIVATE (key_t)0 struct ipc_perm { - key_t key; + key_t key; /* IPC identifier */ uid_t uid; /* owner's user ID */ gid_t gid; /* owner's group ID */ uid_t cuid; /* creator's user ID */ @@ -34,14 +34,10 @@ }; -#ifdef __cplusplus -extern "C" { -#endif +__BEGIN_DECLS key_t ftok(const char *path, int id); -#ifdef __cplusplus -} -#endif +__END_DECLS #endif /* _SYS_IPC_H */ Index: headers/posix/sys/sem.h =================================================================== --- headers/posix/sys/sem.h (revision 0) +++ headers/posix/sys/sem.h (revision 0) @@ -0,0 +1,65 @@ +/* + * Copyright 2008, Haiku Inc. All Rights Reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef _SYS_SEM_H +#define _SYS_SEM_H + +#include <sys/cdefs.h> +#include <sys/ipc.h> +#include <sys/types.h> + + +/* Semaphore operation flags */ +#define SEM_UNDO 10 + +/* Command definition for semctl */ +#define GETPID 3 /* Get process ID of last element manipulating */ +#define GETVAL 4 /* Get semval */ +#define GETALL 5 /* Get all semval */ +#define GETNCNT 6 /* Get semncnt */ +#define GETZCNT 7 /* Get semzcnt */ +#define SETVAL 8 /* Set semval */ +#define SETALL 9 /* Set all semval */ + +struct semid_ds { + struct ipc_perm sem_perm; /* Operation permission structure */ + unsigned short sem_nsems; /* Number of semaphores in set */ + time_t sem_otime; /* Last semop */ + time_t sem_ctime; /* Last time changed by semctl */ +}; + +/* Structure passed as parameter to the semop function */ +struct sembuf { + unsigned short sem_num; /* Semaphore number */ + short sem_op; /* Semaphore operation */ + short sem_flg; /* Operation flags */ +}; + +/* + * Semaphore info structure. Useful for the ipcs + * standard utily + */ +struct seminfo { + int semmni; /* Number of semaphore identifies */ + int semmns; /* Number of semaphore in system */ + int semmnu; /* Number of undo structures in system */ + int semmsl; /* Max number of semaphores per id */ + int semopm; /* Max number of operations per semop call */ + int semume; /* Max number of undo entries per process */ + int semusz; /* Size in bytes of undo structure */ + int semvmx; /* Semaphore maximum valure */ + int semaem; /* adjust on exit max value */ +}; + + +__BEGIN_DECLS + +int semctl(int sem_id, int sem_num, int command, ...); +int semget(key_t key, int num_sems, int sem_flags); +int semop(int sem_id, struct sembuf *sem_ops, size_t num_sem_ops); + +__END_DECLS + + +#endif /* _SYS_SEM_H */ Index: headers/private/kernel/posix/xsi_semaphore.h =================================================================== --- headers/private/kernel/posix/xsi_semaphore.h (revision 0) +++ headers/private/kernel/posix/xsi_semaphore.h (revision 0) @@ -0,0 +1,32 @@ +/* + * Copyright 2008, Haiku Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef KERNEL_XSI_H +#define KERNEL_XSI_H + +#include <sys/sem.h> +#include <sys/cdefs.h> + +#include <OS.h> + + +union semun { + int val; + struct semid_ds *buf; + unsigned short *array; +}; + +__BEGIN_DECLS + +extern void xsi_ipc_init(); + +/* user calls */ +int _user_xsi_semget(key_t key, int numberOfSemaphores, int flags); +int _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command, + union semun* args); +status_t _user_xsi_semop(int semaphoreID, struct sembuf *sops, size_t nsops); + +__END_DECLS + +#endif /* KERNEL_XSI_H */ Index: headers/private/system/syscalls.h =================================================================== --- headers/private/system/syscalls.h (revision 26564) +++ headers/private/system/syscalls.h (working copy) @@ -27,6 +27,7 @@ struct net_stat; struct pollfd; struct rlimit; +struct sembuf; struct sigaction; struct stat; struct _sem_t; @@ -38,6 +39,8 @@ struct user_disk_device_job_info; struct user_disk_system_info; +union semun; + // This marks the beginning of the syscalls prototypes for gensyscallinfos. // NOTE: // * Nothing but those prototypes may live here. @@ -89,6 +92,14 @@ extern status_t _kern_realtime_sem_post(sem_id semID); extern status_t _kern_realtime_sem_wait(sem_id semID, bigtime_t timeout); +/* POSIX XSI sem syscalls */ +extern int _kern_xsi_semget(key_t key, int numberOfSemaphores, + int flags); +extern int _kern_xsi_semctl(int semaphoreID, int semaphoreNumber, + int command, union semun* args); +extern status_t _kern_xsi_semop(int semaphoreID, struct sembuf *sops, + size_t nsops); + /* team & thread syscalls */ extern thread_id _kern_load_image(const char* const* flatArgs,