我原本计划使用以下函数作为替代,但后来发现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);
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)
{
int Result;
do
{
Result = sem_trywait(sem);
if (!Result) return 0;
} while (Result < 0 && errno == EINTR);
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);
do
{
Result = sem_wait(sem);
if (Result == 0 || TempInfo.MxSignaled || (Result < 0 && errno != EINTR)) break;
} while (1);
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);
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()提供的内容填充一个timespec结构。有各种评论称,反复调用host_get_clock_service()是昂贵的。但启动线程也是昂贵的。
真正的解决方法是让苹果实现整个POSIX规范,而不仅仅是强制性部分。仅实现POSIX的强制性部分,然后声称符合POSIX标准,这只会让每个人都面临着半破碎的操作系统和大量的解决方法,如上述可能具有不理想的性能。
以上所有内容都说了,我放弃在Mac OSX和Linux上使用本地信号量(Sys V和POSIX)。它们在很多非常不幸的方式中都是有问题的。其他人也应该放弃它们。(我不会放弃这些操作系统上的信号量,只是本地实现。)无论如何,现在每个人都有一个sem_timedwait()实现,没有商业限制,其他人可以随意复制粘贴。
sem_wait
的想法吗? - Zohar81