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.
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
Wie schreiben Sie es um, um eine Zustandsvariable zu verwenden, die kreuzweise funktioniert, irgendjemand? – rogerdpack