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.
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
Jak przerobić go, aby użyć zmiennej warunkowej, która działa proces krzyżowy, ktoś? – rogerdpack