Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Workaround for missing sem_timedwait on os x #80

Merged
merged 1 commit into from
Mar 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions MavLinkCom/include/MavLinkSemaphore.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
#define MavLinkCom_Semaphore_hpp
#include <memory>

#ifdef __APPLE__
#include <semaphore.h>
#endif

namespace mavlinkcom
{
/*
Expand All @@ -27,13 +31,16 @@ namespace mavlinkcom
void wait();

// wait for a given number of milliseconds for one call to post. Returns false if a timeout or EINTR occurs.
// If post has already been called then timed_wait returns immediately decrementing the count so the next
// If post has already been called then timed_wait returns immediately decrementing the count so the next
// wait will block. Throws exception if an error occurs.
bool timed_wait(int milliseconds);
private:
private:
class semaphore_impl;
std::unique_ptr<semaphore_impl> impl_;
};
}

#endif
#ifdef __APPLE__
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
#endif
94 changes: 93 additions & 1 deletion MavLinkCom/src/MavLinkSemaphore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
using namespace mavlinkcom;
using namespace common_utils;

#ifdef __APPLE__
#include <signal.h> //SIGALRM
#endif

#ifdef _WIN32
#include <Windows.h>

Expand Down Expand Up @@ -117,7 +121,7 @@ class MavLinkSemaphore::semaphore_impl
absolute = absolute - seconds;
auto nanoSecondsRemaining = std::chrono::duration_cast<std::chrono::nanoseconds>(absolute);
struct timespec ts;
ts.tv_sec = seconds.count(); // seconds
ts.tv_sec = seconds.count(); // seconds
milliseconds -= (ts.tv_sec * 1000);
ts.tv_nsec = nanoSecondsRemaining.count(); // nanoseconds
int rc = sem_timedwait(&semaphore, &ts);
Expand Down Expand Up @@ -163,3 +167,91 @@ bool MavLinkSemaphore::timed_wait(int millisecondTimeout)
{
return impl_->timed_wait(millisecondTimeout);
}

#ifdef __APPLE__
struct CSGX__sem_timedwait_Info
{
pthread_mutex_t MxMutex;
pthread_cond_t MxCondition;
pthread_t MxParent;
struct timespec MxTimeout;
bool MxSignaled;
};

void *CSGX__sem_timedwait_Child(void *MainPtr)
{
CSGX__sem_timedwait_Info *TempInfo = (CSGX__sem_timedwait_Info *)MainPtr;

pthread_mutex_lock(&TempInfo->MxMutex);

// Wait until the timeout or the condition is signaled, whichever comes first.
int Result;
do
{
Result = pthread_cond_timedwait(&TempInfo->MxCondition, &TempInfo->MxMutex, &TempInfo->MxTimeout);
if (!Result) break;
} while (1);
if (errno == ETIMEDOUT && !TempInfo->MxSignaled)
{
TempInfo->MxSignaled = true;
pthread_kill(TempInfo->MxParent, SIGALRM);
}

pthread_mutex_unlock(&TempInfo->MxMutex);

return NULL;
}

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout)
{
// Quick test to see if a lock can be immediately obtained.
int Result;

do
{
Result = sem_trywait(sem);
if (!Result) return 0;
} while (Result < 0 && errno == EINTR);

// Since it couldn't be obtained immediately, it is time to shuttle the request off to a thread.
// Depending on the timeout, this could take longer than the timeout.
CSGX__sem_timedwait_Info TempInfo;

pthread_mutex_init(&TempInfo.MxMutex, NULL);
pthread_cond_init(&TempInfo.MxCondition, NULL);
TempInfo.MxParent = pthread_self();
TempInfo.MxTimeout.tv_sec = abs_timeout->tv_sec;
TempInfo.MxTimeout.tv_nsec = abs_timeout->tv_nsec;
TempInfo.MxSignaled = false;

sig_t OldSigHandler = signal(SIGALRM, SIG_DFL);

pthread_t ChildThread;
pthread_create(&ChildThread, NULL, CSGX__sem_timedwait_Child, &TempInfo);

// Wait for the semaphore, the timeout to expire, or an unexpected error condition.
do
{
Result = sem_wait(sem);
if (Result == 0 || TempInfo.MxSignaled || (Result < 0 && errno != EINTR)) break;
} while (1);

// Terminate the thread (if it is still running).
TempInfo.MxSignaled = true;
int LastError = errno;

pthread_mutex_lock(&TempInfo.MxMutex);
pthread_cond_signal(&TempInfo.MxCondition);
pthread_mutex_unlock(&TempInfo.MxMutex);
pthread_join(ChildThread, NULL);
pthread_cond_destroy(&TempInfo.MxCondition);
pthread_mutex_destroy(&TempInfo.MxMutex);

// Restore previous signal handler.
signal(SIGALRM, OldSigHandler);

errno = LastError;

return Result;
}
#endif