Mac OS X上的POSIX信号量:sem_timedwait替代方案

时间:2009-03-13 01:10:58

标签: macos unix posix semaphore

我正在尝试将使用Semaphores的项目(从linux)移植到Mac OS X,但是某些posix信号量并未在Mac OS X上实现

我在此端口中遇到的是sem_timedwait()

我对信号量知之甚少,但是从手册页sem_wait()似乎接近sem_timedwait并且已经实现了

从手册页

  

sem_timedwait()功能必须   锁定由
引用的信号量   与sem函数中的sem_wait()一样。   但是,如果信号量不能为
  锁定而不等待另一个   进程或线程解锁   通过执行sem_post()信号量   功能,这个等待应该是   指定的超时时间   到期

根据我对semphores如何工作的有限理解,我可以看到sem_timedwait()更安全,但我仍然可以使用sem_wait()

这是对的吗?如果不是我还有其他选择......

由于

9 个答案:

答案 0 :(得分:6)

超时很可能对算法的运行很重要。因此,仅使用sem_wait()可能无效。

你可以使用sem_trywait(),它会在所有情况下立即返回。然后,您可以循环,并使用您选择的休眠间隔,每次减少总超时,直到超时或获取信号量为止。

更好的解决方案是重写算法以使用条件变量,然后您可以使用pthread_cond_timedwait()来获得适当的超时。

答案 1 :(得分:3)

您是否考虑过使用apache可移植运行时?它预装在每个Mac OS X Box和许多Linux发行版上,它带有一个围绕线程并发的平台中立包装,甚至可以在MS Windows上运行:

http://apr.apache.org/docs/apr/1.3/group__apr__thread__cond.html

答案 2 :(得分:2)

另一种选择可能是使用sem_timedwait.c 由澳大利亚天文台软件组的Keith Shortridge实施。

来自源文件:

/*
*                       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.
*/

答案 3 :(得分:1)

你是否可以尝试模拟sem_timedwait()调用的功能,方法是在计时器到期后调用sem_post()的另一个线程中启动一个计时器,如果它没有被应该调用sem_post的主线程调用( )?

答案 4 :(得分:1)

我认为最简单的解决方案是将sem_wait()与对alarm()的调用结合使用以唤醒中止等待。例如:

alarm(2);
int return_value = sem_wait( &your_semaphore );
if( return_value == EINTR )
   printf( "we have been interrupted by the alarm." );

一个问题是警报需要几秒钟作为输入,因此在您的情况下,定时等待时间可能太长。

- aghiles

答案 5 :(得分:1)

我以前在OSX上使用命名信号量,但现在sem_timedwait不可用,并且不推荐使用sem_init和friends。我使用pthread mutex实现了信号量,条件如下,对我有用(OSX 10.13.1)。您可能必须创建一个句柄vs结构表,如果它不能在其中保存ptr,则查找sem_t类型(即指针是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

答案 6 :(得分:0)

如果您可以使用MP API:

  • MPCreateSemaphore / MPDeleteSemaphore
  • MPSignalSemaphore / MPWaitOnSemaphore

MPWaitOnSemaphorekMPTimeoutErr一起存在,如果超过指定的超时而没有发出信号。

答案 7 :(得分:0)

我计划使用以下函数作为替换,但后来我发现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
}

帮助填充timepec结构,其中最接近的是clock_gettime()可以提供的结构。有各种各样的评论,反复调用host_get_clock_service()很昂贵。但是启动一个线程也很昂贵。

真正的解决方案是让Apple实施整个POSIX规范,而不仅仅是强制性部分。仅实现POSIX的强制性位,然后声称POSIX合规性只会让每个人都有一个半破坏的操作系统和大量的解决方案,如上所述可能具有不太理想的性能。

以上都说过,我放弃了Mac OSX和Linux上的原生信号量(Sys V和POSIX)。它们以相当不幸的方式被打破。其他人也应该放弃他们。 (我不会放弃那些操作系统上的信号量,只是原生实现。)无论如何,现在每个人都有一个没有商业限制的sem_timedwait()实现,其他人可以将意大利面复制到他们心中的内容。

答案 8 :(得分:0)

一种选择是使用低级信号量马赫API:

#include <mach/semaphore.h>

semaphore_create(...)

semaphore_wait(...)
semaphore_timedwait(...)
semaphore_signal(...)

semaphore_destroy(...)

它在libuv BTW中使用。

参考: