btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / system / kernel / posix / realtime_sem.cpp
blob4bd66525b6dabcc0506a0ff078bec4fedfa368e3
1 /*
2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <posix/realtime_sem.h>
8 #include <string.h>
10 #include <new>
12 #include <OS.h>
14 #include <AutoDeleter.h>
15 #include <fs/KPath.h>
16 #include <kernel.h>
17 #include <lock.h>
18 #include <syscall_restart.h>
19 #include <team.h>
20 #include <thread.h>
21 #include <util/atomic.h>
22 #include <util/AutoLock.h>
23 #include <util/OpenHashTable.h>
24 #include <util/StringHash.h>
27 namespace {
29 class SemInfo {
30 public:
31 SemInfo()
33 fSemaphoreID(-1)
37 virtual ~SemInfo()
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);
48 if (fSemaphoreID < 0)
49 return fSemaphoreID;
51 return B_OK;
54 virtual sem_id ID() const = 0;
55 virtual SemInfo* Clone() = 0;
56 virtual void Delete() = 0;
58 private:
59 sem_id fSemaphoreID;
63 class NamedSem : public SemInfo {
64 public:
65 NamedSem()
67 fName(NULL),
68 fRefCount(1)
72 virtual ~NamedSem()
74 free(fName);
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);
82 if (error != B_OK)
83 return error;
85 fName = strdup(name);
86 if (fName == NULL)
87 return B_NO_MEMORY;
89 fUID = geteuid();
90 fGID = getegid();
91 fPermissions = mode;
93 return B_OK;
96 void AcquireReference()
98 atomic_add(&fRefCount, 1);
101 void ReleaseReference()
103 if (atomic_add(&fRefCount, -1) == 1)
104 delete this;
107 bool HasPermissions() const
109 if ((fPermissions & S_IWOTH) != 0)
110 return true;
112 uid_t uid = geteuid();
113 if (uid == 0 || (uid == fUID && (fPermissions & S_IWUSR) != 0))
114 return true;
116 gid_t gid = getegid();
117 if (gid == fGID && (fPermissions & S_IWGRP) != 0)
118 return true;
120 return false;
123 virtual sem_id ID() const
125 return SemaphoreID();
128 virtual SemInfo* Clone()
130 AcquireReference();
131 return this;
134 virtual void Delete()
136 ReleaseReference();
139 NamedSem*& HashLink()
141 return fHashLink;
144 private:
145 char* fName;
146 int32 fRefCount;
147 uid_t fUID;
148 gid_t fGID;
149 mode_t fPermissions;
151 NamedSem* fHashLink;
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 {
182 public:
183 GlobalSemTable()
185 fSemaphoreCount(0)
187 mutex_init(&fLock, "global named sem table");
190 ~GlobalSemTable()
192 mutex_destroy(&fLock);
195 status_t Init()
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);
206 if (sem != NULL) {
207 if ((openFlags & O_EXCL) != 0)
208 return EEXIST;
210 if (!sem->HasPermissions())
211 return EACCES;
213 sem->AcquireReference();
214 _sem = sem;
215 _created = false;
216 return B_OK;
219 if ((openFlags & O_CREAT) == 0)
220 return ENOENT;
222 // does not exist yet -- create
223 if (fSemaphoreCount >= MAX_POSIX_SEMS)
224 return ENOSPC;
226 sem = new(std::nothrow) NamedSem;
227 if (sem == NULL)
228 return B_NO_MEMORY;
230 status_t error = sem->Init(name, mode, semCount);
231 if (error != B_OK) {
232 delete sem;
233 return error;
236 error = fNamedSemaphores.Insert(sem);
237 if (error != B_OK) {
238 delete sem;
239 return error;
242 // add one reference for the table
243 sem->AcquireReference();
245 fSemaphoreCount++;
247 _sem = sem;
248 _created = true;
249 return B_OK;
252 status_t UnlinkNamedSem(const char* name)
254 MutexLocker _(fLock);
256 NamedSem* sem = fNamedSemaphores.Lookup(name);
257 if (sem == NULL)
258 return ENOENT;
260 if (!sem->HasPermissions())
261 return EACCES;
263 fNamedSemaphores.Remove(sem);
264 sem->ReleaseReference();
265 // release the table reference
266 fSemaphoreCount--;
268 return B_OK;
271 private:
272 typedef BOpenHashTable<NamedSemHashDefinition, true> NamedSemTable;
274 mutex fLock;
275 NamedSemTable fNamedSemaphores;
276 int32 fSemaphoreCount;
280 static GlobalSemTable sSemTable;
283 class TeamSemInfo {
284 public:
285 TeamSemInfo(SemInfo* semaphore, sem_t* userSem)
287 fSemaphore(semaphore),
288 fUserSemaphore(userSem),
289 fOpenCount(1)
293 ~TeamSemInfo()
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; }
303 void Open()
305 fOpenCount++;
308 bool Close()
310 return --fOpenCount == 0;
313 TeamSemInfo* Clone() const
315 SemInfo* sem = fSemaphore->Clone();
316 if (sem == NULL)
317 return NULL;
319 TeamSemInfo* clone = new(std::nothrow) TeamSemInfo(sem, fUserSemaphore);
320 if (clone == NULL) {
321 sem->Delete();
322 return NULL;
325 clone->fOpenCount = fOpenCount;
327 return clone;
330 TeamSemInfo*& HashLink()
332 return fHashLink;
335 private:
336 SemInfo* fSemaphore;
337 sem_t* fUserSemaphore;
338 int32 fOpenCount;
340 TeamSemInfo* fHashLink;
344 struct TeamSemHashDefinition {
345 typedef sem_id KeyType;
346 typedef TeamSemInfo ValueType;
348 size_t HashKey(const KeyType& key) const
350 return (size_t)key;
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();
369 } // namespace
372 struct realtime_sem_context {
373 realtime_sem_context()
375 fSemaphoreCount(0)
377 mutex_init(&fLock, "realtime sem context");
380 ~realtime_sem_context()
382 mutex_lock(&fLock);
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);
390 delete sem;
393 mutex_destroy(&fLock);
396 status_t Init()
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;
406 if (context == NULL)
407 return NULL;
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)
419 return NULL;
421 if (context->fSemaphores.Insert(clonedSem) != B_OK) {
422 delete clonedSem;
423 return NULL;
425 context->fSemaphoreCount++;
428 contextDeleter.Detach();
429 return context;
432 status_t OpenSem(const char* name, int openFlags, mode_t mode,
433 uint32 semCount, sem_t* userSem, sem_t*& _usedUserSem, int32_t& _id,
434 bool& _created)
436 NamedSem* sem = NULL;
437 status_t error = sSemTable.OpenNamedSem(name, openFlags, mode, semCount,
438 sem, _created);
439 if (error != B_OK)
440 return error;
442 MutexLocker _(fLock);
444 TeamSemInfo* teamSem = fSemaphores.Lookup(sem->ID());
445 if (teamSem != NULL) {
446 // already open -- just increment the open count
447 teamSem->Open();
448 sem->ReleaseReference();
449 _usedUserSem = teamSem->UserSemaphore();
450 _id = teamSem->ID();
451 return B_OK;
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();
459 if (_created)
460 sSemTable.UnlinkNamedSem(name);
461 return ENOSPC;
464 teamSem = new(std::nothrow) TeamSemInfo(sem, userSem);
465 if (teamSem == NULL) {
466 sem->ReleaseReference();
467 if (_created)
468 sSemTable.UnlinkNamedSem(name);
469 return B_NO_MEMORY;
472 error = fSemaphores.Insert(teamSem);
473 if (error != B_OK) {
474 delete teamSem;
475 if (_created)
476 sSemTable.UnlinkNamedSem(name);
477 return error;
480 fSemaphoreCount++;
482 _usedUserSem = teamSem->UserSemaphore();
483 _id = teamSem->ID();
485 return B_OK;
488 status_t CloseSem(sem_id id, sem_t*& deleteUserSem)
490 deleteUserSem = NULL;
492 MutexLocker _(fLock);
494 TeamSemInfo* sem = fSemaphores.Lookup(id);
495 if (sem == NULL)
496 return B_BAD_VALUE;
498 if (sem->Close()) {
499 // last reference closed
500 fSemaphores.Remove(sem);
501 fSemaphoreCount--;
502 deleteUserSem = sem->UserSemaphore();
503 delete sem;
506 return B_OK;
509 status_t AcquireSem(sem_id id, bigtime_t timeout)
511 MutexLocker locker(fLock);
513 TeamSemInfo* sem = fSemaphores.Lookup(id);
514 if (sem == NULL)
515 return B_BAD_VALUE;
516 else
517 id = sem->SemaphoreID();
519 locker.Unlock();
521 status_t error;
522 if (timeout == 0) {
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);
527 } else {
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);
540 if (sem == NULL)
541 return B_BAD_VALUE;
542 else
543 id = sem->SemaphoreID();
545 locker.Unlock();
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);
556 if (sem == NULL)
557 return B_BAD_VALUE;
558 else
559 id = sem->SemaphoreID();
561 locker.Unlock();
563 int32 count;
564 status_t error = get_sem_count(id, &count);
565 if (error != B_OK)
566 return error;
568 _count = count;
569 return B_OK;
572 private:
573 sem_id _NextPrivateSemID()
575 while (true) {
576 if (fNextPrivateSemID >= 0)
577 fNextPrivateSemID = -1;
579 sem_id id = fNextPrivateSemID--;
580 if (fSemaphores.Lookup(id) == NULL)
581 return id;
585 private:
586 typedef BOpenHashTable<TeamSemHashDefinition, true> SemTable;
588 mutex fLock;
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;
603 // get context
604 realtime_sem_context* context = atomic_pointer_get(
605 &team->realtime_sem_context);
606 if (context != NULL)
607 return 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) {
612 delete context;
613 return NULL;
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)
620 return context;
622 // someone else was quicker
623 delete context;
624 return oldContext;
628 static status_t
629 copy_sem_name_to_kernel(const char* userName, KPath& buffer, char*& name)
631 if (userName == NULL)
632 return B_BAD_VALUE;
633 if (!IS_USER_ADDRESS(userName))
634 return B_BAD_ADDRESS;
636 if (buffer.InitCheck() != B_OK)
637 return B_NO_MEMORY;
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())
646 return ENAMETOOLONG;
648 return B_OK;
652 // #pragma mark - kernel internal
655 void
656 realtime_sem_init()
658 new(&sSemTable) GlobalSemTable;
659 if (sSemTable.Init() != B_OK)
660 panic("realtime_sem_init() failed to init global sem table");
664 void
665 delete_realtime_sem_context(realtime_sem_context* context)
667 delete context;
671 realtime_sem_context*
672 clone_realtime_sem_context(realtime_sem_context* context)
674 if (context == NULL)
675 return NULL;
677 return context->Clone();
681 // #pragma mark - syscalls
684 status_t
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();
689 if (context == NULL)
690 return B_NO_MEMORY;
692 if (semCount > MAX_POSIX_SEM_VALUE)
693 return B_BAD_VALUE;
695 // userSem must always be given
696 if (userSem == NULL)
697 return B_BAD_VALUE;
698 if (!IS_USER_ADDRESS(userSem))
699 return B_BAD_ADDRESS;
701 // check user pointers
702 if (_usedUserSem == NULL)
703 return B_BAD_VALUE;
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);
709 char* name;
710 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name);
711 if (error != B_OK)
712 return error;
714 // open the semaphore
715 sem_t* usedUserSem;
716 bool created = false;
717 int32_t id;
718 error = context->OpenSem(name, openFlagsOrShared, mode, semCount, userSem,
719 usedUserSem, id, created);
720 if (error != B_OK)
721 return error;
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) {
726 if (created)
727 sSemTable.UnlinkNamedSem(name);
728 sem_t* dummy;
729 context->CloseSem(id, dummy);
730 return B_BAD_ADDRESS;
733 return B_OK;
737 status_t
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();
744 if (context == NULL)
745 return B_BAD_VALUE;
747 // close sem
748 sem_t* deleteUserSem;
749 status_t error = context->CloseSem(semID, deleteUserSem);
750 if (error != B_OK)
751 return error;
753 // copy back result to userland
754 if (_deleteUserSem != NULL
755 && user_memcpy(_deleteUserSem, &deleteUserSem, sizeof(sem_t*))
756 != B_OK) {
757 return B_BAD_ADDRESS;
760 return B_OK;
764 status_t
765 _user_realtime_sem_unlink(const char* userName)
767 // copy name to kernel
768 KPath nameBuffer(B_PATH_NAME_LENGTH);
769 char* name;
770 status_t error = copy_sem_name_to_kernel(userName, nameBuffer, name);
771 if (error != B_OK)
772 return error;
774 return sSemTable.UnlinkNamedSem(name);
778 status_t
779 _user_realtime_sem_get_value(sem_id semID, int* _value)
781 if (_value == NULL)
782 return B_BAD_VALUE;
783 if (!IS_USER_ADDRESS(_value))
784 return B_BAD_ADDRESS;
786 realtime_sem_context* context = get_current_team_context();
787 if (context == NULL)
788 return B_BAD_VALUE;
790 // get sem count
791 int count;
792 status_t error = context->GetSemCount(semID, count);
793 if (error != B_OK)
794 return error;
796 // copy back result to userland
797 if (user_memcpy(_value, &count, sizeof(int)) != B_OK)
798 return B_BAD_ADDRESS;
800 return B_OK;
804 status_t
805 _user_realtime_sem_post(sem_id semID)
807 realtime_sem_context* context = get_current_team_context();
808 if (context == NULL)
809 return B_BAD_VALUE;
811 return context->ReleaseSem(semID);
815 status_t
816 _user_realtime_sem_wait(sem_id semID, bigtime_t timeout)
818 realtime_sem_context* context = get_current_team_context();
819 if (context == NULL)
820 return B_BAD_VALUE;
822 return syscall_restart_handle_post(context->AcquireSem(semID, timeout));