aboutsummaryrefslogtreecommitdiff
path: root/zencore/thread.cpp
diff options
context:
space:
mode:
authorMartin Ridgers <[email protected]>2022-01-07 10:15:24 +0100
committerMartin Ridgers <[email protected]>2022-01-07 10:16:52 +0100
commit3b2b679d3f0f328a4203f478296db0a15b09ca2c (patch)
tree87b8a365826004d213c6605e2bb476fb52df193f /zencore/thread.cpp
parentFound some Wayward whitespace (diff)
downloadzen-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.cpp97
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
}