Семафоры POSIX в Mac OS X: альтернатива sem_timedwait - PullRequest
9 голосов
/ 13 марта 2009

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

В этот порт я попал sem_timedwait()

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

Из справочных страниц

sem_timedwait() функция должна заблокировать семафор, на который ссылается
sem как в функции sem_wait(). Однако, если семафор не может быть
заблокирован, не дожидаясь другого процесс или поток, чтобы разблокировать
семафор, выполнив sem_post() функция, это ожидание должно быть ter-
минимизируется, когда указанный таймаут истекает * * тысячу двадцать-один

Из моего ограниченного понимания того, как работают семфоры, я вижу, что sem_timedwait() безопаснее, но я все еще должен иметь возможность использовать sem_wait ()

Это правильно? Если нет, какие у меня есть альтернативы ...

Спасибо

Ответы [ 8 ]

6 голосов
/ 03 мая 2009

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

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

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

3 голосов
/ 03 мая 2009

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

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

2 голосов
/ 01 марта 2013

Еще одной альтернативой может быть использование sem_timedwait.c реализация Китом Шортриджем из группы разработчиков программного обеспечения Австралийской астрономической обсерватории.

Из исходного файла:

/*
*                       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.
*/
1 голос
/ 14 февраля 2018

Раньше я использовал именованные семафоры в 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
1 голос
/ 30 декабря 2011

Я думаю, что самое простое решение - использовать sem_wait () в сочетании с вызовом alarm () для пробуждения и прерывания ожидания. Например:

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

Одна из проблем заключается в том, что в качестве входного сигнала для сигнала будильника используются секунды, поэтому время ожидания в вашем случае может быть слишком большим.

- агил

1 голос
/ 03 мая 2009

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

0 голосов
/ 19 мая 2016

Я планировал использовать следующую функцию в качестве замены, но потом обнаружил, что 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 () без коммерческих ограничений, которые другие могут копировать-вставлять в свое сердце.

0 голосов
/ 03 марта 2010

Если вы можете просто использовать MP API:

  • MPCreateSemaphore / MPDeleteSemaphore
  • MPSignalSemaphore / MPWaitOnSemaphore

MPWaitOnSemaphore существует с kMPTimeoutErr, если указанное время ожидания превышено без сигнализации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...