Я планировал использовать следующую функцию в качестве замены, но потом обнаружил, что sem_getvalue () также устарела и не работает в OSX. Вы можете использовать следующий слегка непроверенный код под лицензией MIT или LGPL (на ваш выбор).
#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;
sighandler_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
SIGALRM имеет больше смысла, чем SIGUSR2, так как здесь явно используется другой пример (я не стал его рассматривать). SIGALRM в основном зарезервирован для вызовов alarm (), которые практически бесполезны, если требуется разрешение менее секунды.
Этот код сначала пытается получить семафор с помощью sem_trywait (). Если это немедленно удастся, то выручит. В противном случае он запускает поток, в котором таймер реализуется через pthread_cond_timedwait (). Логическое значение MxSignaled используется для определения состояния тайм-аута.
Вы также можете найти эту соответствующую функцию полезной для вызова вышеупомянутой реализации sem_timedwait () (опять же, MIT или LGPL, на ваш выбор):
int CSGX__ClockGetTimeRealtime(struct timespec *ts)
{
#ifdef __APPLE__
clock_serv_t cclock;
mach_timespec_t mts;
if (host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock) != KERN_SUCCESS) return -1;
if (clock_get_time(cclock, &mts) != KERN_SUCCESS) return -1;
if (mach_port_deallocate(mach_task_self(), cclock) != KERN_SUCCESS) return -1;
ts->tv_sec = mts.tv_sec;
ts->tv_nsec = mts.tv_nsec;
return 0;
#else
return clock_gettime(CLOCK_REALTIME, ts);
#endif
}
Помогает заполнить структуру временного спека наиболее близким к тому, что может обеспечить clock_gettime (). Существуют различные комментарии о том, что многократный вызов host_get_clock_service () является дорогостоящим. Но запуск потока также дорог.
Настоящим исправлением для Apple является реализация всей спецификации POSIX, а не только обязательных частей. Реализация только обязательных битов POSIX и затем утверждение соответствия POSIX просто оставляет каждого с полуразбитой ОС и тоннами обходных путей, подобных описанным выше, которые могут иметь далеко не идеальную производительность.
Выше всего сказанного я отказываюсь от собственных семафоров (как Sys V, так и POSIX) на Mac OSX и Linux. Они сломаны довольно несколькими довольно неудачными способами. Все остальные тоже должны от них отказаться. (Я не отказываюсь от семафоров в этих ОС, только от нативных реализаций.) Во всяком случае, теперь у всех есть реализация sem_timedwait () без коммерческих ограничений, которые другие могут копировать-вставлять в свое сердце.