#ifndef PXR_BASE_WORK_SINGULAR_TASK_H
#define PXR_BASE_WORK_SINGULAR_TASK_H
#include "pxr/pxr.h"
#include <atomic>
#include <functional>
#include <type_traits>
PXR_NAMESPACE_OPEN_SCOPE
class WorkDispatcher;
class WorkArenaDispatcher;
class WorkSingularTask
{
public:
WorkSingularTask(WorkSingularTask const &) = delete;
WorkSingularTask &operator=(WorkSingularTask const &) = delete;
#ifdef doxygen
template <class Callable, class A1, class A2, ... class AN>
WorkSingularTask(WorkDispatcher &dispatcher,
Callable &&c, A1 &&a1, A2 &&a2, ... AN &&aN);
template <class Callable, class A1, class A2, ... class AN>
WorkSingularTask(WorkArenaDispatcher &dispatcher,
Callable &&c, A1 &&a1, A2 &&a2, ... AN &&aN);
#else
template <class Callable, class... Args>
WorkSingularTask(WorkDispatcher &d, Callable &&c, Args&&... args)
: _waker(_MakeWaker(d, std::bind(std::forward<Callable>(c),
std::forward<Args>(args)...)))
, _count(0) {}
template <class Callable, class... Args>
WorkSingularTask(WorkArenaDispatcher &d, Callable &&c, Args&&... args)
: _waker(_MakeWaker(d, std::bind(std::forward<Callable>(c),
std::forward<Args>(args)...)))
, _count(0) {}
#endif
inline void Wake() {
if (++_count == 1)
_waker(_count);
}
private:
template <class Dispatcher, class Fn>
struct _Waker {
explicit _Waker(Dispatcher &d, Fn &&fn)
: _dispatcher(d), _fn(std::move(fn)) {}
void operator()(std::atomic_size_t &count) const {
_dispatcher.Run(
[this, &count]() {
size_t old = count;
do { _fn(); } while (
!count.compare_exchange_strong(old, 0));
});
}
Dispatcher &_dispatcher;
Fn _fn;
};
template <class Dispatcher, class Fn>
static std::function<void (std::atomic_size_t &)>
_MakeWaker(Dispatcher &d, Fn &&fn) {
return std::function<void (std::atomic_size_t &)>(
_Waker<Dispatcher, typename std::decay<Fn>::type>(
d, std::forward<Fn>(fn)));
}
std::function<void (std::atomic_size_t &)> _waker;
std::atomic_size_t _count;
};
PXR_NAMESPACE_CLOSE_SCOPE
#endif