Pregunta

Estoy intentando portar un proyecto (desde Linux) que utiliza semáforos a Mac OS X, sin embargo, algunos de los semáforos posix no están implementados en Mac OS X

El que golpeé en este puerto es sem_timedwait()

No sé mucho sobre semáforos, pero de las páginas de manual sem_wait () parece estar cerca de sem_timedwait y está implementado

De las páginas del manual

  La función

sem_timedwait () deberá   bloquear el semáforo al que hace referencia

   sem como en la función sem_wait () .   Sin embargo, si el semáforo no puede ser
  bloqueado sin esperar a otro   proceso o hilo para desbloquear el
  semáforo realizando un sem_post ()   función, esta espera será ter-
  mined cuando el tiempo de espera especificado   expira

Desde mi comprensión limitada de cómo funcionan los semáforos, puedo ver que sem_timedwait () es más seguro, pero aún así debería poder usar sem_wait ()

¿Es esto correcto? Si no, ¿qué otras alternativas tengo ...

Gracias

¿Fue útil?

Solución

Es probable que el tiempo de espera sea importante para el funcionamiento del algoritmo. Por lo tanto, el simple uso de sem_wait () podría no funcionar.

Puede usar sem_trywait () , que regresa de inmediato en todos los casos. Luego puede hacer un bucle y usar un intervalo de reposo que elija, cada vez disminuyendo el tiempo de espera total hasta que se agote el tiempo de espera o se adquiera el semáforo.

Una solución mucho mejor es reescribir el algoritmo para usar una variable de condición, y luego puede usar pthread_cond_timedwait () para obtener el tiempo de espera apropiado.

Otros consejos

¿Ha considerado usar el tiempo de ejecución portátil apache? Está preinstalado en cada Mac OS X Box y muchas distribuciones de Linux y viene con un contenedor neutral de plataforma alrededor de la concurrencia de subprocesos, que funciona incluso en MS Windows:

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

Otra alternativa más puede ser usar sem_timedwait.c implementación por Keith Shortridge del grupo de software del Observatorio Astronómico Australiano.

Desde el archivo fuente:

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

¿Podría intentar imitar la funcionalidad de la llamada sem_timedwait () iniciando un temporizador en otro subproceso que llame a sem_post () después de que el temporizador caduque si no ha sido llamado por el subproceso primario que supuestamente llama a sem_post ( )?

Creo que la solución más simple es usar sem_wait () en combinación con una llamada a alarma () para despertar, abortar la espera. Por ejemplo:

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

Un problema es que la alarma toma segundos como entrada, por lo que la espera programada puede ser demasiado larga en su caso.

- aghiles

Solía ??usar semáforos con nombre en OSX, pero ahora sem_timedwait no está disponible y sem_init y sus amigos están en desuso. Implementé semáforos usando pthread mutex y condiciones de la siguiente manera que funcionan para mí (OSX 10.13.1). Es posible que tenga que hacer una tabla handle vs struct y buscar el tipo sem_t si no puede contener un ptr (es decir, los punteros son 64 bits y sem_t es 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

Si solo puede usar MP API:

  • MPCreateSemaphore / MPDeleteSemaphore
  • MPSignalSemaphore / MPWaitOnSemaphore

MPWaitOnSemaphore existe con kMPTimeoutErr si se excede el tiempo de espera especificado sin señalización.

Estaba planeando usar la siguiente función como reemplazo, pero luego descubrí que sem_getvalue () también estaba en desuso y no funcionaba en OSX. Puede usar el siguiente código ligeramente no probado bajo una licencia MIT o LGPL (su elección).

#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 tiene más sentido que SIGUSR2 como aparentemente otro ejemplo aquí usa (no me molesté en mirarlo). SIGALRM se reserva principalmente para llamadas de alarma (), que son prácticamente inútiles cuando se desea una resolución inferior a un segundo.

Este código primero intenta adquirir el semáforo con sem_trywait (). Si eso tiene éxito de inmediato, entonces se rescata. De lo contrario, inicia un hilo que es donde se implementa el temporizador a través de pthread_cond_timedwait (). El booleano MxSignaled se usa para determinar el estado de tiempo de espera.

También puede encontrar útil esta función relevante para llamar a la implementación sem_timedwait () anterior (nuevamente, MIT o LGPL, su elección):

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
}

Ayuda a completar una estructura de tiempo específico con lo más parecido a lo que puede proporcionar clock_gettime (). Hay varios comentarios por ahí que llamar a host_get_clock_service () repetidamente es costoso. Pero iniciar un hilo también es costoso.

La solución real es que Apple implemente toda la especificación POSIX, no solo las partes obligatorias. Implementar solo los bits obligatorios de POSIX y luego reclamar el cumplimiento de POSIX solo deja a todos con un sistema operativo medio roto y toneladas de soluciones alternativas como las anteriores que pueden tener un rendimiento menos que ideal.

Lo dicho anteriormente, estoy renunciando a los semáforos nativos (tanto Sys V como POSIX) en Mac OSX y Linux. Se rompen de muchas maneras bastante desafortunadas. Todos los demás también deberían renunciar a ellos. (No estoy renunciando a los semáforos en esos sistemas operativos, solo a las implementaciones nativas). De todos modos, ahora todos tienen una implementación sem_timedwait () sin restricciones comerciales que otros pueden copiar y pegar al contenido de su corazón.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top