Skip to content

Commit

Permalink
Merge pull request #80 from ghexp/master
Browse files Browse the repository at this point in the history
Workaround for missing sem_timedwait on os x
  • Loading branch information
lovettchris authored Mar 5, 2017
2 parents d27454d + bf860e8 commit e764ea2
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 3 deletions.
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

0 comments on commit e764ea2

Please sign in to comment.