blob: 7de22820a5337c3b710c9c646b8cc767834d4818 [file] [log] [blame]
Jordan Baylesb0c191e2019-03-26 15:49:57 -07001// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Jordan Baylesa26582d2019-07-10 14:44:58 -07005#ifndef PLATFORM_IMPL_TASK_RUNNER_H_
6#define PLATFORM_IMPL_TASK_RUNNER_H_
Jordan Baylesb0c191e2019-03-26 15:49:57 -07007
8#include <atomic>
9#include <condition_variable> // NOLINT
Yuri Wiitalab929b832019-06-05 17:13:15 -070010#include <map>
Jordan Baylesb0c191e2019-03-26 15:49:57 -070011#include <memory>
Yuri Wiitalab929b832019-06-05 17:13:15 -070012#include <mutex> // NOLINT
Jordan Baylesb0c191e2019-03-26 15:49:57 -070013#include <thread> // NOLINT
14#include <utility>
15#include <vector>
16
17#include "absl/base/thread_annotations.h"
18#include "absl/types/optional.h"
btolsch4051e722019-06-07 16:15:17 -070019#include "platform/api/network_runner.h"
Jordan Baylesb0c191e2019-03-26 15:49:57 -070020#include "platform/api/time.h"
Jordan Baylesa26582d2019-07-10 14:44:58 -070021#include "platform/base/error.h"
Jordan Baylesb0c191e2019-03-26 15:49:57 -070022
23namespace openscreen {
24namespace platform {
25
btolsch4051e722019-06-07 16:15:17 -070026class TaskRunnerImpl final : public TaskRunner {
Jordan Baylesb0c191e2019-03-26 15:49:57 -070027 public:
btolsch4051e722019-06-07 16:15:17 -070028 using Task = TaskRunner::Task;
29
btolschd94fe622019-05-09 14:21:40 -070030 class TaskWaiter {
31 public:
32 virtual ~TaskWaiter() = default;
33
34 // These calls should be thread-safe. The absolute minimum is that
35 // OnTaskPosted must be safe to call from another thread while this is
36 // inside WaitForTaskToBePosted. NOTE: There may be spurious wakeups from
37 // WaitForTaskToBePosted depending on whether the specific implementation
38 // chooses to clear queued WakeUps before entering WaitForTaskToBePosted.
39
40 // Blocks until some event occurs, which means new tasks may have been
41 // posted. Wait may only block up to |timeout| where 0 means don't block at
42 // all (not block forever).
43 virtual Error WaitForTaskToBePosted(Clock::duration timeout) = 0;
44
45 // If a WaitForTaskToBePosted call is currently blocking, unblock it
46 // immediately.
47 virtual void OnTaskPosted() = 0;
48 };
49
50 explicit TaskRunnerImpl(
51 platform::ClockNowFunctionPtr now_function,
52 TaskWaiter* event_waiter = nullptr,
53 Clock::duration waiter_timeout = std::chrono::milliseconds(100));
Jordan Baylesb0c191e2019-03-26 15:49:57 -070054
55 // TaskRunner overrides
Yuri Wiitalab929b832019-06-05 17:13:15 -070056 ~TaskRunnerImpl() final;
57 void PostPackagedTask(Task task) final;
58 void PostPackagedTaskWithDelay(Task task, Clock::duration delay) final;
Jordan Baylesb0c191e2019-03-26 15:49:57 -070059
Jordan Baylesa8e96772019-04-08 10:53:54 -070060 // Tasks will only be executed if RunUntilStopped has been called, and
btolsch4051e722019-06-07 16:15:17 -070061 // RequestStopSoon has not. Important note: TaskRunner does NOT do any
Jordan Baylesa8e96772019-04-08 10:53:54 -070062 // threading, so calling "RunUntilStopped()" will block whatever thread you
63 // are calling it on.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070064 void RunUntilStopped();
Jordan Baylesa8e96772019-04-08 10:53:54 -070065
btolsch4051e722019-06-07 16:15:17 -070066 // Thread-safe method for requesting the TaskRunner to stop running. This sets
67 // a flag that will get checked in the run loop, typically after completing
68 // the current task.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070069 void RequestStopSoon();
70
71 // Execute all tasks immediately, useful for testing only. Note: this method
72 // will schedule any delayed tasks that are ready to run, but does not block
73 // waiting for delayed tasks to become eligible.
74 void RunUntilIdleForTesting();
75
76 private:
Jordan Baylesa8e96772019-04-08 10:53:54 -070077 // Run all tasks already in the task queue. If the queue is empty, wait for
78 // either (1) a delayed task to become available, or (2) a task to be added
79 // to the queue.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070080 void RunCurrentTasksBlocking();
Jordan Baylesa8e96772019-04-08 10:53:54 -070081
82 // Run tasks already in the queue, for testing. If the queue is empty, this
83 // method does not block but instead returns immediately.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070084 void RunCurrentTasksForTesting();
85
Jordan Baylesa8e96772019-04-08 10:53:54 -070086 // Loop method that runs tasks in the current thread, until the
87 // RequestStopSoon method is called.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070088 void RunTasksUntilStopped();
89
90 // Look at all tasks in the delayed task queue, then schedule them if the
91 // minimum delay time has elapsed.
92 void ScheduleDelayedTasks();
93
Jordan Bayles5d72bc22019-04-09 13:33:52 -070094 // Look at the current state of the TaskRunner and determine if the run loop
95 // should be woken up
96 bool ShouldWakeUpRunLoop();
97
Jordan Baylesa8e96772019-04-08 10:53:54 -070098 // Takes the task_mutex_ lock, returning immediately if work is available. If
99 // no work is available, this places the task running thread into a waiting
100 // state until we stop running or work is available.
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700101 std::unique_lock<std::mutex> WaitForWorkAndAcquireLock();
102
103 const platform::ClockNowFunctionPtr now_function_;
104
105 // Atomic so that we can perform atomic exchanges.
106 std::atomic_bool is_running_;
107
Yuri Wiitalab929b832019-06-05 17:13:15 -0700108 // This mutex is used for |tasks_| and |delayed_tasks_|, and also for
109 // notifying the run loop to wake up when it is waiting for a task to be added
110 // to the queue in |run_loop_wakeup_|.
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700111 std::mutex task_mutex_;
Yuri Wiitalab929b832019-06-05 17:13:15 -0700112 std::vector<Task> tasks_ GUARDED_BY(task_mutex_);
113 std::multimap<Clock::time_point, Task> delayed_tasks_ GUARDED_BY(task_mutex_);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700114
btolschd94fe622019-05-09 14:21:40 -0700115 // When |task_waiter_| is nullptr, |run_loop_wakeup_| is used for sleeping the
116 // task runner. Otherwise, |run_loop_wakeup_| isn't used and |task_waiter_|
117 // is used instead (along with |waiter_timeout_|).
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700118 std::condition_variable run_loop_wakeup_;
btolschd94fe622019-05-09 14:21:40 -0700119 TaskWaiter* const task_waiter_;
120 Clock::duration waiter_timeout_;
Yuri Wiitalab929b832019-06-05 17:13:15 -0700121
122 // To prevent excessive re-allocation of the underlying array of the |tasks_|
123 // vector, use an A/B vector-swap mechanism. |running_tasks_| starts out
124 // empty, and is swapped with |tasks_| when it is time to run the Tasks.
125 std::vector<Task> running_tasks_;
Ryan Keane32c88d02019-07-02 18:46:14 -0700126
127 OSP_DISALLOW_COPY_AND_ASSIGN(TaskRunnerImpl);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700128};
129} // namespace platform
130} // namespace openscreen
131
Jordan Baylesa26582d2019-07-10 14:44:58 -0700132#endif // PLATFORM_IMPL_TASK_RUNNER_H_