libroot_debug: Merge guarded heap into libroot_debug.
[haiku.git] / src / system / kernel / posix / realtime_sem.cpp
blobc6442ab020a22549a33fc72202e5bfe9b1f2ddf6
1 /*
2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <posix/realtime_sem.h>
8 #include <string.h>
10 #include <new>
12 #include <OS.h>
14 #include <AutoDeleter.h>
15 #include <fs/KPath.h>
16 #include <kernel.h>
17 #include <lock.h>
18 #include <syscall_restart.h>
19 #include <team.h>
20 #include <thread.h>
21 #include <util/atomic.h>
22 #include <util/AutoLock.h>
23 #include <util/OpenHashTable.h>
24 #include <util/StringHash.h>
27 class SemInfo {
28 public:
29 SemInfo()
31 fSemaphoreID(-1)
35 virtual ~SemInfo()
37 if (fSemaphoreID >= 0)
38 delete_sem(fSemaphoreID);
41 sem_id SemaphoreID() const { return fSemaphoreID; }
43 status_t Init(int32 semCount, const char* name)
45 fSemaphoreID = create_sem(semCount, name);
46 if (fSemaphoreID < 0)
47 return fSemaphoreID;
49 return B_OK;
52 virtual sem_id ID() const = 0;
53 virtual SemInfo* Clone() = 0;
54 virtual void Delete() = 0;
56 private:
57 sem_id fSemaphoreID;
61 class NamedSem : public SemInfo {
62 public:
63 NamedSem()
65 fName(NULL),
66 fRefCount(1)
70 virtual ~NamedSem()
72 free(fName);
75 const char* Name() const { return fName; }
77 status_t Init(const char* name, mode_t mode, int32 semCount)
79 status_t error = SemInfo::Init(semCount, name);
80 if (error != B_OK)
81 return error;
83 fName = strdup(name);
84 if (fName == NULL)
85 return B_NO_MEMORY;
87 fUID = geteuid();
88 fGID = getegid();
89 fPermissions = mode;
91 return B_OK;
94 void AcquireReference()
96 atomic_add(&fRefCount, 1);
99 void ReleaseReference()
101 if (atomic_add(&fRefCount, -1) == 1)
102 delete this;
105 bool HasPermissions() const
107 if ((fPermissions & S_IWOTH) != 0)
108 return true;
110 uid_t uid = geteuid();
111 if (uid == 0 || (uid == fUID && (fPermissions & S_IWUSR) != 0))
112 return true;
114 gid_t gid = getegid();
115 if (gid == fGID && (fPermissions & S_IWGRP) != 0)
116 return true;
118 return false;
121 virtual sem_id ID() const
123 return SemaphoreID();
126 virtual SemInfo* Clone()
128 AcquireReference();
129 return this;
132 virtual void Delete()
134 ReleaseReference();
137 NamedSem*& HashLink()
139 return fHashLink;
142 private:
143 char* fName;
144 int32 fRefCount;
145 uid_t fUID;
146 gid_t fGID;
147 mode_t fPermissions;
149 NamedSem* fHashLink;
153 struct NamedSemHashDefinition {
154 typedef const char* KeyType;
155 typedef NamedSem ValueType;
157 size_t HashKey(const KeyType& key) const
159 return hash_hash_string(key);
162 size_t Hash(NamedSem* semaphore) const
164 return HashKey(semaphore->Name());
167 bool Compare(const KeyType& key, NamedSem* semaphore) const
169 return strcmp(key, semaphore->Name()) == 0;
172 NamedSem*& GetLink(NamedSem* semaphore) const
174 return semaphore->HashLink();
179 class GlobalSemTable {
180 public:
181 GlobalSemTable()
183 fSemaphoreCount(0)
185 mutex_init(&fLock, "global named sem table");
188 ~GlobalSemTable()
190 mutex_destroy(&fLock);
193 status_t Init()
195 return fNamedSemaphores.Init();
198 status_t OpenNamedSem(const char* name, int openFlags, mode_t mode,
199 uint32 semCount, NamedSem*& _sem, bool& _created)
201 MutexLocker _(fLock);
203 NamedSem* sem = fNamedSemaphores.Lookup(name);
204 if (sem != NULL) {
205 if ((openFlags & O_EXCL) != 0)
206 return EEXIST;
208 if (!sem->HasPermissions())
209 return EACCES;
211 sem->AcquireReference();
212 _sem = sem;
213 _created = false;
214 return B_OK;
217 if ((openFlags & O_CREAT) == 0)
218 return ENOENT;
220 // does not exist yet -- create
221 if (fSemaphoreCount >= MAX_POSIX_SEMS)
222 return ENOSPC;
224 sem = new(std::nothrow) NamedSem;
225 if (sem == NULL)
226 return B_NO_MEMORY;
228 status_t error = sem->Init(name, mode, semCount);
229 if (error != B_OK) {
230 delete sem;
231 return error;
234 error = fNamedSemaphores.Insert(sem);
235 if (error != B_OK) {
236 delete sem;
237 return error;
240 // add one reference for the table
241 sem->AcquireReference();
243 fSemaphoreCount++;
245 _sem = sem;
246 _created = true;
247 return B_OK;
250 status_t UnlinkNamedSem(const char* name)
252 MutexLocker _(fLock);
254 NamedSem* sem = fNamedSemaphores.Lookup(name);
255 if (sem == NULL)
256 return ENOENT;
258 if (!sem->HasPermissions())
259 return EACCES;
261 fNamedSemaphores.Remove(sem);
262 sem->ReleaseReference();
263 // release the table reference
264 fSemaphoreCount--;
266 return B_OK;
269 private:
270 typedef BOpenHashTable<NamedSemHashDefinition, true> NamedSemTable;
272 mutex fLock;
273 NamedSemTable fNamedSemaphores;
274 int32 fSemaphoreCount;
278 static GlobalSemTable sSemTable;
281 class TeamSemInfo {
282 public:
283 TeamSemInfo(SemInfo* semaphore, sem_t* userSem)
285 fSemaphore(semaphore),
286 fUserSemaphore(userSem),
287 fOpenCount(1)
291 ~TeamSemInfo()
293 if (fSemaphore != NULL)
294 fSemaphore->Delete();
297 sem_id ID() const { return fSemaphore->ID(); }
298 sem_id SemaphoreID() const { return fSemaphore->SemaphoreID(); }
299 sem_t* UserSemaphore() const { return fUserSemaphore; }
301 void Open()
303 fOpenCount++;
306 bool Close()
308 return --fOpenCount == 0;
311 TeamSemInfo* Clone() const
313 SemInfo* sem = fSemaphore->Clone();
314 if (sem == NULL)
315 return NULL;
317 TeamSemInfo* clone = new(std::nothrow) TeamSemInfo(sem, fUserSemaphore);
318 if (clone == NULL) {
319 sem->Delete();
320 return NULL;
323 clone->fOpenCount = fOpenCount;
325 return clone;
328 TeamSemInfo*& HashLink()
330 return fHashLink;
333 private:
334 SemInfo* fSemaphore;
335 sem_t* fUserSemaphore;
336 int32 fOpenCount;
338 TeamSemInfo* fHashLink;
342 struct TeamSemHashDefinition {
343 typedef sem_id KeyType;
344 typedef TeamSemInfo ValueType;
346 size_t HashKey(const KeyType& key) const
348 return (size_t)key;
351 size_t Hash(TeamSemInfo* semaphore) const
353 return HashKey(semaphore->ID());
356 bool Compare(const KeyType& key, TeamSemInfo* semaphore) const
358 return key == semaphore->ID();
361 TeamSemInfo*& GetLink(TeamSemInfo* semaphore) const
363 return semaphore->HashLink();
368 struct realtime_sem_context {
369 realtime_sem_context()
371 fSemaphoreCount(0)
373 mutex_init(&fLock, "realtime sem context");
376 ~realtime_sem_context()
378 mutex_lock(&fLock);
380 // delete all semaphores.
381 SemTable::Iterator it = fSemaphores.GetIterator();
382 while (TeamSemInfo* sem = it.Next()) {
383 // Note, this uses internal knowledge about how the iterator works.
384 // Ugly, but there's no good alternative.
385 fSemaphores.RemoveUnchecked(sem);
386 delete sem;
389 mutex_destroy(&fLock);
392 status_t Init()
394 fNextPrivateSemID = -1;
395 return fSemaphores.Init();
398 realtime_sem_context* Clone()
400 // create new context
401 realtime_sem_context* context = new(std::nothrow) realtime_sem_context;
402 if (context == NULL)
403 return NULL;
404 ObjectDeleter<realtime_sem_context> contextDeleter(context);
406 MutexLocker _(fLock);
408 context->fNextPrivateSemID = fNextPrivateSemID;
410 // clone all semaphores
411 SemTable::Iterator it = fSemaphores.GetIterator();
412 while (TeamSemInfo* sem = it.Next()) {
413 TeamSemInfo* clonedSem = sem->Clone();
414 if (clonedSem == NULL)
415 return NULL;
417 if (context->fSemaphores.Insert(clonedSem) != B_OK) {
418 delete clonedSem;
419 return NULL;
421 context->fSemaphoreCount++;
424 contextDeleter.Detach();
425 return context;
428 status_t OpenSem(const char* name, int openFlags, mode_t mode,
429 uint32 semCount, sem_t* userSem, sem_t*& _usedUserSem, int32_t& _id,
430 bool& _created)
432 NamedSem* sem = NULL;
433 status_t error = sSemTable.OpenNamedSem(name, openFlags, mode, semCount,
434 sem, _created);
435 if (error != B_OK)
436 return error;
438 MutexLocker _(fLock);
440 TeamSemInfo* teamSem = fSemaphores.Lookup(sem->ID());
441 if (teamSem != NULL) {
442 // already open -- just increment the open count
443 teamSem->Open();
444 sem->ReleaseReference();
445 _usedUserSem = teamSem->UserSemaphore();
446 _id = teamSem->ID();
447 return B_OK;
450 // not open yet -- create a new team sem
452 // first check the semaphore limit, though
453 if (fSemaphoreCount >= MAX_POSIX_SEMS_PER_TEAM) {
454 sem->ReleaseReference();
455 if (_created)
456 sSemTable.UnlinkNamedSem(name);
457 return ENOSPC;
460 teamSem = new(std::nothrow) TeamSemInfo(sem, userSem);
461 if (teamSem == NULL) {
462 sem->ReleaseReference();
463 if (_created)
464 sSemTable.UnlinkNamedSem(name);
465 return B_NO_MEMORY;
468 error = fSemaphores.Insert(teamSem);
469 if (error != B_OK) {
470 delete teamSem;
471 if (_created)
472 sSemTable.UnlinkNamedSem(name);
473 return error;
476 fSemaphoreCount++;
478 _usedUserSem = teamSem->UserSemaphore();
479 _id = teamSem->ID();
481 return B_OK;
484 status_t CloseSem(sem_id id, sem_t*& deleteUserSem)
486 deleteUserSem = NULL;
488 MutexLocker _(fLock);
490 TeamSemInfo* sem = fSemaphores.Lookup(id);
491 if (sem == NULL)
492 return B_BAD_VALUE;
494 if (sem->Close()) {
495 // last reference closed
496 fSemaphores.Remove(sem);
497 fSemaphoreCount--;
498 deleteUserSem = sem->UserSemaphore();
499 delete sem;
502 return B_OK;
505 status_t AcquireSem(sem_id id, bigtime_t timeout)
507 MutexLocker locker(fLock);
509 TeamSemInfo* sem = fSemaphores.Lookup(id);
510 if (sem == NULL)
511 return B_BAD_VALUE;
512 else
513 id = sem->SemaphoreID();
515 locker.Unlock();
517 status_t error;
518 if (timeout == 0) {
519 error = acquire_sem_etc(id, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,
521 } else if (timeout == B_INFINITE_TIMEOUT) {
522 error = acquire_sem_etc(id, 1, B_CAN_INTERRUPT, 0);
523 } else {
524 error = acquire_sem_etc(id, 1,
525 B_CAN_INTERRUPT | B_ABSOLUTE_REAL_TIME_TIMEOUT, timeout);
528 return error == B_BAD_SEM_ID ? B_BAD_VALUE : error;
531 status_t ReleaseSem(sem_id id)
533 MutexLocker locker(fLock);
535 TeamSemInfo* sem = fSemaphores.Lookup(id);
536 if (sem == NULL)
537 return B_BAD_VALUE;
538 else
539 id = sem->SemaphoreID();
541 locker.Unlock();
543 status_t error = release_sem(id);
544 return error == B_BAD_SEM_ID ? B_BAD_VALUE : error;
547 status_t GetSemCount(sem_id id, int& _count)
549 MutexLocker locker(fLock);
551 TeamSemInfo* sem = fSemaphores.Lookup(id);
552 if (sem == NULL)
553 return B_BAD_VALUE;
554 else
555 id = sem->SemaphoreID();
557 locker.Unlock();
559 int32 count;
560 status_t error = get_sem_count(id, &count);
561 if (error != B_OK)
562 return error;
564 _count = count;
565 return B_OK;
568 private:
569 sem_id _NextPrivateSemID()
571 while (true) {
572 if (fNextPrivateSemID >= 0)
573 fNextPrivateSemID = -1;
575 sem_id id = fNextPrivateSemID--;
576 if (fSemaphores.Lookup(id) == NULL)
577 return id;
581 private:
582 typedef BOpenHashTable<TeamSemHashDefinition, true> SemTable;
584 mutex fLock;
585 SemTable fSemaphores;
586 int32 fSemaphoreCount;
587 sem_id fNextPrivateSemID;
591 // #pragma mark - implementation private
594 static realtime_sem_context*
595 get_current_team_context()
597 Team* team = thread_get_current_thread()->team;
599 // get context
600 realtime_sem_context* context = atomic_pointer_get(
601 &team->realtime_sem_context);
602 if (context != NULL)
603 return context;
605 // no context yet -- create a new one
606 context = new(std::nothrow) realtime_sem_context;
607 if (context == NULL || context->Init() != B_OK) {
608 delete context;
609 return NULL;
612 // set the allocated context
613 realtime_sem_context* oldContext = atomic_pointer_test_and_set(
614 &team->realtime_sem_context, context, (realtime_sem_context*)NULL);
615 if (oldContext == NULL)
616 return context;
618 // someone else was quicker
619 delete context;
620 return oldContext;
624 static status_t
625 copy_sem_name_to_kernel(const char* userName, KPath& buffer, char*& name)
627 if (userName == NULL)
628 return B_BAD_VALUE;
629 if (!IS_USER_ADDRESS(userName))
630 return B_BAD_ADDRESS;
632 if (buffer.InitCheck() != B_OK)
633 return B_NO_MEMORY;
635 // copy userland path to kernel
636 name = buffer.LockBuffer();
637 ssize_t actualLength = user_strlcpy(name, userName, buffer.BufferSize());
639 if (actualLength < 0)
640 return B_BAD_ADDRESS;
641 if ((size_t)actualLength >= buffer.BufferSize())
642 return ENAMETOOLONG;
644 return B_OK;
648 // #pragma mark - kernel internal
651 void
652 realtime_sem_init()
654 new(&sSemTable) GlobalSemTable;
655 if (sSemTable.Init() != B_OK)
656 panic("realtime_sem_init() failed to init global sem table");
660 void
661 delete_realtime_sem_context(realtime_sem_context* context)
663 delete context;
667 realtime_sem_context*
668 clone_realtime_sem_context(realtime_sem_context* context)
670 if (context == NULL)
671 return NULL;
673 return context->Clone();
677 // #pragma mark - syscalls
680 status_t
681 _user_realtime_sem_open(const char* userName, int openFlagsOrShared,
682 mode_t mode, uint32 semCount, sem_t* userSem, sem_t** _usedUserSem)
684 realtime_sem_context* context = get_current_team_context();
685 if (context == NULL)
686 return B_NO_MEMORY;
688 if (semCount > MAX_POSIX_SEM_VALUE)
689 return B_BAD_VALUE;
691 // userSem must always be given
692 if (userSem == NULL)
693 return B_BAD_VALUE;
694 if (!IS_USER_ADDRESS(userSem))
695 return B_BAD_ADDRESS;
697 // check user pointers
698 if (_usedUserSem == NULL)
699 return B_BAD_VALUE;
700 if (!IS_USER_ADDRESS(_usedUserSem) || !IS_USER_ADDRESS(userName))
701 return B_BAD_ADDRESS;
703 // copy name to kernel
704 KPath nameBuffer(B_PATH_NAME_LENGTH);
705 char* name;
706 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name);
707 if (error != B_OK)
708 return error;
710 // open the semaphore
711 sem_t* usedUserSem;
712 bool created = false;
713 int32_t id;
714 error = context->OpenSem(name, openFlagsOrShared, mode, semCount, userSem,
715 usedUserSem, id, created);
716 if (error != B_OK)
717 return error;
719 // copy results back to userland
720 if (user_memcpy(&userSem->u.named_sem_id, &id, sizeof(int32_t)) != B_OK
721 || user_memcpy(_usedUserSem, &usedUserSem, sizeof(sem_t*)) != B_OK) {
722 if (created)
723 sSemTable.UnlinkNamedSem(name);
724 sem_t* dummy;
725 context->CloseSem(id, dummy);
726 return B_BAD_ADDRESS;
729 return B_OK;
733 status_t
734 _user_realtime_sem_close(sem_id semID, sem_t** _deleteUserSem)
736 if (_deleteUserSem != NULL && !IS_USER_ADDRESS(_deleteUserSem))
737 return B_BAD_ADDRESS;
739 realtime_sem_context* context = get_current_team_context();
740 if (context == NULL)
741 return B_BAD_VALUE;
743 // close sem
744 sem_t* deleteUserSem;
745 status_t error = context->CloseSem(semID, deleteUserSem);
746 if (error != B_OK)
747 return error;
749 // copy back result to userland
750 if (_deleteUserSem != NULL
751 && user_memcpy(_deleteUserSem, &deleteUserSem, sizeof(sem_t*))
752 != B_OK) {
753 return B_BAD_ADDRESS;
756 return B_OK;
760 status_t
761 _user_realtime_sem_unlink(const char* userName)
763 // copy name to kernel
764 KPath nameBuffer(B_PATH_NAME_LENGTH);
765 char* name;
766 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name);
767 if (error != B_OK)
768 return error;
770 return sSemTable.UnlinkNamedSem(name);
774 status_t
775 _user_realtime_sem_get_value(sem_id semID, int* _value)
777 if (_value == NULL)
778 return B_BAD_VALUE;
779 if (!IS_USER_ADDRESS(_value))
780 return B_BAD_ADDRESS;
782 realtime_sem_context* context = get_current_team_context();
783 if (context == NULL)
784 return B_BAD_VALUE;
786 // get sem count
787 int count;
788 status_t error = context->GetSemCount(semID, count);
789 if (error != B_OK)
790 return error;
792 // copy back result to userland
793 if (user_memcpy(_value, &count, sizeof(int)) != B_OK)
794 return B_BAD_ADDRESS;
796 return B_OK;
800 status_t
801 _user_realtime_sem_post(sem_id semID)
803 realtime_sem_context* context = get_current_team_context();
804 if (context == NULL)
805 return B_BAD_VALUE;
807 return context->ReleaseSem(semID);
811 status_t
812 _user_realtime_sem_wait(sem_id semID, bigtime_t timeout)
814 realtime_sem_context* context = get_current_team_context();
815 if (context == NULL)
816 return B_BAD_VALUE;
818 return syscall_restart_handle_post(context->AcquireSem(semID, timeout));