2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
6 #include <posix/realtime_sem.h>
14 #include <AutoDeleter.h>
18 #include <syscall_restart.h>
21 #include <util/atomic.h>
22 #include <util/AutoLock.h>
23 #include <util/OpenHashTable.h>
24 #include <util/StringHash.h>
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
);
52 virtual sem_id
ID() const = 0;
53 virtual SemInfo
* Clone() = 0;
54 virtual void Delete() = 0;
61 class NamedSem
: public SemInfo
{
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
);
94 void AcquireReference()
96 atomic_add(&fRefCount
, 1);
99 void ReleaseReference()
101 if (atomic_add(&fRefCount
, -1) == 1)
105 bool HasPermissions() const
107 if ((fPermissions
& S_IWOTH
) != 0)
110 uid_t uid
= geteuid();
111 if (uid
== 0 || (uid
== fUID
&& (fPermissions
& S_IWUSR
) != 0))
114 gid_t gid
= getegid();
115 if (gid
== fGID
&& (fPermissions
& S_IWGRP
) != 0)
121 virtual sem_id
ID() const
123 return SemaphoreID();
126 virtual SemInfo
* Clone()
132 virtual void Delete()
137 NamedSem
*& HashLink()
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
{
185 mutex_init(&fLock
, "global named sem table");
190 mutex_destroy(&fLock
);
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
);
205 if ((openFlags
& O_EXCL
) != 0)
208 if (!sem
->HasPermissions())
211 sem
->AcquireReference();
217 if ((openFlags
& O_CREAT
) == 0)
220 // does not exist yet -- create
221 if (fSemaphoreCount
>= MAX_POSIX_SEMS
)
224 sem
= new(std::nothrow
) NamedSem
;
228 status_t error
= sem
->Init(name
, mode
, semCount
);
234 error
= fNamedSemaphores
.Insert(sem
);
240 // add one reference for the table
241 sem
->AcquireReference();
250 status_t
UnlinkNamedSem(const char* name
)
252 MutexLocker
_(fLock
);
254 NamedSem
* sem
= fNamedSemaphores
.Lookup(name
);
258 if (!sem
->HasPermissions())
261 fNamedSemaphores
.Remove(sem
);
262 sem
->ReleaseReference();
263 // release the table reference
270 typedef BOpenHashTable
<NamedSemHashDefinition
, true> NamedSemTable
;
273 NamedSemTable fNamedSemaphores
;
274 int32 fSemaphoreCount
;
278 static GlobalSemTable sSemTable
;
283 TeamSemInfo(SemInfo
* semaphore
, sem_t
* userSem
)
285 fSemaphore(semaphore
),
286 fUserSemaphore(userSem
),
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
; }
308 return --fOpenCount
== 0;
311 TeamSemInfo
* Clone() const
313 SemInfo
* sem
= fSemaphore
->Clone();
317 TeamSemInfo
* clone
= new(std::nothrow
) TeamSemInfo(sem
, fUserSemaphore
);
323 clone
->fOpenCount
= fOpenCount
;
328 TeamSemInfo
*& HashLink()
335 sem_t
* fUserSemaphore
;
338 TeamSemInfo
* fHashLink
;
342 struct TeamSemHashDefinition
{
343 typedef sem_id KeyType
;
344 typedef TeamSemInfo ValueType
;
346 size_t HashKey(const KeyType
& key
) const
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()
373 mutex_init(&fLock
, "realtime sem context");
376 ~realtime_sem_context()
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
);
389 mutex_destroy(&fLock
);
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
;
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
)
417 if (context
->fSemaphores
.Insert(clonedSem
) != B_OK
) {
421 context
->fSemaphoreCount
++;
424 contextDeleter
.Detach();
428 status_t
OpenSem(const char* name
, int openFlags
, mode_t mode
,
429 uint32 semCount
, sem_t
* userSem
, sem_t
*& _usedUserSem
, int32_t& _id
,
432 NamedSem
* sem
= NULL
;
433 status_t error
= sSemTable
.OpenNamedSem(name
, openFlags
, mode
, semCount
,
438 MutexLocker
_(fLock
);
440 TeamSemInfo
* teamSem
= fSemaphores
.Lookup(sem
->ID());
441 if (teamSem
!= NULL
) {
442 // already open -- just increment the open count
444 sem
->ReleaseReference();
445 _usedUserSem
= teamSem
->UserSemaphore();
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();
456 sSemTable
.UnlinkNamedSem(name
);
460 teamSem
= new(std::nothrow
) TeamSemInfo(sem
, userSem
);
461 if (teamSem
== NULL
) {
462 sem
->ReleaseReference();
464 sSemTable
.UnlinkNamedSem(name
);
468 error
= fSemaphores
.Insert(teamSem
);
472 sSemTable
.UnlinkNamedSem(name
);
478 _usedUserSem
= teamSem
->UserSemaphore();
484 status_t
CloseSem(sem_id id
, sem_t
*& deleteUserSem
)
486 deleteUserSem
= NULL
;
488 MutexLocker
_(fLock
);
490 TeamSemInfo
* sem
= fSemaphores
.Lookup(id
);
495 // last reference closed
496 fSemaphores
.Remove(sem
);
498 deleteUserSem
= sem
->UserSemaphore();
505 status_t
AcquireSem(sem_id id
, bigtime_t timeout
)
507 MutexLocker
locker(fLock
);
509 TeamSemInfo
* sem
= fSemaphores
.Lookup(id
);
513 id
= sem
->SemaphoreID();
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);
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
);
539 id
= sem
->SemaphoreID();
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
);
555 id
= sem
->SemaphoreID();
560 status_t error
= get_sem_count(id
, &count
);
569 sem_id
_NextPrivateSemID()
572 if (fNextPrivateSemID
>= 0)
573 fNextPrivateSemID
= -1;
575 sem_id id
= fNextPrivateSemID
--;
576 if (fSemaphores
.Lookup(id
) == NULL
)
582 typedef BOpenHashTable
<TeamSemHashDefinition
, true> SemTable
;
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
;
600 realtime_sem_context
* context
= atomic_pointer_get(
601 &team
->realtime_sem_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
) {
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
)
618 // someone else was quicker
625 copy_sem_name_to_kernel(const char* userName
, KPath
& buffer
, char*& name
)
627 if (userName
== NULL
)
629 if (!IS_USER_ADDRESS(userName
))
630 return B_BAD_ADDRESS
;
632 if (buffer
.InitCheck() != B_OK
)
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())
648 // #pragma mark - kernel internal
654 new(&sSemTable
) GlobalSemTable
;
655 if (sSemTable
.Init() != B_OK
)
656 panic("realtime_sem_init() failed to init global sem table");
661 delete_realtime_sem_context(realtime_sem_context
* context
)
667 realtime_sem_context
*
668 clone_realtime_sem_context(realtime_sem_context
* context
)
673 return context
->Clone();
677 // #pragma mark - syscalls
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();
688 if (semCount
> MAX_POSIX_SEM_VALUE
)
691 // userSem must always be given
694 if (!IS_USER_ADDRESS(userSem
))
695 return B_BAD_ADDRESS
;
697 // check user pointers
698 if (_usedUserSem
== NULL
)
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
);
706 status_t error
= copy_sem_name_to_kernel(userName
, nameBuffer
, name
);
710 // open the semaphore
712 bool created
= false;
714 error
= context
->OpenSem(name
, openFlagsOrShared
, mode
, semCount
, userSem
,
715 usedUserSem
, id
, created
);
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
) {
723 sSemTable
.UnlinkNamedSem(name
);
725 context
->CloseSem(id
, dummy
);
726 return B_BAD_ADDRESS
;
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();
744 sem_t
* deleteUserSem
;
745 status_t error
= context
->CloseSem(semID
, deleteUserSem
);
749 // copy back result to userland
750 if (_deleteUserSem
!= NULL
751 && user_memcpy(_deleteUserSem
, &deleteUserSem
, sizeof(sem_t
*))
753 return B_BAD_ADDRESS
;
761 _user_realtime_sem_unlink(const char* userName
)
763 // copy name to kernel
764 KPath
nameBuffer(B_PATH_NAME_LENGTH
);
766 status_t error
= copy_sem_name_to_kernel(userName
, nameBuffer
, name
);
770 return sSemTable
.UnlinkNamedSem(name
);
775 _user_realtime_sem_get_value(sem_id semID
, int* _value
)
779 if (!IS_USER_ADDRESS(_value
))
780 return B_BAD_ADDRESS
;
782 realtime_sem_context
* context
= get_current_team_context();
788 status_t error
= context
->GetSemCount(semID
, count
);
792 // copy back result to userland
793 if (user_memcpy(_value
, &count
, sizeof(int)) != B_OK
)
794 return B_BAD_ADDRESS
;
801 _user_realtime_sem_post(sem_id semID
)
803 realtime_sem_context
* context
= get_current_team_context();
807 return context
->ReleaseSem(semID
);
812 _user_realtime_sem_wait(sem_id semID
, bigtime_t timeout
)
814 realtime_sem_context
* context
= get_current_team_context();
818 return syscall_restart_handle_post(context
->AcquireSem(semID
, timeout
));