Семафоры POSIX на Mac OS X: альтернатива sem timedwait

Я пытаюсь портировать проект (из linux), который использует семафоры для Mac OS X, однако некоторые из семафоров posix не реализованы на Mac OS X

тот, который я ударил в этом порту sem_timedwait()

Я не много знаю о семафорах, но из man pages sem_wait() Кажется, близко к sem_timedwait и это реализовано

из man-страницы

будет заблокировать семафор, на который ссылается
sem как в

8 ответов


вероятно, что тайм-аут важен для работы алгоритма. Поэтому просто используя sem_wait() может не работать.

можно использовать sem_trywait(), который возвращается сразу во всех случаях. Затем можно выполнить цикл и использовать выбранный интервал сна, каждый раз уменьшая общее время ожидания до тех пор, пока не закончится время ожидания или не будет получен семафор.

гораздо лучшим решением является переписать алгоритм для использования переменной условия, а затем вы можете использовать pthread_cond_timedwait() чтобы получить соответствующий тайм-аут.


вы рассматривали возможность использования портативной среды выполнения apache? Он предустановлен на каждом Mac OS X Box и многих дистрибутивах Linux, и он поставляется с нейтральной оболочкой платформы вокруг параллелизма потоков, которая работает даже на MS Windows:

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


еще одной альтернативой может быть использование sem_timedwait.c реализация Кит 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.
*/

не могли бы вы попытаться имитировать функциональность вызова sem_timedwait (), запустив таймер в другом потоке, который вызывает sem_post() после истечения таймера, если он не был вызван основным потоком, который должен вызвать sem_post()?


Я думаю, что самое простое решение-использовать 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


я использовал именованные семафоры в OSX, но теперь sem_timedwait недоступен, а sem_init и друзья устарели. Я реализовал семафоры, используя мьютекс pthread и следующие условия, которые работают для меня (OSX 10.13.1). Возможно, вам придется сделать дескриптор vs struct table и посмотреть тип sem_t, если он не может содержать ptr в нем (т. е. указатели-64bits, а 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

с 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 в основном зарезервирован для вызовов 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
}

помогает заполнить структуру timespec наиболее близкой к тому, что может предоставить clock_gettime (). Существуют различные комментарии, что вызов host_get_clock_service() неоднократно является дорогостоящим. Но запуск нити также дорог.

реальное исправление заключается в том, что Apple реализует всю спецификацию POSIX, а не только обязательные части. Реализация только обязательных бит POSIX, а затем утверждение соответствия POSIX просто оставляет всех с наполовину сломанной ОС и тоннами обходных путей, подобных приведенным выше, которые могут иметь менее идеальную производительность.

вышесказанное, я отказываюсь от собственных семафоров (как Sys V, так и POSIX) как на Mac OSX, так и на Linux. Они сломаны несколькими довольно неудачными способами. Все остальные тоже должны отказаться от них. (Я не отказываюсь от семафоров на этих Осах, только от собственных реализаций.) Во всяком случае, теперь у каждого есть реализация sem_timedwait () без коммерческой ограничения, которые другие могут копировать-паста в их сердце.