2012-02-20 1 views
2

Si je mets un set_wait_callback sur un boost::unique_future, est-il garanti de ne fonctionner qu'une seule fois?boost :: future - wait_callback est-il garanti pour n'être invoqué qu'une seule fois?

Je suis un peu suspect car quand on regarde le code source que je trouve ce qui suit:

struct relocker 
{ 
    boost::unique_lock<boost::mutex>& lock; 

    relocker(boost::unique_lock<boost::mutex>& lock_): 
     lock(lock_) 
    { 
     lock.unlock(); 
    } 
    ~relocker() 
    { 
     lock.lock(); 
    } 
private: 
    relocker& operator=(relocker const&); 
}; 

void do_callback(boost::unique_lock<boost::mutex>& lock) 
{ 
    if(callback && !done) 
    { 
     boost::function<void()> local_callback=callback; 
     relocker relock(lock); // unlock mutex? 
     local_callback(); 
    } 
}  

void wait(bool rethrow=true) 
{ 
    boost::unique_lock<boost::mutex> lock(mutex); 
    do_callback(lock); 
    while(!done) 
    { 
     waiters.wait(lock); 
    } 
    if(rethrow && exception) 
    { 
     boost::rethrow_exception(exception); 
    } 
} 

Où en do_callback le mutex est en fait déverrouillé alors que le rappel est invoquée de ma compréhension peut conduire à le rappel étant appelé plusieurs fois si plusieurs threads appellent la fonction wait?

Le rappel peut-il être appelé plusieurs fois? Est-ce par conception? Ou est-ce que je manque quelque chose?

La raison pour laquelle je suis un peu surpris que dans la norme C++ 11 le async(std::launch::deferred, ...) (auquel set_wait_callback est un cousin), semble avoir seule garantie d'invocation:

§30.6.8

L'état partagé n'est pas prêt tant que la fonction n'est pas terminée. Le premier appel à une fonction d'attente non chronométré (30.6.4) sur un objet de retour asynchrone faisant référence à cet état partagé invoquera la fonction différée dans le thread qui a appelé la fonction d'attente.

Répondre

2

Je pense que votre soupçon est fondé. Le code devrait ressembler à

void do_callback(boost::unique_lock<boost::mutex>& lock) 
{ 
    if(callback && !done) 
    { 
     boost::function<void()> local_callback=callback; 
     callback=boost::function<void()>; 
     relocker relock(lock); // unlock mutex? 
     local_callback(); 
    } 
}  

ou même

void do_callback(boost::unique_lock<boost::mutex>& lock) 
{ 
    if(callback && !done) 
    { 
     boost::function<void()> local_callback=boos::move(callback); 
     relocker relock(lock); // unlock mutex? 
     local_callback(); 
    } 
}  

une fois Boost.Function soutiendra la sémantique de mouvement.

Cela a encore quelques problèmes car un autre thread pourrait appeler set_wait_callback, donc le callback pourrait être réaffecté et deux callbacks pourraient être appelés. Il semble qu'un état supplémentaire soit nécessaire pour indiquer si le rappel a déjà été fait.

void do_callback(boost::unique_lock<boost::mutex>& lock) 
{ 
    if(callback && ! callback_done && !done) 
    { 
     boost::function<void()> local_callback=callback; 
     callback_done=true; 
     relocker relock(lock); // unlock mutex? 
     local_callback(); 
    } 
}  

BTW, set_wait_callback n'est pas adapté aux threads.

template<typename F,typename U> 
    void set_wait_callback(F f,U* u) 
    { 
     callback=boost::bind(f,boost::ref(*u)); 
    } 

et doivent être protégés

template<typename F,typename U> 
    void set_wait_callback(F f,U* u) 
    { 
     boost::lock_guard<boost::mutex> lock(mutex); 
     callback=boost::bind(f,boost::ref(*u)); 
    } 

S'il vous plaît, pourriez-vous créer un ticket Trac pour stimuler Thread cette question ne soit pas perdu?

+0

https://svn.boost.org/trac/boost/ticket/7798 – ronag

+0

Le problème est plus complexe qu'il n'y paraît à première vue. De la documentation, il semble que le rappel doit être appelé chaque fois qu'une fonction d'attente est appelée. "Effets: Stockez une copie de f avec le résultat asynchrone associé à * ceci comme un rappel d'attente.Cela remplacera tout magasin de rappel d'attente existant à côté de ce résultat.Si un thread appelle par la suite l'une des fonctions d'attente sur un avenir ou un coup de pouce :: shared_future associé à ce résultat, et le résultat n'est pas prêt, f (* this) doit être appelé. " –

Questions connexes