Skip to content

vtkThreadedCallbackQueue: last touches

This MR makes the following changes to vtkThreadedCallbackQueue:

  • Storing the args as native type of the arguments of function f when relevant
    • The arguments are now stored in the type expected by the function f when the argument expected by f is an lvalue referencee. In such circumstances, this allows to store a copy in the type expected by f seamlessly.
  • Removing Start and Stop from public API
    • This API can be dangerous because the user could call Wait() on a token an end up in a deadlock if Stop() was called anywhere. Stop and Start are now private.
    • There also was a bug in SetNumberOfThreads: the thread id was not instantiated correctly when resizing the threads when the queue was already running.
  • Removing Stop API
    • Since the queue is started at construction and cannot be stopped, there is no need to keep this API. This also lightens the code a bit because we can remove IsRunning
  • Optimize PushDependent
    • PushDependent used to push a function just waiting on all input tokens before executing. This is wasteful for the thread waiting.
    • To make things smoother, we instroduce a shared state between an invoker an its token (called InvokerTokenSharedState). When a function needs to wait on a token, it notifies its associated invoker that it is waiting for it to finish by inserting the function's new token in DependentTokens in the shared state of the invoker it's waiting on. If there are any invokers we need to wait on that is not finished, the new invoker is pushed inside a waiting list called InvokersOnHold.
    • On the invoker side, when it has finished, it goes through all its DependentTokens. It fetches the shared state of each token, and looks at the counter NumberOfPriorTokensRemaining which has been incremented for each unfinished token this token found in its token list. The invoker decrements this counter, and when the counter reaches 0, it means that the current invoker is the last invoker this token was waiting on. So the invoker can move this token's invoker in the running queue. We put it on the front as this invoker's execution already has been delayed.
  • Replacing Token by SharedFuture
    • Having a std::shared_future instance made that independent locks were done when they could have been combined. The vtkCallbackToken was actually already near to have all the functionalities of a std::shared_future.
    • We also wanted to avoid std::shared_future::wait_for(0), as this call blocks the current thread. In our implementation, if the invoker is not done yet, you can call an equivalent of wait_for(0) as IsReady without actually blocking the thread.
  • Adding vtkThreadedCallbackQueue::Wait API
    • The new Wait function takes a container of shared futures as input. It is in general less ressource consuming than calling Wait on each shared future as it can block at most once.
  • Specializing std::hash<vtkSmartPointer<T>> to allow to create hash maps of smart pointers
Edited by Yohann Bearzi (Kitware)

Merge request reports