2009-03-13 10 views
9

ich Port versuche ein Projekt (von Linux), die Semaphore auf Mac OS X jedoch einige der Posix-Semaphore verwendet werden unter Mac OS XPOSIX Semaphore auf Mac OS X: sem_timedwait alternative

implementiert derjenige, der ich traf in diesem Port ist sem_timedwait()

ich weiß nicht viel über Semaphore sondern von den Manpages sem_wait() scheint sem_timedwait nahe zu sein und es ist

von den man-Seiten implementiert

sem_timedwait() Funktion wird die Semaphore sperren referenziert von
sem wie in der sem_wait() Funktion. Wenn jedoch die Semaphore nicht
ohne Warten auf einen anderen Prozess gesperrt werden können oder den
Semaphor zu entsperren Faden durch eine sem_post() Funktion durchführt, wird diese Warte ter- sein
kontaminierte wenn das Timeout angegebene

abläuft

von meinem begrenzten Verständnis davon, wie semphores arbeiten kann ich sehen, dass sem_timedwait() ist sicherer, aber ich soll noch in der Lage sein sem_wait()

I zu verwenden S ist das korrekt? Wenn nicht, was andere Alternativen habe ich ...

Dank

Antwort

6

Es ist wahrscheinlich, dass die Zeitüberschreitung für die Funktion des Algorithmus wichtig ist. Daher funktioniert die Verwendung von sem_wait() möglicherweise nicht.

Sie könnten sem_trywait() verwenden, die in allen Fällen sofort zurückkehrt. Sie können dann eine Schleife ausführen und ein von Ihnen ausgewähltes Schlafintervall verwenden, wobei Sie jedes Mal das gesamte Zeitlimit verringern, bis entweder das Zeitlimit abgelaufen ist oder das Semaphor erworben wurde.

Eine viel bessere Lösung ist, den Algorithmus neu zu schreiben, um eine Bedingungsvariable zu verwenden, und dann können Sie pthread_cond_timedwait() verwenden, um die entsprechende Zeitüberschreitung zu erhalten.

+0

Eine Schleife mit trywait mit einem Schlaf funktioniert nicht, da ein Prozess jedes Mal seine Position in der Warteschlange verliert. Wenn viele Threads oder Prozesse versuchen, einen Semaphor zu sperren, würden einige von ihnen immer darauf treffen, wenn sie gesperrt sind, und somit eine Zeitüberschreitung. Eine aktive Schleife könnte funktionieren, aber es ist keine Lösung. – Eugene

+0

Wie schreiben Sie es um, um eine Zustandsvariable zu verwenden, die kreuzweise funktioniert, irgendjemand? – rogerdpack

1

Könnten Sie versuchen, die Funktionalität des sem_timedwait() Aufruf zur Nachahmung durch einen Timer in einem anderen Thread beginnend sem_post(), nachdem der Timer ruft erlischt, wenn es wurde nicht von dem primären Thread aufgerufen, der sem_post() aufrufen soll?

0

Wenn Sie verwenden können, nur MP API:

  • MPCreateSemaphore/MPDeleteSemaphore
  • MPSignalSemaphore/MPWaitOnSemaphore

MPWaitOnSemaphore existiert mit kMPTimeoutErr, wenn das angegebene Zeitlimit ohne Signalisierung überschritten wird.

+0

Leider ist die MultiProcessing API in 10.7 veraltet. – MaddTheSane

1

Ich denke, die einfachste Lösung ist, sem_wait() in Kombination mit einem Aufruf von alarm() zu verwenden, um die Wartezeit abzubrechen. Zum Beispiel:

Ein Problem ist, dass der Alarm Sekunden dauert, damit die Wartezeit in Ihrem Fall zu lang ist.

- aghiles

2

Noch eine weitere Alternative sein kann, die sem_timedwait.c Implementierung von Keith Shortridge der Australian Astronomical Observatory Softwaregruppe zu verwenden.

aus der Quelldatei:

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

Link benötigt Aktualisierung, zeigt 404 – cbinder

+0

Es ist jetzt bei https://github.com/attie/libxbee3/blob/master/xsys_darwin/sem_timedwait.c –

0

Ich wurde über die Verwendung der folgenden Funktion als Ersatz planen, aber dann entdeckte ich, dass sem_getvalue() wurde ebenfalls als veraltet und nicht-funktionale auf OSX. Sie können den folgenden leicht ungeprüften Code unter einer MIT- oder LGPL-Lizenz (Ihrer Wahl) verwenden.

#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 macht mehr Sinn als SIGUSR2 als ein weiteres Beispiel hier offenbar verwendet (ich nicht die Mühe, es zu betrachten). SIGALRM ist hauptsächlich für alarm() -Aufrufe reserviert, die praktisch nutzlos sind, wenn Sie eine Auflösung von unter einer Sekunde wünschen.

Dieser Code versucht zuerst, den Semaphor mit sem_trywait() zu erfassen. Wenn das sofort gelingt, dann schießt es aus. Andernfalls wird ein Thread gestartet, in dem der Timer über pthread_cond_timedwait() implementiert wird. Der Boolean MxSignaled wird verwendet, um den Timeout-Status zu bestimmen.

Sie können auch diese entsprechende Funktion nützlich für den Aufruf der oben sem_timedwait() Implementierung (wieder, MIT oder LGPL, Ihre Wahl) finden:

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 
} 

mit dem am nächsten, was eine timespec Struktur hilft bevölkern, in welchem ​​clock_gettime() kann liefern. Es gibt verschiedene Kommentare da draußen, dass das wiederholte Aufrufen von host_get_clock_service() teuer ist. Aber einen Thread zu starten ist auch teuer.

Der eigentliche Fix ist für Apple, um die gesamte POSIX-Spezifikation zu implementieren, nicht nur die obligatorischen Teile. Das Implementieren nur der obligatorischen Bits von POSIX und dann das Einfordern der POSIX-Konformität lässt nur ein halb gebrochenes OS und Tonnen von Workarounds wie dem oben genannten, die eine nicht optimale Leistung haben können.

Die oben genannten, ich gebe auf native Semaphoren (sowohl Sys V und POSIX) auf Mac OSX und Linux. Sie sind ziemlich unglücklich gebrochen.Alle anderen sollten sie auch aufgeben. (Ich gebe Semaphoren auf diesen Betriebssystemen nicht auf, nur die nativen Implementierungen.) Jedenfalls hat jetzt jeder eine sem_timedwait() Implementierung ohne kommerzielle Einschränkungen, die andere nach Herzenslust kopieren können.

0

Früher verwendete ich unter OSX benannte Semaphore, aber jetzt ist sem_timedwait nicht verfügbar und sem_init und Freunde sind veraltet. Ich habe Semaphore mit Pthread Mutex und Bedingungen wie folgt implementiert, die für mich funktionieren (OSX 10.13.1). Sie könnten einen Griff vs struct Tisch zu machen und den sem_t Typen nachschlagen, wenn es nicht ein ptr darin halten kann (dh Zeiger sind 64bits und sem_t ist 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