2012-08-10 5 views
12

ich durch meine STL-Implementierung las (Standard-Ausgabe g++ 4.6.2) und kam in diesem Stück Race-Bedingung innerhalb von condition_variable:condition_variable timed_wait Implementierung korrekt

template<typename _Rep, typename _Period> 
cv_status 
wait_for(unique_lock<mutex>& __lock, 
     const chrono::duration<_Rep, _Period>& __rtime) 
{ 
    return wait_until(__lock, __clock_t::now() + __rtime); 
} 

Da __clock_t ein std::chrono::system_clock ist, sind wir auf die gebunden Launen von Dingen wie NTP (wenn die Uhr um einen Tag nach __clock_t::now() + __rtime zurückgeschoben wird, dann werden wir einen Tag warten).

Die C++ Standard (30.5.1) erscheint es richtig:

Effekte: als ob

return wait_until(lock, chrono::steady_clock::now() + rel_time);

der Boost-condition_variable Implementierung hat die gleiche Problem:

template<typename duration_type> 
bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration) 
{ 
    return timed_wait(m,get_system_time()+wait_duration); 
} 

In der Tat, die zugrunde liegende pThreads Implementierung scheint das Problem zu sein:

int pthread_cond_timedwait(pthread_cond_t *restrict cond, 
    pthread_mutex_t *restrict mutex, 
    const struct timespec *restrict abstime); 

weil abstime als angegeben "Systemzeit" keine monotone Uhr.

Also meine Frage ist: Wie würde man so etwas wie std::condition_variable::wait_for korrekt implementieren? Gibt es eine bestehende Implementierung, die das richtig macht? Oder fehlt mir etwas?

+0

Hinweis: Intern verwendet 'pthread_cond_timedwait'' gettimeofday', was ein Fehler ist, wenn Sie in der von Ihnen angegebenen Zeit ein Timeout setzen möchten: http://sourceware.org/git/?p=glibc.git;a=blob; f = nptl/pthread_cond_timedwait.c; h = 7278ec45b0eb0b48be50fe832fcd17ae988dca27; hb = HEAD –

+0

Sie sind wahrscheinlich gezwungen, einen anderen Timer-Thread zu verwenden, der eine monotone Uhr verwendet, und weckt dann Ihren Kellner auf, wenn er abläuft, bevor der Kellner ihn abbricht. – jxh

+0

Wenn ich so weit gehen muss (was ich ernsthaft hoffe, dass ich es nicht tue), würde ich lieber ein Dreh warten in meiner 'wait_for' Funktion (es sieht so aus, als hätte mein' sleep_for' dieses Problem nicht, wie 'nanoschlaf 'hat nicht das selbe Problem, weil' CLOCK_MONOTONIC' korrekt verwendet wird. –

Antwort

8

Der Trick besteht darin, eine pthread_condattr_setclock zu verwenden, um die pthread_condattr_t zu benachrichtigen, CLOCK_MONOTONIC zu verwenden. Der C-Code dafür ist recht einfach:

#include <time.h> 
#include <pthread.h> 

#include <errno.h> 
#include <stdio.h> 

int main() 
{ 
    // Set the clock to be CLOCK_MONOTONIC 
    pthread_condattr_t attr; 
    pthread_condattr_init(&attr); 
    if (int err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) 
    { 
     printf("Error setting clock: %d\n", err); 
    } 

    // Now we can initialize the pthreads objects with that condattr 
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
    pthread_cond_t cond; 
    pthread_cond_init(&cond, &attr); 

    // when getting the time, we must poll from CLOCK_MONOTONIC 
    struct timespec timeout; 
    struct timespec now; 
    clock_gettime(CLOCK_MONOTONIC, &now); 
    timeout.tv_sec = now.tv_sec + 5; 
    timeout.tv_nsec = now.tv_nsec; 

    // business as usual... 
    pthread_mutex_lock(&mutex); 
    int rc = pthread_cond_timedwait(&cond, &mutex, &timeout); 
    if (rc == ETIMEDOUT) 
     printf("Success!\n"); 
    else 
     printf("Got return that wasn't timeout: %d\n", rc); 
    pthread_mutex_unlock(&mutex); 

    return 0; 
} 

ich diese offen für eine Weile verlassen werde, weil jemand vielleicht eine einfachere Antwort. Die Sache, über die ich hier nicht glücklich bin, ist, dass es eine wait_until ziemlich schwierig ist, mit einer Echtzeituhr zu implementieren (meine beste Lösung ist, die bereitgestellten Clock in time_point in die steady_clock Zeit zu konvertieren und von dort zu gehen dort ... es unterliegt immer noch den Bedingungen der Zeitänderung, aber wenn Sie eine Zeitüberschreitung in Echtzeit festlegen, machen Sie bereits einen schrecklichen Fehler.

+0

Ich bezweifle, dass es eine einfachere Antwort gibt, weil es eine Beschränkung der POSIX-API ist. Beachten Sie, dass nicht nur 'condition_variable' das Problem mit dem zeitgesteuerten Warten hat, sondern auch' mutex' dieses Problem hat. Außerdem ist die Situation für Mutexe noch schlimmer, weil es keine API wie 'pthread_mutexattr_setclock' gibt: http://stackoverflow.com/q/14248033/5447906. –

Verwandte Themen