libicsneo/platform/posix/sharedsemaphore.cpp

100 lines
2.8 KiB
C++

#include <iostream> // TODO: Remove later
#include <fcntl.h>
#include "icsneo/platform/posix/sharedsemaphore.h"
using namespace icsneo;
SharedSemaphore::~SharedSemaphore() {
close();
}
bool SharedSemaphore::open(const std::string& name, bool create, unsigned initialCount) {
const auto slashPrefixed = "/" + name;
if(create)
sem_unlink(slashPrefixed.c_str()); // try clean-up, it's fine if it errors
const auto sem = create ? sem_open(slashPrefixed.c_str(), O_CREAT | O_EXCL, 0600, initialCount) : sem_open(slashPrefixed.c_str(), 0);
if(sem == SEM_FAILED) {
report(APIEvent::Type::SharedSemaphoreFailedToOpen, APIEvent::Severity::Error);
return false;
}
mName.emplace(slashPrefixed);
semaphore.emplace(sem);
created.emplace(create);
return true;
}
bool SharedSemaphore::close() {
closing = true;
post(); // wake any waiting
bool failed = false;
if(semaphore) {
if(sem_close(semaphore.value()) == -1) {
report(APIEvent::Type::SharedSemaphoreFailedToClose, APIEvent::Severity::Error);
failed = true;
}
semaphore.reset();
}
if(mName && created && *created) {
if(sem_unlink(mName->c_str()) == -1) {
report(APIEvent::Type::SharedSemaphoreFailedToUnlink, APIEvent::Severity::Error);
failed = true;
}
mName.reset();
}
return !failed;
}
bool SharedSemaphore::wait(const std::chrono::milliseconds& timeout) {
if(!semaphore) {
report(APIEvent::Type::SharedSemaphoreNotOpenForWait, APIEvent::Severity::Error);
return false;
}
const auto timedwait = [&]() -> bool {
#if defined(__MACH__)
// TODO: Quite inefficient due to Darwin's lack of sem_timedwait()
const auto tryTill = std::chrono::steady_clock::now() + timeout;
while (std::chrono::steady_clock::now() <= tryTill) {
if(sem_trywait(*semaphore) == 0)
return true;
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
return false;
#else // UNIX
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += static_cast<unsigned int>(timeout.count() / 1000);
ts.tv_nsec += static_cast<int>((timeout.count() % 1000) * 1000000);
// potentially promote another second
if(ts.tv_nsec >= 1000000000) {
ts.tv_nsec -= 1000000000;
++ts.tv_sec;
}
return sem_timedwait(*semaphore, &ts) != -1;
#endif
};
if(!timedwait()) {
if(errno == ETIMEDOUT)
return false; // unable to lock within timeout
if(errno != EINTR) // we don't need a warning for this
report(APIEvent::Type::SharedSemaphoreFailedToWait, APIEvent::Severity::Error);
return false;
}
if(closing)
return false; // we were woken by close()
return true;
}
bool SharedSemaphore::post() {
if(!semaphore) {
report(APIEvent::Type::SharedSemaphoreNotOpenForPost, APIEvent::Severity::Error);
return false;
}
if(sem_post(*semaphore) == -1) {
report(APIEvent::Type::SharedSemaphoreFailedToPost, APIEvent::Severity::Error);
return false;
}
return true;
}