Mac OS X 上的 POSIX 信号量:sem_timedwait 替代方案
题
我正在尝试将一个使用信号量的项目(从 Linux)移植到 Mac OS X,但是某些 posix 信号量在 Mac OS X 上未实现
我在这个端口打到的是 sem_timedwait()
我对信号量了解不多,但从手册页来看 sem_wait()
似乎接近 sem_timedwait
并已实施
从手册页
sem_timedwait()
功能应锁定信号的信号
sem
如在sem_wait()
功能。然而,如果信号量不能
锁定而无需等待其他过程或线程解锁
信号量通过执行sem_post()
函数,这个等待应该是ter-
指定超时到期时开采
从我对信号量如何工作的有限理解来看,我可以看到 sem_timedwait()
更安全,但我仍然应该能够使用 sem_wait()
它是否正确?如果没有的话我还有什么其他选择...
谢谢
解决方案
这可能是因为超时是该算法的操作是重要的。因此仅仅使用sem_wait()
可能无法正常工作。
您可以使用sem_trywait()
,它返回在所有情况下的时候了。然后可以循环,并使用该选择每次睡眠间隔,递减的总超时直到您用完超时或信号量被获取。
一个更好的解决方案是重写要使用的算法的状态变量,并且然后可以使用pthread_cond_timedwait()
来获得适当的超时。
其他提示
你有没有使用Apache便携式运行时考虑?它预装在所有Mac OS X盒和许多Linux发行版,它配备了左右线程并发了一个平台中立的包装,该作品甚至在MS Windows:
http://apr.apache.org/docs/apr/1.3 /group__apr__thread__cond.html
另一种替代方案可以是使用 sem_timedwait.c 实现由Keith邵力殊澳大利亚天文台的软件群。
从源文件:
/*
* s e m _ t i m e d w a i t
*
* Function:
* Implements a version of sem_timedwait().
*
* Description:
* Not all systems implement sem_timedwait(), which is a version of
* sem_wait() with a timeout. Mac OS X is one example, at least up to
* and including version 10.6 (Leopard). If such a function is needed,
* this code provides a reasonable implementation, which I think is
* compatible with the standard version, although possibly less
* efficient. It works by creating a thread that interrupts a normal
* sem_wait() call after the specified timeout.
*
* ...
*
* Limitations:
*
* The mechanism used involves sending a SIGUSR2 signal to the thread
* calling sem_timedwait(). The handler for this signal is set to a null
* routine which does nothing, and with any flags for the signal
* (eg SA_RESTART) cleared. Note that this effective disabling of the
* SIGUSR2 signal is a side-effect of using this routine, and means it
* may not be a completely transparent plug-in replacement for a
* 'normal' sig_timedwait() call. Since OS X does not declare the
* sem_timedwait() call in its standard include files, the relevant
* declaration (shown above in the man pages extract) will probably have
* to be added to any code that uses this.
*
* ...
*
* Copyright (c) Australian Astronomical Observatory.
* Commercial use requires permission.
* This code comes with absolutely no warranty of any kind.
*/
你能尝试通过启动该sem_post()的调用该定时器超时后,如果它没有被称为由应该调用sem_post(主线程另一个线程定时器来模仿sem_timedwait()调用的功能)?
我认为简单的解决方案是组合使用sem_wait()与报警()的调用来唤醒中止期待。例如:
alarm(2);
int return_value = sem_wait( &your_semaphore );
if( return_value == EINTR )
printf( "we have been interrupted by the alarm." );
一个问题是,报警发生秒为输入,从而定时等待可能是你的情况太长时间。
- aghiles
我曾经在 OSX 上使用命名信号量,但现在 sem_timedwait 不可用,并且 sem_init 和朋友已被弃用。我使用 pthread 互斥体和条件实现了信号量,如下所示,这对我有用(OSX 10.13.1)。您可能必须创建一个句柄与结构表,并查找 sem_t 类型(如果它不能容纳 ptr)(即指针是 64 位,sem_t 是 32 位?)
#ifdef __APPLE__
typedef struct
{
pthread_mutex_t count_lock;
pthread_cond_t count_bump;
unsigned count;
}
bosal_sem_t;
int sem_init(sem_t *psem, int flags, unsigned count)
{
bosal_sem_t *pnewsem;
int result;
pnewsem = (bosal_sem_t *)malloc(sizeof(bosal_sem_t));
if (! pnewsem)
{
return -1;
}
result = pthread_mutex_init(&pnewsem->count_lock, NULL);
if (result)
{
free(pnewsem);
return result;
}
result = pthread_cond_init(&pnewsem->count_bump, NULL);
if (result)
{
pthread_mutex_destroy(&pnewsem->count_lock);
free(pnewsem);
return result;
}
pnewsem->count = count;
*psem = (sem_t)pnewsem;
return 0;
}
int sem_destroy(sem_t *psem)
{
bosal_sem_t *poldsem;
if (! psem)
{
return EINVAL;
}
poldsem = (bosal_sem_t *)*psem;
pthread_mutex_destroy(&poldsem->count_lock);
pthread_cond_destroy(&poldsem->count_bump);
free(poldsem);
return 0;
}
int sem_post(sem_t *psem)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
pxsem->count = pxsem->count + 1;
xresult = pthread_cond_signal(&pxsem->count_bump);
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
}
int sem_trywait(sem_t *psem)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
xresult = 0;
if (pxsem->count > 0)
{
pxsem->count--;
}
else
{
xresult = EAGAIN;
}
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
return 0;
}
int sem_wait(sem_t *psem)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
xresult = 0;
if (pxsem->count == 0)
{
xresult = pthread_cond_wait(&pxsem->count_bump, &pxsem->count_lock);
}
if (! xresult)
{
if (pxsem->count > 0)
{
pxsem->count--;
}
}
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
return 0;
}
int sem_timedwait(sem_t *psem, const struct timespec *abstim)
{
bosal_sem_t *pxsem;
int result, xresult;
if (! psem)
{
return EINVAL;
}
pxsem = (bosal_sem_t *)*psem;
result = pthread_mutex_lock(&pxsem->count_lock);
if (result)
{
return result;
}
xresult = 0;
if (pxsem->count == 0)
{
xresult = pthread_cond_timedwait(&pxsem->count_bump, &pxsem->count_lock, abstim);
}
if (! xresult)
{
if (pxsem->count > 0)
{
pxsem->count--;
}
}
result = pthread_mutex_unlock(&pxsem->count_lock);
if (result)
{
return result;
}
if (xresult)
{
errno = xresult;
return -1;
}
return 0;
}
#endif
如果你可以只使用 MP API:
- MPCreateSemaphore/MPDeleteSemaphore
- MPSignalSemaphore/MPWaitOnSemaphore
MPWaitOnSemaphore
存在于 kMPTimeoutErr
如果超过指定的超时时间而没有发出信号。
我打算使用下面的函数作为替换,但后来我发现,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主要是保留给报警()调用,这是当你想一秒分辨率几乎是无用的。
此代码首先尝试与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
}
可帮助填充最接近一个timespec结构什么clock_gettime()可以提供。有各种意见,在那里,叫host_get_clock_service()反复是昂贵的。但启动一个线程也是昂贵的。
在实际的解决方法是为苹果实现整个POSIX规范,而不仅仅是必备部分。仅实现POSIX的强制位,然后声称符合POSIX刚离开大家一起解决方法的半断OS和吨像上面可能有低于理想的性能。
上面所有的说,我就在Mac OSX和Linux原生信号灯(系统V和POSIX两者)放弃。它们在相当多的,而不幸的方式打破。其他人应该对他们放弃了。 (我不是在这些操作系统上的信号灯放弃,只是在本地实现)。无论如何,现在每个人都没有其他人可以复制面食自己的心脏的内容商业的限制sem_timedwait()实现。