2009-03-13 14 views
9

Usiłuję do portu projektu (od linux), który używa semaforów do Mac OS X jednak niektóre z POSIX semafory nie są realizowane w systemie Mac OS XPOSIX Semafory w systemie Mac OS X: sem_timedwait alternatywa

Ten, który uderzyłem w tym porcie jest sem_timedwait()

nie wiem zbyt wiele o semafory ale od strony podręcznika sem_wait() wydaje się być blisko sem_timedwait i jest realizowany

od strony podręcznika

sem_timedwait() funkcja powinna zablokować semafora odwołuje
sem jak w funkcji sem_wait(). Jednakże, jeśli semafor nie może być
zablokowany bez czekania na inny proces lub wątku, aby odblokować
semafora wykonując sem_post() funkcję, to czekać będą ter-
minated gdy określony limit czasu wygasa

z mojego ograniczonego zrozumienia jak działa semphores widzę, że sem_timedwait() jest bezpieczniejsze, ale nadal powinny mieć możliwość korzystania z sem_wait()

I Czy to prawda? Jeśli nie jakie inne alternatywy mam ...

Dzięki

Odpowiedz

6

Jest prawdopodobne, że limit czasu jest ważny dla działania algorytmu. Dlatego po prostu użycie sem_wait() może nie działać.

Można użyć wartości sem_trywait(), która zwraca się od razu we wszystkich przypadkach. Następnie możesz zapętlić i użyć wybranego przedziału czasu snu, za każdym razem zmniejszając całkowity czas oczekiwania, aż skończy się limit czasu lub zostanie uzyskany semafor.

Znacznie lepszym rozwiązaniem jest przepisanie algorytmu w celu użycia zmiennej warunkowej, a następnie można użyć parametru pthread_cond_timedwait(), aby uzyskać odpowiedni limit czasu.

+0

pętlę z trywait ze snu nie będzie działać, ponieważ proces utraciłby swoją pozycję w kolejce za każdym razem. Jeśli wiele wątków lub procesów próbuje zablokować jeden semafor, niektóre z nich zawsze trafiałyby, gdy są zablokowane, a tym samym limit czasu. Zajęty pętla może działać, ale nie jest to rozwiązanie. – Eugene

+0

Jak przerobić go, aby użyć zmiennej warunkowej, która działa proces krzyżowy, ktoś? – rogerdpack

1

można spróbować naśladować funkcjonalność sem_timedwait() rozmowy przez uruchomienie zegara w innym wątku, który wywołuje sem_post() po czasomierz wygasa, jeżeli nie został wywołany przez główny wątek, który ma wywoływać sem_post()?

0

Jeśli można po prostu użyć MP API:

  • MPCreateSemaphore/MPDeleteSemaphore
  • MPSignalSemaphore/MPWaitOnSemaphore

MPWaitOnSemaphore istnieje z kMPTimeoutErr, jeśli określony limit czasu został przekroczony bez sygnalizacji.

+0

Niestety, interfejs API MultiProcessing został wycofany w wersji 10.7. – MaddTheSane

1

Myślę, że najprostszym rozwiązaniem jest użycie sem_wait() w połączeniu z wywołaniem alarmu(), aby obudzić się, przerwać oczekiwanie. Na przykład:

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

Jednym z problemów jest to, że alarm trwa sekundy jako dane wejściowe, więc czas oczekiwania może być w twoim przypadku zbyt długi.

- aghiles

2

Jeszcze inną alternatywą może być użycie realizację sem_timedwait.c przez Keith Shortridge grupy oprogramowania Australijskiego Obserwatorium Astronomicznego w.

z pliku źródłowego:

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

Aktualizacja potrzeb linków, pokazująca 404 – cbinder

+0

Teraz jest na https://github.com/attie/libxbee3/blob/master/xsys_darwin/sem_timedwait.c –

0

I planuje stosując następującą funkcję jako zamiennik, ale potem odkryłem, że sem_getvalue() również przestarzałe i niefunkcjonalne na OSX. Możesz użyć poniższego, lekko nie testowanego kodu na licencji MIT lub LGPL (do wyboru).

#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 ma więcej sensu niż SIGUSR2, jak kolejny przykład tutaj najwyraźniej używa (nie zawracałem sobie głowy patrzeniem na to). SIGALRM jest zarezerwowany głównie dla wywołań alarmowych(), które są praktycznie bezużyteczne, gdy potrzebujesz rozdzielczości drugiej sekundy.

Ten kod próbuje najpierw uzyskać semafor za pomocą sem_trzyjit(). Jeśli to natychmiast się powiedzie, to się wygrywa. W przeciwnym razie uruchamia wątek, w którym timer jest zaimplementowany za pomocą pthread_cond_timedwait(). Wartość logiczna MxSignaled służy do określania stanu limitu czasu.

Można również znaleźć tę odpowiednią funkcję użyteczną dla wywołanie powyżej sem_timedwait() realizacja (ponownie, MIT czy LGPL, do wyboru):

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 
} 

Pomaga wypełnić timespec strukturę z najbliższa rzecz do czego clock_gettime() może dostarczyć. Istnieją różne komentarze, że wywoływanie funkcji host_get_clock_service() jest wielokrotnie kosztowne. Ale uruchomienie wątku jest również drogie.

Prawdziwą poprawką jest dla Apple wdrożenie całej specyfikacji POSIX, a nie tylko części obowiązkowych. Wdrożenie tylko obowiązkowych bitów POSIX, a następnie uzyskanie zgodności z POSIXem, pozostawia po sobie tylko połowę uszkodzonego systemu operacyjnego i mnóstwo obejść podobnych do powyższych, które mogą mieć mniej niż idealną wydajność.

Powiedziałem przede wszystkim, że rezygnuję z natywnych semaforów (zarówno Sys V, jak i POSIX) zarówno w systemie Mac OS X, jak i Linux. Są podzielone na kilka raczej niefortunnych sposobów.Wszyscy inni też powinni się poddać. (Nie rezygnuję z semaforów na tych systemach operacyjnych, tylko z natywnych implementacji.) W każdym razie, teraz każdy ma implementację sem_timedwait() bez komercyjnych ograniczeń, które inni mogą kopiować-makarony do treści swoich serc.

0

Używałem nazwanych semaforów na OSX, ale teraz sem_timedwait nie jest dostępny, a sem_init i przyjaciele są nieaktualni. Zaimplementowałem semafory przy użyciu pthread mutex i warunków w następujący sposób, które działają dla mnie (OSX 10.13.1). Może trzeba zrobić uchwyt vs tabeli struct i zajrzeć do sem_t typ jeśli nie może posiadać ptr w nim (tj wskaźniki są 64bits i sem_t jest 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 
Powiązane problemy