2 * Copyright 2008-2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Salvatore Benedetto <salvatore.benedetto@gmail.com>
9 #include <posix/xsi_semaphore.h>
14 #include <sys/types.h>
19 #include <syscall_restart.h>
21 #include <util/atomic.h>
22 #include <util/AutoLock.h>
23 #include <util/DoublyLinkedList.h>
24 #include <util/OpenHashTable.h>
25 #include <AutoDeleter.h>
28 //#define TRACE_XSI_SEM
30 # define TRACE(x) dprintf x
31 # define TRACE_ERROR(x) dprintf x
33 # define TRACE(x) /* nothing */
34 # define TRACE_ERROR(x) dprintf x
40 // Queue for holding blocked threads
41 struct queued_thread
: DoublyLinkedListLinkImpl
<queued_thread
> {
42 queued_thread(Thread
*thread
, int32 count
)
55 typedef DoublyLinkedList
<queued_thread
> ThreadQueue
;
57 class XsiSemaphoreSet
;
59 struct sem_undo
: DoublyLinkedListLinkImpl
<sem_undo
> {
60 sem_undo(XsiSemaphoreSet
*semaphoreSet
, Team
*team
, int16
*undoValues
)
62 semaphore_set(semaphoreSet
),
64 undo_values(undoValues
)
68 DoublyLinkedListLink
<sem_undo
> team_link
;
69 XsiSemaphoreSet
*semaphore_set
;
74 typedef DoublyLinkedList
<sem_undo
> UndoList
;
75 typedef DoublyLinkedList
<sem_undo
,
76 DoublyLinkedListMemberGetLink
<sem_undo
, &sem_undo::team_link
> > TeamList
;
81 // Forward declared in global namespace.
82 struct xsi_sem_context
{
85 mutex_init(&lock
, "Private team undo_list lock");
100 // Xsi semaphore definition
104 : fLastPidOperation(0),
105 fThreadsWaitingToIncrease(0),
106 fThreadsWaitingToBeZero(0),
113 // For some reason the semaphore is getting destroyed.
114 // Wake up any remaing awaiting threads
115 while (queued_thread
*entry
= fWaitingToIncreaseQueue
.RemoveHead()) {
116 entry
->queued
= false;
117 thread_unblock(entry
->thread
, EIDRM
);
119 while (queued_thread
*entry
= fWaitingToBeZeroQueue
.RemoveHead()) {
120 entry
->queued
= false;
121 thread_unblock(entry
->thread
, EIDRM
);
123 // No need to remove any sem_undo request still
124 // hanging. When the process exit and doesn't found
125 // the semaphore set, it'll just ignore the sem_undo
126 // request. That's better than iterating trough the
127 // whole sUndoList. Beside we don't know our semaphore
128 // number nor our semaphore set id.
131 // We return true in case the operation causes the
132 // caller to wait, so it can undo all the operations
134 bool Add(short value
)
136 if ((int)(fValue
+ value
) < 0) {
137 TRACE(("XsiSemaphore::Add: potentially going to sleep\n"));
141 if (fValue
== 0 && fThreadsWaitingToBeZero
> 0)
143 else if (fValue
> 0 && fThreadsWaitingToIncrease
> 0)
149 status_t
BlockAndUnlock(Thread
*thread
, MutexLocker
*setLocker
)
151 thread_prepare_to_block(thread
, B_CAN_INTERRUPT
,
152 THREAD_BLOCK_TYPE_OTHER
, (void*)"xsi semaphore");
153 // Unlock the set before blocking
156 // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to
157 // interruption, we will still be queued. A WakeUpThread() at this point will
158 // call thread_unblock() and might thus screw with our trying to re-lock the
160 return thread_block();
163 void Deque(queued_thread
*queueEntry
, bool waitForZero
)
165 if (queueEntry
->queued
) {
167 fWaitingToBeZeroQueue
.Remove(queueEntry
);
168 fThreadsWaitingToBeZero
--;
170 fWaitingToIncreaseQueue
.Remove(queueEntry
);
171 fThreadsWaitingToIncrease
--;
176 void Enqueue(queued_thread
*queueEntry
, bool waitForZero
)
179 fWaitingToBeZeroQueue
.Add(queueEntry
);
180 fThreadsWaitingToBeZero
++;
182 fWaitingToIncreaseQueue
.Add(queueEntry
);
183 fThreadsWaitingToIncrease
++;
185 queueEntry
->queued
= true;
188 pid_t
LastPid() const
190 return fLastPidOperation
;
193 void Revert(short value
)
196 if (fValue
== 0 && fThreadsWaitingToBeZero
> 0)
198 else if (fValue
> 0 && fThreadsWaitingToIncrease
> 0)
202 void SetPid(pid_t pid
)
204 fLastPidOperation
= pid
;
207 void SetValue(ushort value
)
212 ushort
ThreadsWaitingToIncrease() const
214 return fThreadsWaitingToIncrease
;
217 ushort
ThreadsWaitingToBeZero() const
219 return fThreadsWaitingToBeZero
;
227 void WakeUpThread(bool waitingForZero
)
229 if (waitingForZero
) {
230 // Wake up all threads waiting on zero
231 while (queued_thread
*entry
= fWaitingToBeZeroQueue
.RemoveHead()) {
232 entry
->queued
= false;
233 fThreadsWaitingToBeZero
--;
234 thread_unblock(entry
->thread
, 0);
237 // Wake up all threads even though they might go back to sleep
238 while (queued_thread
*entry
= fWaitingToIncreaseQueue
.RemoveHead()) {
239 entry
->queued
= false;
240 fThreadsWaitingToIncrease
--;
241 thread_unblock(entry
->thread
, 0);
247 pid_t fLastPidOperation
; // sempid
248 ushort fThreadsWaitingToIncrease
; // semncnt
249 ushort fThreadsWaitingToBeZero
; // semzcnt
250 ushort fValue
; // semval
252 ThreadQueue fWaitingToIncreaseQueue
;
253 ThreadQueue fWaitingToBeZeroQueue
;
256 #define MAX_XSI_SEMS_PER_TEAM 128
258 // Xsi semaphore set definition (semid_ds)
259 class XsiSemaphoreSet
{
261 XsiSemaphoreSet(int numberOfSemaphores
, int flags
)
263 fLastSemctlTime((time_t)real_time_clock()),
265 fNumberOfSemaphores(numberOfSemaphores
),
268 mutex_init(&fLock
, "XsiSemaphoreSet private mutex");
269 SetIpcKey((key_t
)-1);
270 SetPermissions(flags
);
271 fSemaphores
= new(std::nothrow
) XsiSemaphore
[numberOfSemaphores
];
272 if (fSemaphores
== NULL
) {
273 TRACE_ERROR(("XsiSemaphoreSet::XsiSemaphore(): failed to allocate "
274 "XsiSemaphore object\n"));
281 TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore "
283 mutex_destroy(&fLock
);
284 delete[] fSemaphores
;
287 void ClearUndo(unsigned short semaphoreNumber
)
289 Team
*team
= thread_get_current_thread()->team
;
290 UndoList::Iterator iterator
= fUndoList
.GetIterator();
291 while (iterator
.HasNext()) {
292 struct sem_undo
*current
= iterator
.Next();
293 if (current
->team
== team
) {
294 TRACE(("XsiSemaphoreSet::ClearUndo: teamID = %d, "
295 "semaphoreSetID = %d, semaphoreNumber = %d\n",
296 fID
, semaphoreNumber
, (int)team
->id
));
297 MutexLocker
_(team
->xsi_sem_context
->lock
);
298 current
->undo_values
[semaphoreNumber
] = 0;
306 // Clear all undo_values (POSIX semadj equivalent)
307 // of the calling team. This happens only on semctl SETALL.
308 Team
*team
= thread_get_current_thread()->team
;
309 DoublyLinkedList
<sem_undo
>::Iterator iterator
= fUndoList
.GetIterator();
310 while (iterator
.HasNext()) {
311 struct sem_undo
*current
= iterator
.Next();
312 if (current
->team
== team
) {
313 TRACE(("XsiSemaphoreSet::ClearUndos: teamID = %d, "
314 "semaphoreSetID = %d\n", (int)team
->id
, fID
));
315 MutexLocker
_(team
->xsi_sem_context
->lock
);
316 memset(current
->undo_values
, 0,
317 sizeof(int16
) * fNumberOfSemaphores
);
323 void DoIpcSet(struct semid_ds
*result
)
325 fPermissions
.uid
= result
->sem_perm
.uid
;
326 fPermissions
.gid
= result
->sem_perm
.gid
;
327 fPermissions
.mode
= (fPermissions
.mode
& ~0x01ff)
328 | (result
->sem_perm
.mode
& 0x01ff);
331 bool HasPermission() const
333 if ((fPermissions
.mode
& S_IWOTH
) != 0)
336 uid_t uid
= geteuid();
337 if (uid
== 0 || (uid
== fPermissions
.uid
338 && (fPermissions
.mode
& S_IWUSR
) != 0))
341 gid_t gid
= getegid();
342 if (gid
== fPermissions
.gid
&& (fPermissions
.mode
& S_IWGRP
) != 0)
348 bool HasReadPermission() const
351 return HasPermission();
366 return fPermissions
.key
;
369 struct ipc_perm
IpcPermission() const
374 time_t LastSemctlTime() const
376 return fLastSemctlTime
;
379 time_t LastSemopTime() const
381 return fLastSemopTime
;
389 ushort
NumberOfSemaphores() const
391 return fNumberOfSemaphores
;
394 // Record the sem_undo operation into our private fUndoList and
395 // the team undo_list. The only limit here is the memory needed
396 // for creating a new sem_undo structure.
397 int RecordUndo(short semaphoreNumber
, short value
)
399 // Look if there is already a record from the team caller
400 // for the same semaphore set
401 bool notFound
= true;
402 Team
*team
= thread_get_current_thread()->team
;
403 DoublyLinkedList
<sem_undo
>::Iterator iterator
= fUndoList
.GetIterator();
404 while (iterator
.HasNext()) {
405 struct sem_undo
*current
= iterator
.Next();
406 if (current
->team
== team
) {
407 // Update its undo value
408 MutexLocker
_(team
->xsi_sem_context
->lock
);
409 int newValue
= current
->undo_values
[semaphoreNumber
] + value
;
410 if (newValue
> USHRT_MAX
|| newValue
< -USHRT_MAX
) {
411 TRACE_ERROR(("XsiSemaphoreSet::RecordUndo: newValue %d "
412 "out of range\n", newValue
));
415 current
->undo_values
[semaphoreNumber
] = newValue
;
417 TRACE(("XsiSemaphoreSet::RecordUndo: found record. Team = %d, "
418 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n",
419 (int)team
->id
, fID
, semaphoreNumber
,
420 current
->undo_values
[semaphoreNumber
]));
426 // First sem_undo request from this team for this
429 = (int16
*)malloc(sizeof(int16
) * fNumberOfSemaphores
);
430 if (undoValues
== NULL
)
432 struct sem_undo
*request
433 = new(std::nothrow
) sem_undo(this, team
, undoValues
);
434 if (request
== NULL
) {
438 memset(request
->undo_values
, 0, sizeof(int16
) * fNumberOfSemaphores
);
439 request
->undo_values
[semaphoreNumber
] = value
;
441 // Check if it's the very first sem_undo request for this team
442 xsi_sem_context
*context
= atomic_pointer_get(&team
->xsi_sem_context
);
443 if (context
== NULL
) {
444 // Create the context
445 context
= new(std::nothrow
) xsi_sem_context
;
446 if (context
== NULL
) {
447 free(request
->undo_values
);
451 // Since we don't hold any global lock, someone
452 // else could have been quicker than us, so we have
453 // to delete the one we just created and use the one
455 if (atomic_pointer_test_and_set(&team
->xsi_sem_context
, context
,
456 (xsi_sem_context
*)NULL
) != NULL
)
460 // Add the request to both XsiSemaphoreSet and team list
461 fUndoList
.Add(request
);
462 MutexLocker
_(team
->xsi_sem_context
->lock
);
463 team
->xsi_sem_context
->undo_list
.Add(request
);
464 TRACE(("XsiSemaphoreSet::RecordUndo: new record added. Team = %d, "
465 "semaphoreSetID = %d, semaphoreNumber = %d, value = %d\n",
466 (int)team
->id
, fID
, semaphoreNumber
, value
));
471 void RevertUndo(short semaphoreNumber
, short value
)
473 // This can be called only when RecordUndo fails.
474 Team
*team
= thread_get_current_thread()->team
;
475 DoublyLinkedList
<sem_undo
>::Iterator iterator
= fUndoList
.GetIterator();
476 while (iterator
.HasNext()) {
477 struct sem_undo
*current
= iterator
.Next();
478 if (current
->team
== team
) {
479 MutexLocker
_(team
->xsi_sem_context
->lock
);
480 fSemaphores
[semaphoreNumber
].Revert(value
);
486 XsiSemaphore
* Semaphore(int nth
) const
488 return &fSemaphores
[nth
];
491 uint32
SequenceNumber() const
493 return fSequenceNumber
;
496 // Implemented after sGlobalSequenceNumber is declared
499 void SetIpcKey(key_t key
)
501 fPermissions
.key
= key
;
504 void SetLastSemctlTime()
506 fLastSemctlTime
= real_time_clock();
509 void SetLastSemopTime()
511 fLastSemopTime
= real_time_clock();
514 void SetPermissions(int flags
)
516 fPermissions
.uid
= fPermissions
.cuid
= geteuid();
517 fPermissions
.gid
= fPermissions
.cgid
= getegid();
518 fPermissions
.mode
= (flags
& 0x01ff);
521 UndoList
&GetUndoList()
526 XsiSemaphoreSet
*& Link()
532 int fID
; // semaphore set id
534 time_t fLastSemctlTime
; // sem_ctime
535 time_t fLastSemopTime
; // sem_otime
536 mutex fLock
; // private lock
537 ushort fNumberOfSemaphores
; // sem_nsems
538 struct ipc_perm fPermissions
; // sem_perm
539 XsiSemaphore
*fSemaphores
; // array of semaphores
540 uint32 fSequenceNumber
; // used as a second id
541 UndoList fUndoList
; // undo list requests
543 XsiSemaphoreSet
* fLink
;
546 // Xsi semaphore set hash table
547 struct SemaphoreHashTableDefinition
{
549 typedef XsiSemaphoreSet ValueType
;
551 size_t HashKey (const int key
) const
556 size_t Hash(XsiSemaphoreSet
*variable
) const
558 return (size_t)variable
->ID();
561 bool Compare(const int key
, XsiSemaphoreSet
*variable
) const
563 return (int)key
== (int)variable
->ID();
566 XsiSemaphoreSet
*& GetLink(XsiSemaphoreSet
*variable
) const
568 return variable
->Link();
587 int SemaphoreSetID() const
589 return fSemaphoreSetId
;
592 void SetSemaphoreSetID(XsiSemaphoreSet
*semaphoreSet
)
594 fSemaphoreSetId
= semaphoreSet
->ID();
609 struct IpcHashTableDefinition
{
610 typedef key_t KeyType
;
611 typedef Ipc ValueType
;
613 size_t HashKey (const key_t key
) const
615 return (size_t)(key
);
618 size_t Hash(Ipc
*variable
) const
620 return (size_t)HashKey(variable
->Key());
623 bool Compare(const key_t key
, Ipc
*variable
) const
625 return (key_t
)key
== (key_t
)variable
->Key();
628 Ipc
*& GetLink(Ipc
*variable
) const
630 return variable
->Link();
638 #define MAX_XSI_SEMAPHORE 4096
639 #define MAX_XSI_SEMAPHORE_SET 2048
640 static BOpenHashTable
<IpcHashTableDefinition
> sIpcHashTable
;
641 static BOpenHashTable
<SemaphoreHashTableDefinition
> sSemaphoreHashTable
;
643 static mutex sIpcLock
;
644 static mutex sXsiSemaphoreSetLock
;
646 static uint32 sGlobalSequenceNumber
= 1;
647 static int32 sXsiSemaphoreCount
= 0;
648 static int32 sXsiSemaphoreSetCount
= 0;
655 XsiSemaphoreSet::SetID()
657 fID
= real_time_clock();
658 // The lock is held before calling us
660 if (sSemaphoreHashTable
.Lookup(fID
) == NULL
)
662 fID
= (fID
+ 1) % INT_MAX
;
664 sGlobalSequenceNumber
= (sGlobalSequenceNumber
+ 1) % UINT_MAX
;
665 fSequenceNumber
= sGlobalSequenceNumber
;
669 // #pragma mark - Kernel exported API
675 // Initialize hash tables
676 status_t status
= sIpcHashTable
.Init();
678 panic("xsi_sem_init() failed to initialize ipc hash table\n");
679 status
= sSemaphoreHashTable
.Init();
681 panic("xsi_sem_init() failed to initialize semaphore hash table\n");
683 mutex_init(&sIpcLock
, "global POSIX semaphore IPC table");
684 mutex_init(&sXsiSemaphoreSetLock
, "global POSIX xsi sem table");
688 /*! Function called on team exit to process any sem_undo requests */
690 xsi_sem_undo(Team
*team
)
692 if (team
->xsi_sem_context
== NULL
)
695 // By acquiring first the semaphore hash table lock
696 // we make sure the semaphore set in our sem_undo
697 // list won't get removed by IPC_RMID call
698 MutexLocker
_(sXsiSemaphoreSetLock
);
700 // Process all sem_undo request in the team sem undo list
702 TeamList::Iterator iterator
703 = team
->xsi_sem_context
->undo_list
.GetIterator();
704 while (iterator
.HasNext()) {
705 struct sem_undo
*current
= iterator
.Next();
706 XsiSemaphoreSet
*semaphoreSet
= current
->semaphore_set
;
707 // Acquire the set lock in order to prevent race
708 // condition with RecordUndo
709 MutexLocker
setLocker(semaphoreSet
->Lock());
710 MutexLocker
_(team
->xsi_sem_context
->lock
);
711 // Revert the changes done by this process
712 for (int i
= 0; i
< semaphoreSet
->NumberOfSemaphores(); i
++)
713 if (current
->undo_values
[i
] != 0) {
714 TRACE(("xsi_sem_undo: TeamID = %d, SemaphoreSetID = %d, "
715 "SemaphoreNumber = %d, undo value = %d\n", (int)team
->id
,
716 semaphoreSet
->ID(), i
, (int)current
->undo_values
[i
]));
717 semaphoreSet
->Semaphore(i
)->Revert(current
->undo_values
[i
]);
720 // Remove and free the sem_undo structure from both lists
722 semaphoreSet
->GetUndoList().Remove(current
);
725 delete team
->xsi_sem_context
;
726 team
->xsi_sem_context
= NULL
;
730 // #pragma mark - Syscalls
734 _user_xsi_semget(key_t key
, int numberOfSemaphores
, int flags
)
736 TRACE(("xsi_semget: key = %d, numberOfSemaphores = %d, flags = %d\n",
737 (int)key
, numberOfSemaphores
, flags
));
738 XsiSemaphoreSet
*semaphoreSet
= NULL
;
740 // Default assumptions
741 bool isPrivate
= true;
744 MutexLocker
_(sIpcLock
);
745 if (key
!= IPC_PRIVATE
) {
747 // Check if key already exist, if it does it already has a semaphore
748 // set associated with it
749 ipcKey
= sIpcHashTable
.Lookup(key
);
750 if (ipcKey
== NULL
) {
751 // The ipc key does not exist. Create it and add it to the system
752 if (!(flags
& IPC_CREAT
)) {
753 TRACE_ERROR(("xsi_semget: key %d does not exist, but the "
754 "caller did not ask for creation\n",(int)key
));
757 ipcKey
= new(std::nothrow
) Ipc(key
);
758 if (ipcKey
== NULL
) {
759 TRACE_ERROR(("xsi_semget: failed to create new Ipc object "
760 "for key %d\n", (int)key
));
763 sIpcHashTable
.Insert(ipcKey
);
765 // The IPC key exist and it already has a semaphore
766 if ((flags
& IPC_CREAT
) && (flags
& IPC_EXCL
)) {
767 TRACE_ERROR(("xsi_semget: key %d already exist\n", (int)key
));
770 int semaphoreSetID
= ipcKey
->SemaphoreSetID();
772 MutexLocker
_(sXsiSemaphoreSetLock
);
773 semaphoreSet
= sSemaphoreHashTable
.Lookup(semaphoreSetID
);
774 if (!semaphoreSet
->HasPermission()) {
775 TRACE_ERROR(("xsi_semget: calling process has not permission "
776 "on semaphore %d, key %d\n", semaphoreSet
->ID(),
780 if (numberOfSemaphores
> semaphoreSet
->NumberOfSemaphores()
781 && numberOfSemaphores
!= 0) {
782 TRACE_ERROR(("xsi_semget: numberOfSemaphores greater than the "
783 "one associated with semaphore %d, key %d\n",
784 semaphoreSet
->ID(), (int)key
));
792 // Create a new sempahore set for this key
793 if (numberOfSemaphores
<= 0
794 || numberOfSemaphores
>= MAX_XSI_SEMS_PER_TEAM
) {
795 TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n"));
798 if (sXsiSemaphoreCount
>= MAX_XSI_SEMAPHORE
799 || sXsiSemaphoreSetCount
>= MAX_XSI_SEMAPHORE_SET
) {
800 TRACE_ERROR(("xsi_semget: reached limit of maximum number of "
801 "semaphores allowed\n"));
805 semaphoreSet
= new(std::nothrow
) XsiSemaphoreSet(numberOfSemaphores
,
807 if (semaphoreSet
== NULL
|| !semaphoreSet
->InitOK()) {
808 TRACE_ERROR(("xsi_semget: failed to allocate a new xsi "
813 atomic_add(&sXsiSemaphoreCount
, numberOfSemaphores
);
814 atomic_add(&sXsiSemaphoreSetCount
, 1);
816 MutexLocker
_(sXsiSemaphoreSetLock
);
817 semaphoreSet
->SetID();
819 semaphoreSet
->SetIpcKey((key_t
)-1);
821 semaphoreSet
->SetIpcKey(key
);
822 ipcKey
->SetSemaphoreSetID(semaphoreSet
);
824 sSemaphoreHashTable
.Insert(semaphoreSet
);
825 TRACE(("semget: new set = %d created, sequence = %ld\n",
826 semaphoreSet
->ID(), semaphoreSet
->SequenceNumber()));
829 return semaphoreSet
->ID();
834 _user_xsi_semctl(int semaphoreID
, int semaphoreNumber
, int command
,
837 TRACE(("xsi_semctl: semaphoreID = %d, semaphoreNumber = %d, command = %d\n",
838 semaphoreID
, semaphoreNumber
, command
));
839 MutexLocker
ipcHashLocker(sIpcLock
);
840 MutexLocker
setHashLocker(sXsiSemaphoreSetLock
);
841 XsiSemaphoreSet
*semaphoreSet
= sSemaphoreHashTable
.Lookup(semaphoreID
);
842 if (semaphoreSet
== NULL
) {
843 TRACE_ERROR(("xsi_semctl: semaphore set id %d not valid\n",
847 if (semaphoreNumber
< 0
848 || semaphoreNumber
> semaphoreSet
->NumberOfSemaphores()) {
849 TRACE_ERROR(("xsi_semctl: semaphore number %d not valid for "
850 "semaphore %d\n", semaphoreNumber
, semaphoreID
));
853 if (args
!= 0 && !IS_USER_ADDRESS(args
)) {
854 TRACE_ERROR(("xsi_semctl: semun address is not valid\n"));
855 return B_BAD_ADDRESS
;
858 // Lock the semaphore set itself and release both the semaphore
859 // set hash table lock and the ipc hash table lock _only_ if
860 // the command it's not IPC_RMID, this prevents undesidered
861 // situation from happening while (hopefully) improving the
863 MutexLocker setLocker
;
864 if (command
!= IPC_RMID
) {
865 setLocker
.SetTo(&semaphoreSet
->Lock(), false);
866 setHashLocker
.Unlock();
867 ipcHashLocker
.Unlock();
869 // We are about to delete the set along with its mutex, so
870 // we can't use the MutexLocker class, as the mutex itself
871 // won't exist on function exit
872 mutex_lock(&semaphoreSet
->Lock());
875 XsiSemaphore
*semaphore
= semaphoreSet
->Semaphore(semaphoreNumber
);
878 if (!semaphoreSet
->HasReadPermission()) {
879 TRACE_ERROR(("xsi_semctl: calling process has not permission "
880 "on semaphore %d, key %d\n", semaphoreSet
->ID(),
881 (int)semaphoreSet
->IpcKey()));
884 result
= semaphore
->Value();
889 if (!semaphoreSet
->HasPermission()) {
890 TRACE_ERROR(("xsi_semctl: calling process has not permission "
891 "on semaphore %d, key %d\n", semaphoreSet
->ID(),
892 (int)semaphoreSet
->IpcKey()));
896 if (user_memcpy(&value
, &args
->val
, sizeof(int)) < B_OK
) {
897 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
898 result
= B_BAD_ADDRESS
;
899 } else if (value
> USHRT_MAX
) {
900 TRACE_ERROR(("xsi_semctl: value %d out of range\n", value
));
903 semaphore
->SetValue(value
);
904 semaphoreSet
->ClearUndo(semaphoreNumber
);
911 if (!semaphoreSet
->HasReadPermission()) {
912 TRACE_ERROR(("xsi_semctl: calling process has not permission "
913 "on semaphore %d, key %d\n", semaphoreSet
->ID(),
914 (int)semaphoreSet
->IpcKey()));
917 result
= semaphore
->LastPid();
922 if (!semaphoreSet
->HasReadPermission()) {
923 TRACE_ERROR(("xsi_semctl: calling process has not permission "
924 "on semaphore %d, key %d\n", semaphoreSet
->ID(),
925 (int)semaphoreSet
->IpcKey()));
928 result
= semaphore
->ThreadsWaitingToIncrease();
933 if (!semaphoreSet
->HasReadPermission()) {
934 TRACE_ERROR(("xsi_semctl: calling process has not permission "
935 "on semaphore %d, key %d\n", semaphoreSet
->ID(),
936 (int)semaphoreSet
->IpcKey()));
939 result
= semaphore
->ThreadsWaitingToBeZero();
944 if (!semaphoreSet
->HasReadPermission()) {
945 TRACE_ERROR(("xsi_semctl: calling process has not read "
946 "permission on semaphore %d, key %d\n", semaphoreSet
->ID(),
947 (int)semaphoreSet
->IpcKey()));
950 for (int i
= 0; i
< semaphoreSet
->NumberOfSemaphores(); i
++) {
951 semaphore
= semaphoreSet
->Semaphore(i
);
952 unsigned short value
= semaphore
->Value();
953 if (user_memcpy(&args
->array
[i
], &value
,
954 sizeof(unsigned short)) < B_OK
) {
955 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
956 result
= B_BAD_ADDRESS
;
964 if (!semaphoreSet
->HasPermission()) {
965 TRACE_ERROR(("xsi_semctl: calling process has not permission "
966 "on semaphore %d, key %d\n", semaphoreSet
->ID(),
967 (int)semaphoreSet
->IpcKey()));
971 for (int i
= 0; i
< semaphoreSet
->NumberOfSemaphores(); i
++) {
972 semaphore
= semaphoreSet
->Semaphore(i
);
973 unsigned short value
;
974 if (user_memcpy(&value
, &args
->array
[i
], sizeof(unsigned short))
976 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
977 result
= B_BAD_ADDRESS
;
981 semaphore
->SetValue(value
);
984 semaphoreSet
->ClearUndos();
990 if (!semaphoreSet
->HasReadPermission()) {
991 TRACE_ERROR(("xsi_semctl: calling process has not read "
992 "permission on semaphore %d, key %d\n", semaphoreSet
->ID(),
993 (int)semaphoreSet
->IpcKey()));
997 sem
.sem_perm
= semaphoreSet
->IpcPermission();
998 sem
.sem_nsems
= semaphoreSet
->NumberOfSemaphores();
999 sem
.sem_otime
= semaphoreSet
->LastSemopTime();
1000 sem
.sem_ctime
= semaphoreSet
->LastSemctlTime();
1001 if (user_memcpy(args
->buf
, &sem
, sizeof(struct semid_ds
))
1003 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
1004 result
= B_BAD_ADDRESS
;
1011 if (!semaphoreSet
->HasPermission()) {
1012 TRACE_ERROR(("xsi_semctl: calling process has not "
1013 "permission on semaphore %d, key %d\n",
1014 semaphoreSet
->ID(), (int)semaphoreSet
->IpcKey()));
1017 struct semid_ds sem
;
1018 if (user_memcpy(&sem
, args
->buf
, sizeof(struct semid_ds
))
1020 TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
1021 result
= B_BAD_ADDRESS
;
1023 semaphoreSet
->DoIpcSet(&sem
);
1029 // If this was the command, we are still holding
1030 // the semaphore set hash table lock along with the
1031 // ipc hash table lock and the semaphore set lock
1032 // itself, this way we are sure there is not
1033 // one waiting in the queue of the mutex.
1034 if (!semaphoreSet
->HasPermission()) {
1035 TRACE_ERROR(("xsi_semctl: calling process has not "
1036 "permission on semaphore %d, key %d\n",
1037 semaphoreSet
->ID(), (int)semaphoreSet
->IpcKey()));
1040 key_t key
= semaphoreSet
->IpcKey();
1043 ipcKey
= sIpcHashTable
.Lookup(key
);
1044 sIpcHashTable
.Remove(ipcKey
);
1046 sSemaphoreHashTable
.Remove(semaphoreSet
);
1047 // Wake up of threads waiting on this set
1048 // happens in the destructor
1051 atomic_add(&sXsiSemaphoreCount
, -semaphoreSet
->NumberOfSemaphores());
1052 atomic_add(&sXsiSemaphoreSetCount
, -1);
1053 // Remove any sem_undo request
1054 while (struct sem_undo
*entry
1055 = semaphoreSet
->GetUndoList().RemoveHead()) {
1056 MutexLocker
_(entry
->team
->xsi_sem_context
->lock
);
1057 entry
->team
->xsi_sem_context
->undo_list
.Remove(entry
);
1061 delete semaphoreSet
;
1066 TRACE_ERROR(("xsi_semctl: command %d not valid\n", command
));
1075 _user_xsi_semop(int semaphoreID
, struct sembuf
*ops
, size_t numOps
)
1077 TRACE(("xsi_semop: semaphoreID = %d, ops = %p, numOps = %ld\n",
1078 semaphoreID
, ops
, numOps
));
1079 MutexLocker
setHashLocker(sXsiSemaphoreSetLock
);
1080 XsiSemaphoreSet
*semaphoreSet
= sSemaphoreHashTable
.Lookup(semaphoreID
);
1081 if (semaphoreSet
== NULL
) {
1082 TRACE_ERROR(("xsi_semop: semaphore set id %d not valid\n",
1086 MutexLocker
setLocker(semaphoreSet
->Lock());
1087 setHashLocker
.Unlock();
1089 if (!IS_USER_ADDRESS(ops
)) {
1090 TRACE_ERROR(("xsi_semop: sembuf address is not valid\n"));
1091 return B_BAD_ADDRESS
;
1094 if (numOps
< 0 || numOps
>= MAX_XSI_SEMS_PER_TEAM
) {
1095 TRACE_ERROR(("xsi_semop: numOps out of range\n"));
1099 struct sembuf
*operations
1100 = (struct sembuf
*)malloc(sizeof(struct sembuf
) * numOps
);
1101 if (operations
== NULL
) {
1102 TRACE_ERROR(("xsi_semop: failed to allocate sembuf struct\n"));
1105 MemoryDeleter
operationsDeleter(operations
);
1107 if (user_memcpy(operations
, ops
,
1108 (sizeof(struct sembuf
) * numOps
)) < B_OK
) {
1109 TRACE_ERROR(("xsi_semop: user_memcpy failed\n"));
1110 return B_BAD_ADDRESS
;
1113 // We won't do partial request, that is operations
1114 // only on some sempahores belonging to the set and then
1115 // going to sleep. If we must wait on a semaphore, we undo
1116 // all the operations already done and go to sleep, otherwise
1117 // we may caused some unwanted deadlock among threads
1118 // fighting for the same set.
1119 bool notDone
= true;
1120 status_t result
= 0;
1122 XsiSemaphore
*semaphore
= NULL
;
1123 short numberOfSemaphores
= semaphoreSet
->NumberOfSemaphores();
1124 bool goToSleep
= false;
1127 for (; i
< numOps
; i
++) {
1128 short semaphoreNumber
= operations
[i
].sem_num
;
1129 if (semaphoreNumber
>= numberOfSemaphores
) {
1130 TRACE_ERROR(("xsi_semop: %" B_PRIu32
" invalid semaphore number"
1135 semaphore
= semaphoreSet
->Semaphore(semaphoreNumber
);
1136 unsigned short value
= semaphore
->Value();
1137 short operation
= operations
[i
].sem_op
;
1138 TRACE(("xsi_semop: semaphoreNumber = %d, value = %d\n",
1139 semaphoreNumber
, value
));
1140 if (operation
< 0) {
1141 if (semaphore
->Add(operation
)) {
1142 if (operations
[i
].sem_flg
& IPC_NOWAIT
)
1148 } else if (operation
== 0) {
1151 else if (operations
[i
].sem_flg
& IPC_NOWAIT
) {
1159 // Operation must be greater than zero,
1160 // just add the value and continue
1161 semaphore
->Add(operation
);
1165 // Either we have to wait or an error occured
1166 if (goToSleep
|| result
!= 0) {
1167 // Undo all previously done operations
1168 for (uint32 j
= 0; j
< i
; j
++) {
1169 short semaphoreNumber
= operations
[j
].sem_num
;
1170 semaphore
= semaphoreSet
->Semaphore(semaphoreNumber
);
1171 short operation
= operations
[j
].sem_op
;
1173 semaphore
->Revert(operation
);
1178 // We have to wait: first enqueue the thread
1179 // in the appropriate set waiting list, then
1180 // unlock the set itself and block the thread.
1181 bool waitOnZero
= true;
1182 if (operations
[i
].sem_op
!= 0)
1185 Thread
*thread
= thread_get_current_thread();
1186 queued_thread
queueEntry(thread
, (int32
)operations
[i
].sem_op
);
1187 semaphore
->Enqueue(&queueEntry
, waitOnZero
);
1189 uint32 sequenceNumber
= semaphoreSet
->SequenceNumber();
1191 TRACE(("xsi_semop: thread %d going to sleep\n", (int)thread
->id
));
1192 result
= semaphore
->BlockAndUnlock(thread
, &setLocker
);
1193 TRACE(("xsi_semop: thread %d back to life\n", (int)thread
->id
));
1195 // We are back to life. Find out why!
1196 // Make sure the set hasn't been deleted or worst yet
1198 setHashLocker
.Lock();
1199 semaphoreSet
= sSemaphoreHashTable
.Lookup(semaphoreID
);
1200 if (result
== EIDRM
|| semaphoreSet
== NULL
|| (semaphoreSet
!= NULL
1201 && sequenceNumber
!= semaphoreSet
->SequenceNumber())) {
1202 TRACE_ERROR(("xsi_semop: semaphore set id %d (sequence = "
1203 "%" B_PRIu32
") got destroyed\n", semaphoreID
,
1207 } else if (result
== B_INTERRUPTED
) {
1208 TRACE_ERROR(("xsi_semop: thread %d got interrupted while "
1209 "waiting on semaphore set id %d\n",(int)thread
->id
,
1211 semaphore
->Deque(&queueEntry
, waitOnZero
);
1216 setHashLocker
.Unlock();
1219 // everything worked like a charm (so far)
1221 TRACE(("xsi_semop: semaphore acquired succesfully\n"));
1222 // We acquired the semaphore, now records the sem_undo
1224 XsiSemaphore
*semaphore
= NULL
;
1226 for (; i
< numOps
; i
++) {
1227 short semaphoreNumber
= operations
[i
].sem_num
;
1228 semaphore
= semaphoreSet
->Semaphore(semaphoreNumber
);
1229 short operation
= operations
[i
].sem_op
;
1230 if (operations
[i
].sem_flg
& SEM_UNDO
)
1231 if (semaphoreSet
->RecordUndo(semaphoreNumber
, operation
)
1233 // Unlikely scenario, but we might get here.
1235 // Start with semaphore operations
1236 for (uint32 j
= 0; j
< numOps
; j
++) {
1237 short semaphoreNumber
= operations
[j
].sem_num
;
1238 semaphore
= semaphoreSet
->Semaphore(semaphoreNumber
);
1239 short operation
= operations
[j
].sem_op
;
1241 semaphore
->Revert(operation
);
1243 // Remove all previously registered sem_undo request
1244 for (uint32 j
= 0; j
< i
; j
++) {
1245 if (operations
[j
].sem_flg
& SEM_UNDO
)
1246 semaphoreSet
->RevertUndo(operations
[j
].sem_num
,
1247 operations
[j
].sem_op
);
1255 // We did it. Set the pid of all semaphores used
1257 for (uint32 i
= 0; i
< numOps
; i
++) {
1258 short semaphoreNumber
= operations
[i
].sem_num
;
1259 XsiSemaphore
*semaphore
= semaphoreSet
->Semaphore(semaphoreNumber
);
1260 semaphore
->SetPid(getpid());