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>
39 if (fSemaphoreID
>= 0)
40 delete_sem(fSemaphoreID
);
43 sem_id
SemaphoreID() const { return fSemaphoreID
; }
45 status_t
Init(int32 semCount
, const char* name
)
47 fSemaphoreID
= create_sem(semCount
, name
);
54 virtual sem_id
ID() const = 0;
55 virtual SemInfo
* Clone() = 0;
56 virtual void Delete() = 0;
63 class NamedSem
: public SemInfo
{
77 const char* Name() const { return fName
; }
79 status_t
Init(const char* name
, mode_t mode
, int32 semCount
)
81 status_t error
= SemInfo::Init(semCount
, name
);
96 void AcquireReference()
98 atomic_add(&fRefCount
, 1);
101 void ReleaseReference()
103 if (atomic_add(&fRefCount
, -1) == 1)
107 bool HasPermissions() const
109 if ((fPermissions
& S_IWOTH
) != 0)
112 uid_t uid
= geteuid();
113 if (uid
== 0 || (uid
== fUID
&& (fPermissions
& S_IWUSR
) != 0))
116 gid_t gid
= getegid();
117 if (gid
== fGID
&& (fPermissions
& S_IWGRP
) != 0)
123 virtual sem_id
ID() const
125 return SemaphoreID();
128 virtual SemInfo
* Clone()
134 virtual void Delete()
139 NamedSem
*& HashLink()
155 struct NamedSemHashDefinition
{
156 typedef const char* KeyType
;
157 typedef NamedSem ValueType
;
159 size_t HashKey(const KeyType
& key
) const
161 return hash_hash_string(key
);
164 size_t Hash(NamedSem
* semaphore
) const
166 return HashKey(semaphore
->Name());
169 bool Compare(const KeyType
& key
, NamedSem
* semaphore
) const
171 return strcmp(key
, semaphore
->Name()) == 0;
174 NamedSem
*& GetLink(NamedSem
* semaphore
) const
176 return semaphore
->HashLink();
181 class GlobalSemTable
{
187 mutex_init(&fLock
, "global named sem table");
192 mutex_destroy(&fLock
);
197 return fNamedSemaphores
.Init();
200 status_t
OpenNamedSem(const char* name
, int openFlags
, mode_t mode
,
201 uint32 semCount
, NamedSem
*& _sem
, bool& _created
)
203 MutexLocker
_(fLock
);
205 NamedSem
* sem
= fNamedSemaphores
.Lookup(name
);
207 if ((openFlags
& O_EXCL
) != 0)
210 if (!sem
->HasPermissions())
213 sem
->AcquireReference();
219 if ((openFlags
& O_CREAT
) == 0)
222 // does not exist yet -- create
223 if (fSemaphoreCount
>= MAX_POSIX_SEMS
)
226 sem
= new(std::nothrow
) NamedSem
;
230 status_t error
= sem
->Init(name
, mode
, semCount
);
236 error
= fNamedSemaphores
.Insert(sem
);
242 // add one reference for the table
243 sem
->AcquireReference();
252 status_t
UnlinkNamedSem(const char* name
)
254 MutexLocker
_(fLock
);
256 NamedSem
* sem
= fNamedSemaphores
.Lookup(name
);
260 if (!sem
->HasPermissions())
263 fNamedSemaphores
.Remove(sem
);
264 sem
->ReleaseReference();
265 // release the table reference
272 typedef BOpenHashTable
<NamedSemHashDefinition
, true> NamedSemTable
;
275 NamedSemTable fNamedSemaphores
;
276 int32 fSemaphoreCount
;
280 static GlobalSemTable sSemTable
;
285 TeamSemInfo(SemInfo
* semaphore
, sem_t
* userSem
)
287 fSemaphore(semaphore
),
288 fUserSemaphore(userSem
),
295 if (fSemaphore
!= NULL
)
296 fSemaphore
->Delete();
299 sem_id
ID() const { return fSemaphore
->ID(); }
300 sem_id
SemaphoreID() const { return fSemaphore
->SemaphoreID(); }
301 sem_t
* UserSemaphore() const { return fUserSemaphore
; }
310 return --fOpenCount
== 0;
313 TeamSemInfo
* Clone() const
315 SemInfo
* sem
= fSemaphore
->Clone();
319 TeamSemInfo
* clone
= new(std::nothrow
) TeamSemInfo(sem
, fUserSemaphore
);
325 clone
->fOpenCount
= fOpenCount
;
330 TeamSemInfo
*& HashLink()
337 sem_t
* fUserSemaphore
;
340 TeamSemInfo
* fHashLink
;
344 struct TeamSemHashDefinition
{
345 typedef sem_id KeyType
;
346 typedef TeamSemInfo ValueType
;
348 size_t HashKey(const KeyType
& key
) const
353 size_t Hash(TeamSemInfo
* semaphore
) const
355 return HashKey(semaphore
->ID());
358 bool Compare(const KeyType
& key
, TeamSemInfo
* semaphore
) const
360 return key
== semaphore
->ID();
363 TeamSemInfo
*& GetLink(TeamSemInfo
* semaphore
) const
365 return semaphore
->HashLink();
372 struct realtime_sem_context
{
373 realtime_sem_context()
377 mutex_init(&fLock
, "realtime sem context");
380 ~realtime_sem_context()
384 // delete all semaphores.
385 SemTable::Iterator it
= fSemaphores
.GetIterator();
386 while (TeamSemInfo
* sem
= it
.Next()) {
387 // Note, this uses internal knowledge about how the iterator works.
388 // Ugly, but there's no good alternative.
389 fSemaphores
.RemoveUnchecked(sem
);
393 mutex_destroy(&fLock
);
398 fNextPrivateSemID
= -1;
399 return fSemaphores
.Init();
402 realtime_sem_context
* Clone()
404 // create new context
405 realtime_sem_context
* context
= new(std::nothrow
) realtime_sem_context
;
408 ObjectDeleter
<realtime_sem_context
> contextDeleter(context
);
410 MutexLocker
_(fLock
);
412 context
->fNextPrivateSemID
= fNextPrivateSemID
;
414 // clone all semaphores
415 SemTable::Iterator it
= fSemaphores
.GetIterator();
416 while (TeamSemInfo
* sem
= it
.Next()) {
417 TeamSemInfo
* clonedSem
= sem
->Clone();
418 if (clonedSem
== NULL
)
421 if (context
->fSemaphores
.Insert(clonedSem
) != B_OK
) {
425 context
->fSemaphoreCount
++;
428 contextDeleter
.Detach();
432 status_t
OpenSem(const char* name
, int openFlags
, mode_t mode
,
433 uint32 semCount
, sem_t
* userSem
, sem_t
*& _usedUserSem
, int32_t& _id
,
436 NamedSem
* sem
= NULL
;
437 status_t error
= sSemTable
.OpenNamedSem(name
, openFlags
, mode
, semCount
,
442 MutexLocker
_(fLock
);
444 TeamSemInfo
* teamSem
= fSemaphores
.Lookup(sem
->ID());
445 if (teamSem
!= NULL
) {
446 // already open -- just increment the open count
448 sem
->ReleaseReference();
449 _usedUserSem
= teamSem
->UserSemaphore();
454 // not open yet -- create a new team sem
456 // first check the semaphore limit, though
457 if (fSemaphoreCount
>= MAX_POSIX_SEMS_PER_TEAM
) {
458 sem
->ReleaseReference();
460 sSemTable
.UnlinkNamedSem(name
);
464 teamSem
= new(std::nothrow
) TeamSemInfo(sem
, userSem
);
465 if (teamSem
== NULL
) {
466 sem
->ReleaseReference();
468 sSemTable
.UnlinkNamedSem(name
);
472 error
= fSemaphores
.Insert(teamSem
);
476 sSemTable
.UnlinkNamedSem(name
);
482 _usedUserSem
= teamSem
->UserSemaphore();
488 status_t
CloseSem(sem_id id
, sem_t
*& deleteUserSem
)
490 deleteUserSem
= NULL
;
492 MutexLocker
_(fLock
);
494 TeamSemInfo
* sem
= fSemaphores
.Lookup(id
);
499 // last reference closed
500 fSemaphores
.Remove(sem
);
502 deleteUserSem
= sem
->UserSemaphore();
509 status_t
AcquireSem(sem_id id
, bigtime_t timeout
)
511 MutexLocker
locker(fLock
);
513 TeamSemInfo
* sem
= fSemaphores
.Lookup(id
);
517 id
= sem
->SemaphoreID();
523 error
= acquire_sem_etc(id
, 1, B_CAN_INTERRUPT
| B_RELATIVE_TIMEOUT
,
525 } else if (timeout
== B_INFINITE_TIMEOUT
) {
526 error
= acquire_sem_etc(id
, 1, B_CAN_INTERRUPT
, 0);
528 error
= acquire_sem_etc(id
, 1,
529 B_CAN_INTERRUPT
| B_ABSOLUTE_REAL_TIME_TIMEOUT
, timeout
);
532 return error
== B_BAD_SEM_ID
? B_BAD_VALUE
: error
;
535 status_t
ReleaseSem(sem_id id
)
537 MutexLocker
locker(fLock
);
539 TeamSemInfo
* sem
= fSemaphores
.Lookup(id
);
543 id
= sem
->SemaphoreID();
547 status_t error
= release_sem(id
);
548 return error
== B_BAD_SEM_ID
? B_BAD_VALUE
: error
;
551 status_t
GetSemCount(sem_id id
, int& _count
)
553 MutexLocker
locker(fLock
);
555 TeamSemInfo
* sem
= fSemaphores
.Lookup(id
);
559 id
= sem
->SemaphoreID();
564 status_t error
= get_sem_count(id
, &count
);
573 sem_id
_NextPrivateSemID()
576 if (fNextPrivateSemID
>= 0)
577 fNextPrivateSemID
= -1;
579 sem_id id
= fNextPrivateSemID
--;
580 if (fSemaphores
.Lookup(id
) == NULL
)
586 typedef BOpenHashTable
<TeamSemHashDefinition
, true> SemTable
;
589 SemTable fSemaphores
;
590 int32 fSemaphoreCount
;
591 sem_id fNextPrivateSemID
;
595 // #pragma mark - implementation private
598 static realtime_sem_context
*
599 get_current_team_context()
601 Team
* team
= thread_get_current_thread()->team
;
604 realtime_sem_context
* context
= atomic_pointer_get(
605 &team
->realtime_sem_context
);
609 // no context yet -- create a new one
610 context
= new(std::nothrow
) realtime_sem_context
;
611 if (context
== NULL
|| context
->Init() != B_OK
) {
616 // set the allocated context
617 realtime_sem_context
* oldContext
= atomic_pointer_test_and_set(
618 &team
->realtime_sem_context
, context
, (realtime_sem_context
*)NULL
);
619 if (oldContext
== NULL
)
622 // someone else was quicker
629 copy_sem_name_to_kernel(const char* userName
, KPath
& buffer
, char*& name
)
631 if (userName
== NULL
)
633 if (!IS_USER_ADDRESS(userName
))
634 return B_BAD_ADDRESS
;
636 if (buffer
.InitCheck() != B_OK
)
639 // copy userland path to kernel
640 name
= buffer
.LockBuffer();
641 ssize_t actualLength
= user_strlcpy(name
, userName
, buffer
.BufferSize());
643 if (actualLength
< 0)
644 return B_BAD_ADDRESS
;
645 if ((size_t)actualLength
>= buffer
.BufferSize())
652 // #pragma mark - kernel internal
658 new(&sSemTable
) GlobalSemTable
;
659 if (sSemTable
.Init() != B_OK
)
660 panic("realtime_sem_init() failed to init global sem table");
665 delete_realtime_sem_context(realtime_sem_context
* context
)
671 realtime_sem_context
*
672 clone_realtime_sem_context(realtime_sem_context
* context
)
677 return context
->Clone();
681 // #pragma mark - syscalls
685 _user_realtime_sem_open(const char* userName
, int openFlagsOrShared
,
686 mode_t mode
, uint32 semCount
, sem_t
* userSem
, sem_t
** _usedUserSem
)
688 realtime_sem_context
* context
= get_current_team_context();
692 if (semCount
> MAX_POSIX_SEM_VALUE
)
695 // userSem must always be given
698 if (!IS_USER_ADDRESS(userSem
))
699 return B_BAD_ADDRESS
;
701 // check user pointers
702 if (_usedUserSem
== NULL
)
704 if (!IS_USER_ADDRESS(_usedUserSem
) || !IS_USER_ADDRESS(userName
))
705 return B_BAD_ADDRESS
;
707 // copy name to kernel
708 KPath
nameBuffer(B_PATH_NAME_LENGTH
);
710 status_t error
= copy_sem_name_to_kernel(userName
, nameBuffer
, name
);
714 // open the semaphore
716 bool created
= false;
718 error
= context
->OpenSem(name
, openFlagsOrShared
, mode
, semCount
, userSem
,
719 usedUserSem
, id
, created
);
723 // copy results back to userland
724 if (user_memcpy(&userSem
->u
.named_sem_id
, &id
, sizeof(int32_t)) != B_OK
725 || user_memcpy(_usedUserSem
, &usedUserSem
, sizeof(sem_t
*)) != B_OK
) {
727 sSemTable
.UnlinkNamedSem(name
);
729 context
->CloseSem(id
, dummy
);
730 return B_BAD_ADDRESS
;
738 _user_realtime_sem_close(sem_id semID
, sem_t
** _deleteUserSem
)
740 if (_deleteUserSem
!= NULL
&& !IS_USER_ADDRESS(_deleteUserSem
))
741 return B_BAD_ADDRESS
;
743 realtime_sem_context
* context
= get_current_team_context();
748 sem_t
* deleteUserSem
;
749 status_t error
= context
->CloseSem(semID
, deleteUserSem
);
753 // copy back result to userland
754 if (_deleteUserSem
!= NULL
755 && user_memcpy(_deleteUserSem
, &deleteUserSem
, sizeof(sem_t
*))
757 return B_BAD_ADDRESS
;
765 _user_realtime_sem_unlink(const char* userName
)
767 // copy name to kernel
768 KPath
nameBuffer(B_PATH_NAME_LENGTH
);
770 status_t error
= copy_sem_name_to_kernel(userName
, nameBuffer
, name
);
774 return sSemTable
.UnlinkNamedSem(name
);
779 _user_realtime_sem_get_value(sem_id semID
, int* _value
)
783 if (!IS_USER_ADDRESS(_value
))
784 return B_BAD_ADDRESS
;
786 realtime_sem_context
* context
= get_current_team_context();
792 status_t error
= context
->GetSemCount(semID
, count
);
796 // copy back result to userland
797 if (user_memcpy(_value
, &count
, sizeof(int)) != B_OK
)
798 return B_BAD_ADDRESS
;
805 _user_realtime_sem_post(sem_id semID
)
807 realtime_sem_context
* context
= get_current_team_context();
811 return context
->ReleaseSem(semID
);
816 _user_realtime_sem_wait(sem_id semID
, bigtime_t timeout
)
818 realtime_sem_context
* context
= get_current_team_context();
822 return syscall_restart_handle_post(context
->AcquireSem(semID
, timeout
));