xref: /haiku/src/system/kernel/posix/xsi_semaphore.cpp (revision a5061ecec55353a5f394759473f1fd6df04890da)
1 /*
2  * Copyright 2008-2011, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Salvatore Benedetto <salvatore.benedetto@gmail.com>
7  */
8 
9 #include <posix/xsi_semaphore.h>
10 
11 #include <new>
12 
13 #include <sys/ipc.h>
14 #include <sys/types.h>
15 
16 #include <OS.h>
17 
18 #include <kernel.h>
19 #include <syscall_restart.h>
20 
21 #include <util/atomic.h>
22 #include <util/AutoLock.h>
23 #include <util/DoublyLinkedList.h>
24 #include <util/OpenHashTable.h>
25 #include <AutoDeleter.h>
26 
27 
28 //#define TRACE_XSI_SEM
29 #ifdef TRACE_XSI_SEM
30 #	define TRACE(x)			dprintf x
31 #	define TRACE_ERROR(x)	dprintf x
32 #else
33 #	define TRACE(x)			/* nothing */
34 #	define TRACE_ERROR(x)	dprintf x
35 #endif
36 
37 
38 namespace {
39 
40 // Queue for holding blocked threads
41 struct queued_thread : DoublyLinkedListLinkImpl<queued_thread> {
42 	queued_thread(Thread *thread, int32 count)
43 		:
44 		thread(thread),
45 		count(count),
46 		queued(false)
47 	{
48 	}
49 
50 	Thread	*thread;
51 	int32	count;
52 	bool	queued;
53 };
54 
55 typedef DoublyLinkedList<queued_thread> ThreadQueue;
56 
57 class XsiSemaphoreSet;
58 
59 struct sem_undo : DoublyLinkedListLinkImpl<sem_undo> {
60 	sem_undo(XsiSemaphoreSet *semaphoreSet, Team *team, int16 *undoValues)
61 		:
62 		semaphore_set(semaphoreSet),
63 		team(team),
64 		undo_values(undoValues)
65 	{
66 	}
67 
68 	DoublyLinkedListLink<sem_undo>		team_link;
69 	XsiSemaphoreSet						*semaphore_set;
70 	Team								*team;
71 	int16								*undo_values;
72 };
73 
74 typedef DoublyLinkedList<sem_undo> UndoList;
75 typedef DoublyLinkedList<sem_undo,
76 	DoublyLinkedListMemberGetLink<sem_undo, &sem_undo::team_link> > TeamList;
77 
78 } // namespace
79 
80 
81 // Forward declared in global namespace.
82 struct xsi_sem_context {
83 	xsi_sem_context()
84 	{
85 		mutex_init(&lock, "Private team undo_list lock");
86 	}
87 
88 	~xsi_sem_context()
89 	{
90 		mutex_destroy(&lock);
91 	}
92 
93 	TeamList	undo_list;
94 	mutex		lock;
95 };
96 
97 
98 namespace {
99 
100 // Xsi semaphore definition
101 class XsiSemaphore {
102 public:
103 	XsiSemaphore()
104 		: fLastPidOperation(0),
105 		fThreadsWaitingToIncrease(0),
106 		fThreadsWaitingToBeZero(0),
107 		fValue(0)
108 	{
109 	}
110 
111 	~XsiSemaphore()
112 	{
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);
118 		}
119 		while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) {
120 			entry->queued = false;
121 			thread_unblock(entry->thread, EIDRM);
122 		}
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.
129 	}
130 
131 	// We return true in case the operation causes the
132 	// caller to wait, so it can undo all the operations
133 	// previously done
134 	bool Add(short value)
135 	{
136 		if ((int)(fValue + value) < 0) {
137 			TRACE(("XsiSemaphore::Add: potentially going to sleep\n"));
138 			return true;
139 		} else {
140 			fValue += value;
141 			if (fValue == 0 && fThreadsWaitingToBeZero > 0)
142 				WakeUpThread(true);
143 			else if (fValue > 0 && fThreadsWaitingToIncrease > 0)
144 				WakeUpThread(false);
145 			return false;
146 		}
147 	}
148 
149 	status_t BlockAndUnlock(Thread *thread, MutexLocker *setLocker)
150 	{
151 		thread_prepare_to_block(thread, B_CAN_INTERRUPT,
152 			THREAD_BLOCK_TYPE_OTHER, (void*)"xsi semaphore");
153 		// Unlock the set before blocking
154 		setLocker->Unlock();
155 
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
159 // mutex.
160 		return thread_block();
161 	}
162 
163 	void Deque(queued_thread *queueEntry, bool waitForZero)
164 	{
165 		if (queueEntry->queued) {
166 			if (waitForZero) {
167 				fWaitingToBeZeroQueue.Remove(queueEntry);
168 				fThreadsWaitingToBeZero--;
169 			} else {
170 				fWaitingToIncreaseQueue.Remove(queueEntry);
171 				fThreadsWaitingToIncrease--;
172 			}
173 		}
174 	}
175 
176 	void Enqueue(queued_thread *queueEntry, bool waitForZero)
177 	{
178 		if (waitForZero) {
179 			fWaitingToBeZeroQueue.Add(queueEntry);
180 			fThreadsWaitingToBeZero++;
181 		} else {
182 			fWaitingToIncreaseQueue.Add(queueEntry);
183 			fThreadsWaitingToIncrease++;
184 		}
185 		queueEntry->queued = true;
186 	}
187 
188 	pid_t LastPid() const
189 	{
190 		return fLastPidOperation;
191 	}
192 
193 	void Revert(short value)
194 	{
195 		fValue -= value;
196 		if (fValue == 0 && fThreadsWaitingToBeZero > 0)
197 			WakeUpThread(true);
198 		else if (fValue > 0 && fThreadsWaitingToIncrease > 0)
199 			WakeUpThread(false);
200 	}
201 
202 	void SetPid(pid_t pid)
203 	{
204 		fLastPidOperation = pid;
205 	}
206 
207 	void SetValue(ushort value)
208 	{
209 		fValue = value;
210 	}
211 
212 	ushort ThreadsWaitingToIncrease() const
213 	{
214 		return fThreadsWaitingToIncrease;
215 	}
216 
217 	ushort ThreadsWaitingToBeZero() const
218 	{
219 		return fThreadsWaitingToBeZero;
220 	}
221 
222 	ushort Value() const
223 	{
224 		return fValue;
225 	}
226 
227 	void WakeUpThread(bool waitingForZero)
228 	{
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);
235 			}
236 		} else {
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);
242 			}
243 		}
244 	}
245 
246 private:
247 	pid_t				fLastPidOperation;				// sempid
248 	ushort				fThreadsWaitingToIncrease;		// semncnt
249 	ushort				fThreadsWaitingToBeZero;		// semzcnt
250 	ushort				fValue;							// semval
251 
252 	ThreadQueue			fWaitingToIncreaseQueue;
253 	ThreadQueue			fWaitingToBeZeroQueue;
254 };
255 
256 #define MAX_XSI_SEMS_PER_TEAM	128
257 
258 // Xsi semaphore set definition (semid_ds)
259 class XsiSemaphoreSet {
260 public:
261 	XsiSemaphoreSet(int numberOfSemaphores, int flags)
262 		: fInitOK(false),
263 		fLastSemctlTime((time_t)real_time_clock()),
264 		fLastSemopTime(0),
265 		fNumberOfSemaphores(numberOfSemaphores),
266 		fSemaphores(0)
267 	{
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"));
275 		} else
276 			fInitOK = true;
277 	}
278 
279 	~XsiSemaphoreSet()
280 	{
281 		TRACE(("XsiSemaphoreSet::~XsiSemaphoreSet(): removing semaphore "
282 			"set %d\n", fID));
283 		mutex_destroy(&fLock);
284 		delete[] fSemaphores;
285 	}
286 
287 	void ClearUndo(unsigned short semaphoreNumber)
288 	{
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;
299 				return;
300 			}
301 		}
302 	}
303 
304 	void ClearUndos()
305 	{
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);
318 				return;
319 			}
320 		}
321 	}
322 
323 	void DoIpcSet(struct semid_ds *result)
324 	{
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);
329 	}
330 
331 	bool HasPermission() const
332 	{
333 		if ((fPermissions.mode & S_IWOTH) != 0)
334 			return true;
335 
336 		uid_t uid = geteuid();
337 		if (uid == 0 || (uid == fPermissions.uid
338 			&& (fPermissions.mode & S_IWUSR) != 0))
339 			return true;
340 
341 		gid_t gid = getegid();
342 		if (gid == fPermissions.gid && (fPermissions.mode & S_IWGRP) != 0)
343 			return true;
344 
345 		return false;
346 	}
347 
348 	bool HasReadPermission() const
349 	{
350 		// TODO: fix this
351 		return HasPermission();
352 	}
353 
354 	int ID() const
355 	{
356 		return fID;
357 	}
358 
359 	bool InitOK()
360 	{
361 		return fInitOK;
362 	}
363 
364 	key_t IpcKey() const
365 	{
366 		return fPermissions.key;
367 	}
368 
369 	struct ipc_perm IpcPermission() const
370 	{
371 		return fPermissions;
372 	}
373 
374 	time_t LastSemctlTime() const
375 	{
376 		return fLastSemctlTime;
377 	}
378 
379 	time_t LastSemopTime() const
380 	{
381 		return fLastSemopTime;
382 	}
383 
384 	mutex &Lock()
385 	{
386 		return fLock;
387 	}
388 
389 	ushort NumberOfSemaphores() const
390 	{
391 		return fNumberOfSemaphores;
392 	}
393 
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)
398 	{
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));
413 					return ERANGE;
414 				}
415 				current->undo_values[semaphoreNumber] = newValue;
416 				notFound = false;
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]));
421 				break;
422 			}
423 		}
424 
425 		if (notFound) {
426 			// First sem_undo request from this team for this
427 			// semaphore set
428 			int16 *undoValues
429 				= (int16 *)malloc(sizeof(int16) * fNumberOfSemaphores);
430 			if (undoValues == NULL)
431 				return B_NO_MEMORY;
432 			struct sem_undo *request
433 				= new(std::nothrow) sem_undo(this, team, undoValues);
434 			if (request == NULL) {
435 				free(undoValues);
436 				return B_NO_MEMORY;
437 			}
438 			memset(request->undo_values, 0, sizeof(int16) * fNumberOfSemaphores);
439 			request->undo_values[semaphoreNumber] = value;
440 
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);
448 					delete request;
449 					return B_NO_MEMORY;
450 				}
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
454 				// in place.
455 				if (atomic_pointer_test_and_set(&team->xsi_sem_context, context,
456 					(xsi_sem_context *)NULL) != NULL)
457 					delete context;
458 			}
459 
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));
467 		}
468 		return B_OK;
469 	}
470 
471 	void RevertUndo(short semaphoreNumber, short value)
472 	{
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);
481 				break;
482 			}
483 		}
484 	}
485 
486 	XsiSemaphore* Semaphore(int nth) const
487 	{
488 		return &fSemaphores[nth];
489 	}
490 
491 	uint32 SequenceNumber() const
492 	{
493 		return fSequenceNumber;
494 	}
495 
496 	// Implemented after sGlobalSequenceNumber is declared
497 	void SetID();
498 
499 	void SetIpcKey(key_t key)
500 	{
501 		fPermissions.key = key;
502 	}
503 
504 	void SetLastSemctlTime()
505 	{
506 		fLastSemctlTime = real_time_clock();
507 	}
508 
509 	void SetLastSemopTime()
510 	{
511 		fLastSemopTime = real_time_clock();
512 	}
513 
514 	void SetPermissions(int flags)
515 	{
516 		fPermissions.uid = fPermissions.cuid = geteuid();
517 		fPermissions.gid = fPermissions.cgid = getegid();
518 		fPermissions.mode = (flags & 0x01ff);
519 	}
520 
521 	UndoList &GetUndoList()
522 	{
523 		return fUndoList;
524 	}
525 
526 	XsiSemaphoreSet*& Link()
527 	{
528 		return fLink;
529 	}
530 
531 private:
532 	int							fID;					// semaphore set id
533 	bool						fInitOK;
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
542 
543 	XsiSemaphoreSet*			fLink;
544 };
545 
546 // Xsi semaphore set hash table
547 struct SemaphoreHashTableDefinition {
548 	typedef int					KeyType;
549 	typedef XsiSemaphoreSet		ValueType;
550 
551 	size_t HashKey (const int key) const
552 	{
553 		return (size_t)key;
554 	}
555 
556 	size_t Hash(XsiSemaphoreSet *variable) const
557 	{
558 		return (size_t)variable->ID();
559 	}
560 
561 	bool Compare(const int key, XsiSemaphoreSet *variable) const
562 	{
563 		return (int)key == (int)variable->ID();
564 	}
565 
566 	XsiSemaphoreSet*& GetLink(XsiSemaphoreSet *variable) const
567 	{
568 		return variable->Link();
569 	}
570 };
571 
572 
573 // IPC class
574 class Ipc {
575 public:
576 	Ipc(key_t key)
577 		: fKey(key),
578 		fSemaphoreSetId(-1)
579 	{
580 	}
581 
582 	key_t Key() const
583 	{
584 		return fKey;
585 	}
586 
587 	int SemaphoreSetID() const
588 	{
589 		return fSemaphoreSetId;
590 	}
591 
592 	void SetSemaphoreSetID(XsiSemaphoreSet *semaphoreSet)
593 	{
594 		fSemaphoreSetId = semaphoreSet->ID();
595 	}
596 
597 	Ipc*& Link()
598 	{
599 		return fLink;
600 	}
601 
602 private:
603 	key_t				fKey;
604 	int					fSemaphoreSetId;
605 	Ipc*				fLink;
606 };
607 
608 
609 struct IpcHashTableDefinition {
610 	typedef key_t	KeyType;
611 	typedef Ipc		ValueType;
612 
613 	size_t HashKey (const key_t key) const
614 	{
615 		return (size_t)(key);
616 	}
617 
618 	size_t Hash(Ipc *variable) const
619 	{
620 		return (size_t)HashKey(variable->Key());
621 	}
622 
623 	bool Compare(const key_t key, Ipc *variable) const
624 	{
625 		return (key_t)key == (key_t)variable->Key();
626 	}
627 
628 	Ipc*& GetLink(Ipc *variable) const
629 	{
630 		return variable->Link();
631 	}
632 };
633 
634 } // namespace
635 
636 
637 // Arbitrary limit
638 #define MAX_XSI_SEMAPHORE		4096
639 #define MAX_XSI_SEMAPHORE_SET	2048
640 static BOpenHashTable<IpcHashTableDefinition> sIpcHashTable;
641 static BOpenHashTable<SemaphoreHashTableDefinition> sSemaphoreHashTable;
642 
643 static mutex sIpcLock;
644 static mutex sXsiSemaphoreSetLock;
645 
646 static uint32 sGlobalSequenceNumber = 1;
647 static int32 sXsiSemaphoreCount = 0;
648 static int32 sXsiSemaphoreSetCount = 0;
649 
650 
651 //	#pragma mark -
652 
653 
654 void
655 XsiSemaphoreSet::SetID()
656 {
657 	fID = real_time_clock();
658 	// The lock is held before calling us
659 	while (true) {
660 		if (sSemaphoreHashTable.Lookup(fID) == NULL)
661 			break;
662 		fID = (fID + 1) % INT_MAX;
663 	}
664 	sGlobalSequenceNumber = (sGlobalSequenceNumber + 1) % UINT_MAX;
665 	fSequenceNumber = sGlobalSequenceNumber;
666 }
667 
668 
669 //	#pragma mark - Kernel exported API
670 
671 
672 void
673 xsi_sem_init()
674 {
675 	// Initialize hash tables
676 	status_t status = sIpcHashTable.Init();
677 	if (status != B_OK)
678 		panic("xsi_sem_init() failed to initialize ipc hash table\n");
679 	status =  sSemaphoreHashTable.Init();
680 	if (status != B_OK)
681 		panic("xsi_sem_init() failed to initialize semaphore hash table\n");
682 
683 	mutex_init(&sIpcLock, "global POSIX semaphore IPC table");
684 	mutex_init(&sXsiSemaphoreSetLock, "global POSIX xsi sem table");
685 }
686 
687 
688 /*!	Function called on team exit to process any sem_undo requests */
689 void
690 xsi_sem_undo(Team *team)
691 {
692 	if (team->xsi_sem_context == NULL)
693 		return;
694 
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);
699 
700 	// Process all sem_undo request in the team sem undo list
701 	// if any
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]);
718 			}
719 
720 		// Remove and free the sem_undo structure from both lists
721 		iterator.Remove();
722 		semaphoreSet->GetUndoList().Remove(current);
723 		delete current;
724 	}
725 	delete team->xsi_sem_context;
726 	team->xsi_sem_context = NULL;
727 }
728 
729 
730 //	#pragma mark - Syscalls
731 
732 
733 int
734 _user_xsi_semget(key_t key, int numberOfSemaphores, int flags)
735 {
736 	TRACE(("xsi_semget: key = %d, numberOfSemaphores = %d, flags = %d\n",
737 		(int)key, numberOfSemaphores, flags));
738 	XsiSemaphoreSet *semaphoreSet = NULL;
739 	Ipc *ipcKey = NULL;
740 
741 	// Default assumption
742 	bool isPrivate = true;
743 
744 	MutexLocker ipcLocker(sIpcLock);
745 	if (key != IPC_PRIVATE) {
746 		isPrivate = false;
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));
755 				return ENOENT;
756 			}
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));
761 				return ENOMEM;
762 			}
763 		} else {
764 			// The IPC key exist and it already has a semaphore
765 			if ((flags & IPC_CREAT) && (flags & IPC_EXCL)) {
766 				TRACE_ERROR(("xsi_semget: key %d already exist\n", (int)key));
767 				return EEXIST;
768 			}
769 			int semaphoreSetID = ipcKey->SemaphoreSetID();
770 
771 			MutexLocker semaphoreSetLocker(sXsiSemaphoreSetLock);
772 			semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreSetID);
773 			if (semaphoreSet == NULL) {
774 				TRACE_ERROR(("xsi_semget: calling process has no semaphore, "
775 					"key %d\n", (int)key));
776 				return EINVAL;
777 			}
778 			if (!semaphoreSet->HasPermission()) {
779 				TRACE_ERROR(("xsi_semget: calling process has no permission "
780 					"on semaphore %d, key %d\n", semaphoreSet->ID(),
781 					(int)key));
782 				return EACCES;
783 			}
784 			if (numberOfSemaphores > semaphoreSet->NumberOfSemaphores()
785 				&& numberOfSemaphores != 0) {
786 				TRACE_ERROR(("xsi_semget: numberOfSemaphores greater than the "
787 					"one associated with semaphore %d, key %d\n",
788 					semaphoreSet->ID(), (int)key));
789 				return EINVAL;
790 			}
791 
792 			return semaphoreSet->ID();
793 		}
794 	}
795 
796 	// Create a new sempahore set for this key
797 	if (numberOfSemaphores <= 0
798 		|| numberOfSemaphores >= MAX_XSI_SEMS_PER_TEAM) {
799 		TRACE_ERROR(("xsi_semget: numberOfSemaphores out of range\n"));
800 		delete ipcKey;
801 		return EINVAL;
802 	}
803 	if (sXsiSemaphoreCount >= MAX_XSI_SEMAPHORE
804 		|| sXsiSemaphoreSetCount >= MAX_XSI_SEMAPHORE_SET) {
805 		TRACE_ERROR(("xsi_semget: reached limit of maximum number of "
806 			"semaphores allowed\n"));
807 		delete ipcKey;
808 		return ENOSPC;
809 	}
810 
811 	semaphoreSet = new(std::nothrow) XsiSemaphoreSet(numberOfSemaphores,
812 		flags);
813 	if (semaphoreSet == NULL || !semaphoreSet->InitOK()) {
814 		TRACE_ERROR(("xsi_semget: failed to allocate a new xsi "
815 			"semaphore set\n"));
816 		delete semaphoreSet;
817 		delete ipcKey;
818 		return ENOMEM;
819 	}
820 
821 	atomic_add(&sXsiSemaphoreCount, numberOfSemaphores);
822 	atomic_add(&sXsiSemaphoreSetCount, 1);
823 
824 	MutexLocker semaphoreSetLocker(sXsiSemaphoreSetLock);
825 	semaphoreSet->SetID();
826 	if (isPrivate) {
827 		semaphoreSet->SetIpcKey((key_t)-1);
828 	} else {
829 		sIpcHashTable.Insert(ipcKey);
830 		semaphoreSet->SetIpcKey(key);
831 		ipcKey->SetSemaphoreSetID(semaphoreSet);
832 	}
833 	sSemaphoreHashTable.Insert(semaphoreSet);
834 	TRACE(("semget: new set = %d created, sequence = %ld\n",
835 		semaphoreSet->ID(), semaphoreSet->SequenceNumber()));
836 
837 	return semaphoreSet->ID();
838 }
839 
840 
841 int
842 _user_xsi_semctl(int semaphoreID, int semaphoreNumber, int command,
843 	union semun *_args)
844 {
845 	TRACE(("xsi_semctl: semaphoreID = %d, semaphoreNumber = %d, command = %d\n",
846 		semaphoreID, semaphoreNumber, command));
847 
848 	union semun args = {0};
849 	if (_args != NULL) {
850 		if (!IS_USER_ADDRESS(_args)
851 				|| user_memcpy(&args, _args, sizeof(union semun)) != B_OK)
852 			return B_BAD_ADDRESS;
853 	}
854 
855 	MutexLocker ipcHashLocker(sIpcLock);
856 	MutexLocker setHashLocker(sXsiSemaphoreSetLock);
857 	XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
858 	if (semaphoreSet == NULL) {
859 		TRACE_ERROR(("xsi_semctl: semaphore set id %d not valid\n",
860 			semaphoreID));
861 		return EINVAL;
862 	}
863 	if (semaphoreNumber < 0
864 		|| semaphoreNumber > semaphoreSet->NumberOfSemaphores()) {
865 		TRACE_ERROR(("xsi_semctl: semaphore number %d not valid for "
866 			"semaphore %d\n", semaphoreNumber, semaphoreID));
867 		return EINVAL;
868 	}
869 
870 	// Lock the semaphore set itself and release both the semaphore
871 	// set hash table lock and the ipc hash table lock _only_ if
872 	// the command it's not IPC_RMID, this prevents undesidered
873 	// situation from happening while (hopefully) improving the
874 	// concurrency.
875 	MutexLocker setLocker;
876 	if (command != IPC_RMID) {
877 		setLocker.SetTo(&semaphoreSet->Lock(), false);
878 		setHashLocker.Unlock();
879 		ipcHashLocker.Unlock();
880 	} else {
881 		// We are about to delete the set along with its mutex, so
882 		// we can't use the MutexLocker class, as the mutex itself
883 		// won't exist on function exit
884 		mutex_lock(&semaphoreSet->Lock());
885 	}
886 
887 	int result = 0;
888 	XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber);
889 	switch (command) {
890 		case GETVAL: {
891 			if (!semaphoreSet->HasReadPermission()) {
892 				TRACE_ERROR(("xsi_semctl: calling process has not permission "
893 					"on semaphore %d, key %d\n", semaphoreSet->ID(),
894 					(int)semaphoreSet->IpcKey()));
895 				result = EACCES;
896 			} else
897 				result = semaphore->Value();
898 			break;
899 		}
900 
901 		case SETVAL: {
902 			if (!semaphoreSet->HasPermission()) {
903 				TRACE_ERROR(("xsi_semctl: calling process has not permission "
904 					"on semaphore %d, key %d\n", semaphoreSet->ID(),
905 					(int)semaphoreSet->IpcKey()));
906 				result = EACCES;
907 			} else {
908 				if (args.val > USHRT_MAX) {
909 					TRACE_ERROR(("xsi_semctl: value %d out of range\n", args.val));
910 					result = ERANGE;
911 				} else {
912 					semaphore->SetValue(args.val);
913 					semaphoreSet->ClearUndo(semaphoreNumber);
914 				}
915 			}
916 			break;
917 		}
918 
919 		case GETPID: {
920 			if (!semaphoreSet->HasReadPermission()) {
921 				TRACE_ERROR(("xsi_semctl: calling process has not permission "
922 					"on semaphore %d, key %d\n", semaphoreSet->ID(),
923 					(int)semaphoreSet->IpcKey()));
924 				result = EACCES;
925 			} else
926 				result = semaphore->LastPid();
927 			break;
928 		}
929 
930 		case GETNCNT: {
931 			if (!semaphoreSet->HasReadPermission()) {
932 				TRACE_ERROR(("xsi_semctl: calling process has not permission "
933 					"on semaphore %d, key %d\n", semaphoreSet->ID(),
934 					(int)semaphoreSet->IpcKey()));
935 				result = EACCES;
936 			} else
937 				result = semaphore->ThreadsWaitingToIncrease();
938 			break;
939 		}
940 
941 		case GETZCNT: {
942 			if (!semaphoreSet->HasReadPermission()) {
943 				TRACE_ERROR(("xsi_semctl: calling process has not permission "
944 					"on semaphore %d, key %d\n", semaphoreSet->ID(),
945 					(int)semaphoreSet->IpcKey()));
946 				result = EACCES;
947 			} else
948 				result = semaphore->ThreadsWaitingToBeZero();
949 			break;
950 		}
951 
952 		case GETALL: {
953 			if (!semaphoreSet->HasReadPermission()) {
954 				TRACE_ERROR(("xsi_semctl: calling process has not read "
955 					"permission on semaphore %d, key %d\n", semaphoreSet->ID(),
956 					(int)semaphoreSet->IpcKey()));
957 				result = EACCES;
958 			} else
959 				for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) {
960 					semaphore = semaphoreSet->Semaphore(i);
961 					unsigned short value = semaphore->Value();
962 					if (user_memcpy(args.array + i, &value,
963 							sizeof(unsigned short)) != B_OK) {
964 						TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
965 						result = B_BAD_ADDRESS;
966 						break;
967 					}
968 				}
969 			break;
970 		}
971 
972 		case SETALL: {
973 			if (!semaphoreSet->HasPermission()) {
974 				TRACE_ERROR(("xsi_semctl: calling process has not permission "
975 					"on semaphore %d, key %d\n", semaphoreSet->ID(),
976 					(int)semaphoreSet->IpcKey()));
977 				result = EACCES;
978 			} else {
979 				bool doClear = true;
980 				for (int i = 0; i < semaphoreSet->NumberOfSemaphores(); i++) {
981 					semaphore = semaphoreSet->Semaphore(i);
982 					unsigned short value;
983 					if (user_memcpy(&value, args.array + i,
984 							sizeof(unsigned short)) != B_OK) {
985 						TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
986 						result = B_BAD_ADDRESS;
987 						doClear = false;
988 						break;
989 					} else
990 						semaphore->SetValue(value);
991 				}
992 				if (doClear)
993 					semaphoreSet->ClearUndos();
994 			}
995 			break;
996 		}
997 
998 		case IPC_STAT: {
999 			if (!semaphoreSet->HasReadPermission()) {
1000 				TRACE_ERROR(("xsi_semctl: calling process has not read "
1001 					"permission on semaphore %d, key %d\n", semaphoreSet->ID(),
1002 					(int)semaphoreSet->IpcKey()));
1003 				result = EACCES;
1004 			} else {
1005 				struct semid_ds sem;
1006 				sem.sem_perm = semaphoreSet->IpcPermission();
1007 				sem.sem_nsems = semaphoreSet->NumberOfSemaphores();
1008 				sem.sem_otime = semaphoreSet->LastSemopTime();
1009 				sem.sem_ctime = semaphoreSet->LastSemctlTime();
1010 				if (user_memcpy(args.buf, &sem, sizeof(struct semid_ds))
1011 						< B_OK) {
1012 					TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
1013 					result = B_BAD_ADDRESS;
1014 				}
1015 			}
1016 			break;
1017 		}
1018 
1019 		case IPC_SET: {
1020 			if (!semaphoreSet->HasPermission()) {
1021 				TRACE_ERROR(("xsi_semctl: calling process has not "
1022 					"permission on semaphore %d, key %d\n",
1023 					semaphoreSet->ID(), (int)semaphoreSet->IpcKey()));
1024 				result = EACCES;
1025 			} else {
1026 				struct semid_ds sem;
1027 				if (user_memcpy(&sem, args.buf, sizeof(struct semid_ds))
1028 						!= B_OK) {
1029 					TRACE_ERROR(("xsi_semctl: user_memcpy failed\n"));
1030 					result = B_BAD_ADDRESS;
1031 				} else
1032 					semaphoreSet->DoIpcSet(&sem);
1033 			}
1034 			break;
1035 		}
1036 
1037 		case IPC_RMID: {
1038 			// If this was the command, we are still holding
1039 			// the semaphore set hash table lock along with the
1040 			// ipc hash table lock and the semaphore set lock
1041 			// itself, this way we are sure there is not
1042 			// one waiting in the queue of the mutex.
1043 			if (!semaphoreSet->HasPermission()) {
1044 				TRACE_ERROR(("xsi_semctl: calling process has not "
1045 					"permission on semaphore %d, key %d\n",
1046 					semaphoreSet->ID(), (int)semaphoreSet->IpcKey()));
1047 				return EACCES;
1048 			}
1049 			key_t key = semaphoreSet->IpcKey();
1050 			Ipc *ipcKey = NULL;
1051 			if (key != -1) {
1052 				ipcKey = sIpcHashTable.Lookup(key);
1053 				sIpcHashTable.Remove(ipcKey);
1054 			}
1055 			sSemaphoreHashTable.Remove(semaphoreSet);
1056 			// Wake up of threads waiting on this set
1057 			// happens in the destructor
1058 			if (key != -1)
1059 				delete ipcKey;
1060 			atomic_add(&sXsiSemaphoreCount, -semaphoreSet->NumberOfSemaphores());
1061 			atomic_add(&sXsiSemaphoreSetCount, -1);
1062 			// Remove any sem_undo request
1063 			while (struct sem_undo *entry
1064 					= semaphoreSet->GetUndoList().RemoveHead()) {
1065 				MutexLocker _(entry->team->xsi_sem_context->lock);
1066 				entry->team->xsi_sem_context->undo_list.Remove(entry);
1067 				delete entry;
1068 			}
1069 
1070 			delete semaphoreSet;
1071 			return 0;
1072 		}
1073 
1074 		default:
1075 			TRACE_ERROR(("xsi_semctl: command %d not valid\n", command));
1076 			result = EINVAL;
1077 	}
1078 
1079 	return result;
1080 }
1081 
1082 
1083 status_t
1084 _user_xsi_semop(int semaphoreID, struct sembuf *ops, size_t numOps)
1085 {
1086 	TRACE(("xsi_semop: semaphoreID = %d, ops = %p, numOps = %ld\n",
1087 		semaphoreID, ops, numOps));
1088 	MutexLocker setHashLocker(sXsiSemaphoreSetLock);
1089 	XsiSemaphoreSet *semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
1090 	if (semaphoreSet == NULL) {
1091 		TRACE_ERROR(("xsi_semop: semaphore set id %d not valid\n",
1092 			semaphoreID));
1093 		return EINVAL;
1094 	}
1095 	MutexLocker setLocker(semaphoreSet->Lock());
1096 	setHashLocker.Unlock();
1097 
1098 	if (!IS_USER_ADDRESS(ops)) {
1099 		TRACE_ERROR(("xsi_semop: sembuf address is not valid\n"));
1100 		return B_BAD_ADDRESS;
1101 	}
1102 
1103 	if (numOps < 0 || numOps >= MAX_XSI_SEMS_PER_TEAM) {
1104 		TRACE_ERROR(("xsi_semop: numOps out of range\n"));
1105 		return EINVAL;
1106 	}
1107 
1108 	struct sembuf *operations
1109 		= (struct sembuf *)malloc(sizeof(struct sembuf) * numOps);
1110 	if (operations == NULL) {
1111 		TRACE_ERROR(("xsi_semop: failed to allocate sembuf struct\n"));
1112 		return B_NO_MEMORY;
1113 	}
1114 	MemoryDeleter operationsDeleter(operations);
1115 
1116 	if (user_memcpy(operations, ops,
1117 			(sizeof(struct sembuf) * numOps)) != B_OK) {
1118 		TRACE_ERROR(("xsi_semop: user_memcpy failed\n"));
1119 		return B_BAD_ADDRESS;
1120 	}
1121 
1122 	// We won't do partial request, that is operations
1123 	// only on some sempahores belonging to the set and then
1124 	// going to sleep. If we must wait on a semaphore, we undo
1125 	// all the operations already done and go to sleep, otherwise
1126 	// we may caused some unwanted deadlock among threads
1127 	// fighting for the same set.
1128 	bool notDone = true;
1129 	status_t result = 0;
1130 	while (notDone) {
1131 		XsiSemaphore *semaphore = NULL;
1132 		short numberOfSemaphores = semaphoreSet->NumberOfSemaphores();
1133 		bool goToSleep = false;
1134 
1135 		uint32 i = 0;
1136 		for (; i < numOps; i++) {
1137 			short semaphoreNumber = operations[i].sem_num;
1138 			if (semaphoreNumber >= numberOfSemaphores) {
1139 				TRACE_ERROR(("xsi_semop: %" B_PRIu32 " invalid semaphore number"
1140 					"\n", i));
1141 				result = EINVAL;
1142 				break;
1143 			}
1144 			semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1145 			unsigned short value = semaphore->Value();
1146 			short operation = operations[i].sem_op;
1147 			TRACE(("xsi_semop: semaphoreNumber = %d, value = %d\n",
1148 				semaphoreNumber, value));
1149 			if (operation < 0) {
1150 				if (semaphore->Add(operation)) {
1151 					if (operations[i].sem_flg & IPC_NOWAIT)
1152 						result = EAGAIN;
1153 					else
1154 						goToSleep = true;
1155 					break;
1156 				}
1157 			} else if (operation == 0) {
1158 				if (value == 0)
1159 					continue;
1160 				else if (operations[i].sem_flg & IPC_NOWAIT) {
1161 					result = EAGAIN;
1162 					break;
1163 				} else {
1164 					goToSleep = true;
1165 					break;
1166 				}
1167 			} else {
1168 				// Operation must be greater than zero,
1169 				// just add the value and continue
1170 				semaphore->Add(operation);
1171 			}
1172 		}
1173 
1174 		// Either we have to wait or an error occured
1175 		if (goToSleep || result != 0) {
1176 			// Undo all previously done operations
1177 			for (uint32 j = 0; j < i; j++) {
1178 				short semaphoreNumber = operations[j].sem_num;
1179 				semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1180 				short operation = operations[j].sem_op;
1181 				if (operation != 0)
1182 					semaphore->Revert(operation);
1183 			}
1184 			if (result != 0)
1185 				return result;
1186 
1187 			// We have to wait: first enqueue the thread
1188 			// in the appropriate set waiting list, then
1189 			// unlock the set itself and block the thread.
1190 			bool waitOnZero = true;
1191 			if (operations[i].sem_op != 0)
1192 				waitOnZero = false;
1193 
1194 			Thread *thread = thread_get_current_thread();
1195 			queued_thread queueEntry(thread, (int32)operations[i].sem_op);
1196 			semaphore->Enqueue(&queueEntry, waitOnZero);
1197 
1198 			uint32 sequenceNumber = semaphoreSet->SequenceNumber();
1199 
1200 			TRACE(("xsi_semop: thread %d going to sleep\n", (int)thread->id));
1201 			result = semaphore->BlockAndUnlock(thread, &setLocker);
1202 			TRACE(("xsi_semop: thread %d back to life\n", (int)thread->id));
1203 
1204 			// We are back to life. Find out why!
1205 			// Make sure the set hasn't been deleted or worst yet
1206 			// replaced.
1207 			setHashLocker.Lock();
1208 			semaphoreSet = sSemaphoreHashTable.Lookup(semaphoreID);
1209 			if (result == EIDRM || semaphoreSet == NULL || (semaphoreSet != NULL
1210 				&& sequenceNumber != semaphoreSet->SequenceNumber())) {
1211 				TRACE_ERROR(("xsi_semop: semaphore set id %d (sequence = "
1212 					"%" B_PRIu32 ") got destroyed\n", semaphoreID,
1213 					sequenceNumber));
1214 				notDone = false;
1215 				result = EIDRM;
1216 			} else if (result == B_INTERRUPTED) {
1217 				TRACE_ERROR(("xsi_semop: thread %d got interrupted while "
1218 					"waiting on semaphore set id %d\n",(int)thread->id,
1219 					semaphoreID));
1220 				semaphore->Deque(&queueEntry, waitOnZero);
1221 				result = EINTR;
1222 				notDone = false;
1223 			} else {
1224 				setLocker.Lock();
1225 				setHashLocker.Unlock();
1226 			}
1227 		} else {
1228 			// everything worked like a charm (so far)
1229 			notDone = false;
1230 			TRACE(("xsi_semop: semaphore acquired succesfully\n"));
1231 			// We acquired the semaphore, now records the sem_undo
1232 			// requests
1233 			XsiSemaphore *semaphore = NULL;
1234 			uint32 i = 0;
1235 			for (; i < numOps; i++) {
1236 				short semaphoreNumber = operations[i].sem_num;
1237 				semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1238 				short operation = operations[i].sem_op;
1239 				if (operations[i].sem_flg & SEM_UNDO)
1240 					if (semaphoreSet->RecordUndo(semaphoreNumber, operation)
1241 						!= B_OK) {
1242 						// Unlikely scenario, but we might get here.
1243 						// Undo everything!
1244 						// Start with semaphore operations
1245 						for (uint32 j = 0; j < numOps; j++) {
1246 							short semaphoreNumber = operations[j].sem_num;
1247 							semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1248 							short operation = operations[j].sem_op;
1249 							if (operation != 0)
1250 								semaphore->Revert(operation);
1251 						}
1252 						// Remove all previously registered sem_undo request
1253 						for (uint32 j = 0; j < i; j++) {
1254 							if (operations[j].sem_flg & SEM_UNDO)
1255 								semaphoreSet->RevertUndo(operations[j].sem_num,
1256 									operations[j].sem_op);
1257 						}
1258 						result = ENOSPC;
1259 					}
1260 			}
1261 		}
1262 	}
1263 
1264 	// We did it. Set the pid of all semaphores used
1265 	if (result == 0) {
1266 		for (uint32 i = 0; i < numOps; i++) {
1267 			short semaphoreNumber = operations[i].sem_num;
1268 			XsiSemaphore *semaphore = semaphoreSet->Semaphore(semaphoreNumber);
1269 			semaphore->SetPid(getpid());
1270 		}
1271 	}
1272 	return result;
1273 }
1274