Add OS-abstracted semaphore primitive

* Uses POSIX sem on linux/android, dispatch semaphores on apple, Win32
  Semaphores on windows.
This commit is contained in:
baldurk
2024-08-29 12:21:41 +01:00
parent 62c86f647e
commit 841a487d36
5 changed files with 240 additions and 0 deletions
+11
View File
@@ -146,6 +146,17 @@ uint64_t AllocateTLSSlot();
void *GetTLSValue(uint64_t slot);
void SetTLSValue(uint64_t slot, void *value);
struct Semaphore
{
static Semaphore *Create();
void Destroy();
void Wake(uint32_t numToWake);
void WaitForWake();
protected:
Semaphore();
~Semaphore();
};
// must typedef CriticalSectionTemplate<X> CriticalSection
void SetCurrentThreadName(const rdcstr &name);
@@ -24,6 +24,10 @@
#include "os/os_specific.h"
#include "common/common.h"
#include <errno.h>
#include <semaphore.h>
#include <time.h>
#include <unistd.h>
@@ -42,3 +46,67 @@ uint64_t Timing::GetTick()
void Threading::SetCurrentThreadName(const rdcstr &name)
{
}
namespace Threading
{
// works for all posix except apple, hence being here
struct PosixSemaphore : public Semaphore
{
~PosixSemaphore() {}
sem_t h;
};
Semaphore *Semaphore::Create()
{
PosixSemaphore *sem = new PosixSemaphore();
int err = sem_init(&sem->h, 0, 0);
// only documented errors are too large initial value (impossible for 0) or for shared semaphores
// going wrong (we're not shared)
RDCASSERT(err == 0, errno);
return sem;
}
void Semaphore::Destroy()
{
PosixSemaphore *sem = (PosixSemaphore *)this;
sem_destroy(&sem->h);
delete sem;
}
void Semaphore::Wake(uint32_t numToWake)
{
PosixSemaphore *sem = (PosixSemaphore *)this;
for(uint32_t i = 0; i < numToWake; i++)
sem_post(&sem->h);
}
void Semaphore::WaitForWake()
{
PosixSemaphore *sem = (PosixSemaphore *)this;
// handle extremely moronic stupid signal interruptions
do
{
int ret = sem_wait(&sem->h);
if(ret == -1)
{
if(errno == EINTR)
continue;
RDCWARN("Semaphore wait failed: %d", errno);
}
} while(false);
}
Semaphore::Semaphore()
{
}
Semaphore::~Semaphore()
{
}
};
@@ -24,6 +24,7 @@
#include "os/os_specific.h"
#include <dispatch/dispatch.h>
#include <mach/mach_time.h>
double Timing::GetTickFrequency()
@@ -44,3 +45,51 @@ uint64_t Timing::GetTick()
void Threading::SetCurrentThreadName(const rdcstr &name)
{
}
namespace Threading
{
struct AppleSemaphore : public Semaphore
{
~AppleSemaphore() {}
dispatch_semaphore_t h;
};
Semaphore *Semaphore::Create()
{
AppleSemaphore *sem = new AppleSemaphore();
sem->h = dispatch_semaphore_create(0);
return sem;
}
void Semaphore::Destroy()
{
AppleSemaphore *sem = (AppleSemaphore *)this;
dispatch_release(sem->h);
delete sem;
}
void Semaphore::Wake(uint32_t numToWake)
{
AppleSemaphore *sem = (AppleSemaphore *)this;
for(uint32_t i = 0; i < numToWake; i++)
dispatch_semaphore_signal(sem->h);
}
void Semaphore::WaitForWake()
{
AppleSemaphore *sem = (AppleSemaphore *)this;
// no timeout, so no point checking return value as that's the only failure case
dispatch_semaphore_wait(sem->h, DISPATCH_TIME_FOREVER);
}
Semaphore::Semaphore()
{
}
Semaphore::~Semaphore()
{
}
};
@@ -24,6 +24,10 @@
#include "os/os_specific.h"
#include "common/common.h"
#include <errno.h>
#include <semaphore.h>
#include <sys/prctl.h>
#include <time.h>
#include <unistd.h>
@@ -44,3 +48,67 @@ void Threading::SetCurrentThreadName(const rdcstr &name)
{
prctl(PR_SET_NAME, (unsigned long)name.c_str(), 0, 0, 0);
}
namespace Threading
{
// works for all posix except apple, hence being here
struct PosixSemaphore : public Semaphore
{
~PosixSemaphore() {}
sem_t h;
};
Semaphore *Semaphore::Create()
{
PosixSemaphore *sem = new PosixSemaphore();
int err = sem_init(&sem->h, 0, 0);
// only documented errors are too large initial value (impossible for 0) or for shared semaphores
// going wrong (we're not shared)
RDCASSERT(err == 0, errno);
return sem;
}
void Semaphore::Destroy()
{
PosixSemaphore *sem = (PosixSemaphore *)this;
sem_destroy(&sem->h);
delete sem;
}
void Semaphore::Wake(uint32_t numToWake)
{
PosixSemaphore *sem = (PosixSemaphore *)this;
for(uint32_t i = 0; i < numToWake; i++)
sem_post(&sem->h);
}
void Semaphore::WaitForWake()
{
PosixSemaphore *sem = (PosixSemaphore *)this;
// handle extremely moronic stupid signal interruptions
do
{
int ret = sem_wait(&sem->h);
if(ret == -1)
{
if(errno == EINTR)
continue;
RDCWARN("Semaphore wait failed: %d", errno);
}
} while(false);
}
Semaphore::Semaphore()
{
}
Semaphore::~Semaphore()
{
}
};
+44
View File
@@ -368,4 +368,48 @@ void Sleep(uint32_t milliseconds)
{
::Sleep((DWORD)milliseconds);
}
struct Win32Semaphore : public Semaphore
{
~Win32Semaphore() {}
HANDLE h;
};
Semaphore *Semaphore::Create()
{
Win32Semaphore *sem = new Win32Semaphore();
sem->h = CreateSemaphore(NULL, 0, 0xffff, NULL);
return sem;
}
void Semaphore::Destroy()
{
Win32Semaphore *sem = (Win32Semaphore *)this;
CloseHandle(sem->h);
delete sem;
}
void Semaphore::Wake(uint32_t numToWake)
{
Win32Semaphore *sem = (Win32Semaphore *)this;
ReleaseSemaphore(sem->h, numToWake, NULL);
}
void Semaphore::WaitForWake()
{
Win32Semaphore *sem = (Win32Semaphore *)this;
DWORD err = WaitForSingleObject(sem->h, INFINITE);
if(err == WAIT_FAILED)
RDCWARN("Semaphore failed to sleep: %d", GetLastError());
}
Semaphore::Semaphore()
{
}
Semaphore::~Semaphore()
{
}
};