diff options
| author | Martin Ridgers <[email protected]> | 2022-01-07 10:15:24 +0100 |
|---|---|---|
| committer | Martin Ridgers <[email protected]> | 2022-01-07 10:16:52 +0100 |
| commit | 3b2b679d3f0f328a4203f478296db0a15b09ca2c (patch) | |
| tree | 87b8a365826004d213c6605e2bb476fb52df193f /zencore/thread.cpp | |
| parent | Found some Wayward whitespace (diff) | |
| download | zen-3b2b679d3f0f328a4203f478296db0a15b09ca2c.tar.xz zen-3b2b679d3f0f328a4203f478296db0a15b09ca2c.zip | |
Implemented NamedEvents on Mac using System V semaphores
Diffstat (limited to 'zencore/thread.cpp')
| -rw-r--r-- | zencore/thread.cpp | 97 |
1 files changed, 95 insertions, 2 deletions
diff --git a/zencore/thread.cpp b/zencore/thread.cpp index 249eee121..063b060c9 100644 --- a/zencore/thread.cpp +++ b/zencore/thread.cpp @@ -30,6 +30,10 @@ # include <mqueue.h> #endif +#if ZEN_PLATFORM_MAC +# include <sys/sem.h> +#endif + #include <thread> ZEN_THIRD_PARTY_INCLUDES_START @@ -292,8 +296,52 @@ NamedEvent::NamedEvent(std::string_view EventName) } m_EventHandle = (void*)intptr_t(Inner); -#else -# error Implement NamedEvent for this platform +#elif ZEN_PLATFORM_MAC + // Create a file to back the semaphore + ExtendableStringBuilder<64> EventPath; + EventPath << "/tmp/" << EventName; + + int Fd = open(EventPath.c_str(), O_RDWR|O_CREAT, 0644); + if (Fd < 0) + { + using namespace fmt::literals; + ThrowLastError("Failed to create '{}' for named event"_format(EventPath)); + } + + // Use the file path to generate an IPC key + key_t IpcKey = ftok(EventPath.c_str(), 1); + if (IpcKey < 0) + { + close(Fd); + ThrowLastError("Failed to create an SysV IPC key"); + } + + // Use the key to create/open the semaphore + int Sem = semget(IpcKey, 1, 0600 | IPC_CREAT); + if (Sem < 0) + { + close(Fd); + ThrowLastError("Failed creating an SysV semaphore"); + } + + // Atomically claim ownership of the semaphore's key. The owner initialises + // the semaphore to 1 so we can use the wait-for-zero op as that does not + // modify the semaphore's value on a successful wait. + int LockResult = flock(Fd, LOCK_EX | LOCK_NB); + if (LockResult == 0) + { + // This isn't thread safe really. Another thread could open the same + // semaphore and successfully wait on it in the period of time where + // this comment is but before the semaphore's initialised. + semctl(Sem, 0, SETVAL, 1); + } + + // Pack into the handle + static_assert(sizeof(Sem) + sizeof(Fd) <= sizeof(void*), "Semaphore packing assumptions not met"); + intptr_t Packed; + Packed = intptr_t(Sem) << 32; + Packed |= intptr_t(Fd) & 0xffff'ffff; + m_EventHandle = (void*)Packed; #endif } @@ -323,6 +371,20 @@ NamedEvent::Close() } close(Inner); +#elif ZEN_PLATFORM_MAC + int Fd = int(intptr_t(m_EventHandle) & 0xffff'ffff); + + if (flock(Fd, LOCK_EX | LOCK_NB) == 0) + { + std::filesystem::path Name = PathFromHandle((void*)(intptr_t(Fd))); + unlink(Name.c_str()); + + flock(Fd, LOCK_UN | LOCK_NB); + close(Fd); + + int Sem = int(intptr_t(m_EventHandle) >> 32); + semctl(Sem, 0, IPC_RMID); + } #endif m_EventHandle = nullptr; @@ -346,6 +408,9 @@ NamedEvent::Set() { ThrowLastError("Unable to send set message to queue"); } +#elif ZEN_PLATFORM_MAC + int Sem = int(intptr_t(m_EventHandle) >> 32); + semctl(Sem, 0, SETVAL, 0); #endif } @@ -382,6 +447,34 @@ NamedEvent::Wait(int TimeoutMs) FD_ZERO(&FdSet); FD_SET(Inner, &FdSet); return select(Inner + 1, &FdSet, nullptr, nullptr, TimeoutPtr) > 0; +#elif ZEN_PLATFORM_MAC + int Sem = int(intptr_t(m_EventHandle) >> 32); + + int Result; + struct sembuf SemOp = {}; + + if (TimeoutMs < 0) + { + Result = semop(Sem, &SemOp, 1); + return Result == 0; + } + + const int SleepTimeMs = 10; + SemOp.sem_flg = IPC_NOWAIT; + do + { + Result = semop(Sem, &SemOp, 1); + if (Result == 0 || errno != EAGAIN) + { + break; + } + + Sleep(SleepTimeMs); + TimeoutMs -= SleepTimeMs; + } + while (TimeoutMs > 0); + + return Result == 0; #endif } |