TaskRunner: Replace use of std::function with std::packaged_task.
The task runner is meant to run a "task" once. Thus, client code should
expect that it can std::bind() with movable types (e.g.,
std::unique_ptr<T>) as arguments. However, the TaskRunner uses
std::function, which only works when all std::bind() arguments are
copyable.
The solution is to replace std::function with std::packaged_task, the
latter of which is purposely designed to be a run-once variant of
std::function; and it also supports move-only types in the bind state.
This change also cleans-up a few other details around the switch:
1. std::packaged_task uses an explicit ctor, whereas std::function
provided an implicit one. Thus, TaskRunner::PostTask() has become a
templated member function, an inline wrapper around calling the
std::packaged_task ctor explicitly.
2. TaskRunnerImpl's use of std::priority_queue for delayed tasks was
replaced with std::multimap, because the API of the former did not
provide non-const access to its elements (which was required for move
operations).
3. Minor tweaks around comments and resource/allocation concerns in
touched code.
Change-Id: I5fe9a2e3a3c2d8d69b564575f81c4a7f4a80b265
Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/1646564
Commit-Queue: Yuri Wiitala <miu@chromium.org>
Reviewed-by: Ryan Keane <rwkeane@google.com>
Reviewed-by: Max Yakimakha <yakimakha@chromium.org>
diff --git a/platform/api/task_runner.h b/platform/api/task_runner.h
index 2f05803..611b2b5 100644
--- a/platform/api/task_runner.h
+++ b/platform/api/task_runner.h
@@ -5,7 +5,7 @@
#ifndef PLATFORM_API_TASK_RUNNER_H_
#define PLATFORM_API_TASK_RUNNER_H_
-#include <functional>
+#include <future>
#include "platform/api/time.h"
@@ -23,18 +23,33 @@
// NOTE: we do not make any assumptions about what thread tasks shall run on.
class TaskRunner {
public:
- using Task = std::function<void()>;
+ using Task = std::packaged_task<void() noexcept>;
virtual ~TaskRunner() = default;
- // Takes a Task that should be run at the first convenient time.
- virtual void PostTask(Task task) = 0;
+ // Takes any callable target (function, lambda-expression, std::bind result,
+ // etc.) that should be run at the first convenient time.
+ template <typename Functor>
+ inline void PostTask(Functor f) {
+ PostPackagedTask(Task(std::move(f)));
+ }
- // Takes a Task that should be run no sooner than "delay" time from now. Note
- // that we do not guarantee it will run precisely "delay" later, merely that
- // it will run no sooner than "delay" time from now.
- virtual void PostTaskWithDelay(Task task, Clock::duration delay) = 0;
+ // Takes any callable target (function, lambda-expression, std::bind result,
+ // etc.) that should be run no sooner than |delay| time from now. Note that
+ // the Task might run after an additional delay, especially under heavier
+ // system load. There is no deadline concept.
+ template <typename Functor>
+ inline void PostTaskWithDelay(Functor f, Clock::duration delay) {
+ PostPackagedTaskWithDelay(Task(std::move(f)), delay);
+ }
+
+ // Implementations should provide the behavior explained in the comments above
+ // for PostTask[WithDelay](). Client code may also call these directly when
+ // passing an existing Task object.
+ virtual void PostPackagedTask(Task task) = 0;
+ virtual void PostPackagedTaskWithDelay(Task task, Clock::duration delay) = 0;
};
+
} // namespace platform
} // namespace openscreen