Question

J'essaie de porter un projet (sous Linux) utilisant des sémaphores vers Mac OS X, mais certains sémaphores posix ne sont pas implémentés sous Mac OS X

Celui que j'ai frappé sur ce port est sem_timedwait ()

Je ne connais pas grand chose aux sémaphores, mais dans les pages de manuel sem_wait () semble être proche de sem_timedwait et est implémenté

.

À partir des pages de manuel

  La fonction

sem_timedwait () doit   verrouille le sémaphore référencé par
   sem comme dans la fonction sem_wait () .   Cependant, si le sémaphore ne peut pas être
  verrouillé sans attendre un autre   processus ou thread pour déverrouiller le
  sémaphore en effectuant un sem_post ()   fonction, cette attente doit être ter-
  minated lorsque le délai d'attente spécifié   expire

De par ma compréhension limitée du fonctionnement des semphores, je peux voir que sem_timedwait () est plus sûr, mais je devrais quand même pouvoir utiliser sem_wait ()

Est-ce correct? Si non, quelles autres alternatives ai-je ...

Merci

Était-ce utile?

La solution

Il est probable que le délai d'attente est important pour le fonctionnement de l'algorithme. Par conséquent, utiliser sem_wait () peut ne pas fonctionner.

Vous pouvez utiliser sem_trywait () , qui revient immédiatement dans tous les cas. Vous pouvez ensuite effectuer une boucle et utiliser un intervalle de sommeil que vous choisissez, décrémentant chaque fois le délai total jusqu'à l'expiration du délai imparti ou à l'acquisition du sémaphore.

Une meilleure solution consiste à réécrire l'algorithme pour utiliser une variable de condition. Vous pouvez ensuite utiliser pthread_cond_timedwait () pour obtenir le délai d'expiration approprié.

Autres conseils

Avez-vous envisagé d'utiliser le runtime portable Apache? Il est préinstallé sur chaque Mac OS X Box et sur de nombreuses distributions Linux. Il est livré avec un wrapper neutre pour la plate-forme, qui fonctionne même sous MS Windows:

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

Une autre solution consiste à utiliser le sem_timedwait.c mise en œuvre par Keith Shortridge du groupe de logiciels de l’Observatoire australien de l’astronomie.

À partir du fichier source:

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

Pourriez-vous essayer de reproduire les fonctionnalités de l’appel sem_timedwait () en démarrant une minuterie dans un autre thread qui appelle sem_post () après son expiration, si elle n’a pas été appelée par le thread principal censé appeler sem_post ( )?

Je pense que la solution la plus simple consiste à utiliser sem_wait () en combinaison avec un appel à alarm () pour vous réveiller et annuler l’attente. Par exemple:

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

L’un des problèmes est que l’alarme prend quelques secondes en entrée et que, par conséquent, l’attente programmée est trop longue.

- aghiles

J'avais l'habitude d'utiliser des sémaphores nommés sur OSX, mais maintenant, sem_timedwait n'est plus disponible et sem_init et ses amis sont obsolètes. J'ai implémenté les sémaphores en utilisant pthread mutex et les conditions suivantes qui fonctionnent pour moi (OSX 10.13.1). Vous devrez peut-être créer une table vs structure et rechercher le type sem_t s’il ne peut pas contenir de ptr (c.-à-d. Que les pointeurs ont 64 bits et que sem_t a 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 vous ne pouvez utiliser que l'API MP:

  • MPCreateSemaphore / MPDeleteSemaphore
  • MPSignalSemaphore / MPWaitOnSemaphore

MPWaitOnSemaphore existe avec kMPTimeoutErr si le délai d'expiration spécifié est dépassé sans signalisation.

Je prévoyais d'utiliser la fonction suivante en remplacement, mais j'ai ensuite découvert que sem_getvalue () était également obsolète et non fonctionnel sur OSX. Vous êtes libre d'utiliser le code suivant, légèrement non testé, sous une licence MIT ou LGPL (à vous de choisir).

#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 est plus logique que SIGUSR2, comme le montre un autre exemple utilisé ici (je ne me suis pas soucié de le regarder). SIGALRM est principalement réservé aux appels alarm (), qui sont pratiquement inutiles lorsque vous souhaitez une résolution inférieure à la seconde.

Ce code tente d’abord d’acquérir le sémaphore avec sem_trywait (). Si cela réussit immédiatement, il échouera. Sinon, il démarre un thread sur lequel le minuteur est implémenté via pthread_cond_timedwait (). Le booléen MxSignaled est utilisé pour déterminer l'état du délai d'attente.

Vous pouvez également trouver cette fonction pertinente utile pour appeler la mise en œuvre sem_timedwait () ci-dessus (encore une fois, MIT ou LGPL, votre choix):

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
}

Aide à renseigner une structure timespec avec ce qui se rapproche le plus de ce que clock_gettime () peut fournir. Il existe de nombreux commentaires selon lesquels appeler host_get_clock_service () à plusieurs reprises coûte cher. Mais démarrer un fil coûte également cher.

Le véritable correctif consiste pour Apple à mettre en œuvre l'intégralité de la spécification POSIX, pas seulement les éléments obligatoires. Mettre en œuvre uniquement les éléments obligatoires de POSIX, puis revendiquer la conformité POSIX, laisse tout le monde avec un système d'exploitation à moitié défectueux et des tonnes de solutions de contournement similaires à celles décrites ci-dessus, dont les performances risquent d'être moins qu'idéales.

Ceci dit, j’abandonne les sémaphores natifs (Sys V et POSIX) sous Mac OSX et Linux. Ils sont brisés de plusieurs façons plutôt malheureuses. Tout le monde devrait les abandonner aussi. (Je n'abandonne pas les sémaphores sur ces systèmes d'exploitation, mais simplement les implémentations natives.) Quoi qu'il en soit, tout le monde a maintenant une implémentation sem_timedwait () sans restrictions commerciales que les autres peuvent copier-coller au contenu de leur coeur.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top