blob: b79d2fe9dc508dcff29fca2c5e95c931bacad0b3 [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
btolschc92ba2f2019-04-10 11:46:01 -07005#ifndef PLATFORM_BASE_TASK_RUNNER_IMPL_H_
6#define PLATFORM_BASE_TASK_RUNNER_IMPL_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"
btolschd94fe622019-05-09 14:21:40 -070019#include "osp_base/error.h"
btolschc92ba2f2019-04-10 11:46:01 -070020#include "platform/api/task_runner.h"
Jordan Baylesb0c191e2019-03-26 15:49:57 -070021#include "platform/api/time.h"
22
23namespace openscreen {
24namespace platform {
25
26class TaskRunnerImpl : public TaskRunner {
27 public:
btolschd94fe622019-05-09 14:21:40 -070028 class TaskWaiter {
29 public:
30 virtual ~TaskWaiter() = default;
31
32 // These calls should be thread-safe. The absolute minimum is that
33 // OnTaskPosted must be safe to call from another thread while this is
34 // inside WaitForTaskToBePosted. NOTE: There may be spurious wakeups from
35 // WaitForTaskToBePosted depending on whether the specific implementation
36 // chooses to clear queued WakeUps before entering WaitForTaskToBePosted.
37
38 // Blocks until some event occurs, which means new tasks may have been
39 // posted. Wait may only block up to |timeout| where 0 means don't block at
40 // all (not block forever).
41 virtual Error WaitForTaskToBePosted(Clock::duration timeout) = 0;
42
43 // If a WaitForTaskToBePosted call is currently blocking, unblock it
44 // immediately.
45 virtual void OnTaskPosted() = 0;
46 };
47
48 explicit TaskRunnerImpl(
49 platform::ClockNowFunctionPtr now_function,
50 TaskWaiter* event_waiter = nullptr,
51 Clock::duration waiter_timeout = std::chrono::milliseconds(100));
Jordan Baylesb0c191e2019-03-26 15:49:57 -070052
53 // TaskRunner overrides
Yuri Wiitalab929b832019-06-05 17:13:15 -070054 ~TaskRunnerImpl() final;
55 void PostPackagedTask(Task task) final;
56 void PostPackagedTaskWithDelay(Task task, Clock::duration delay) final;
Jordan Baylesb0c191e2019-03-26 15:49:57 -070057
Jordan Baylesa8e96772019-04-08 10:53:54 -070058 // Tasks will only be executed if RunUntilStopped has been called, and
59 // RequestStopSoon has not. Important note: TaskRunnerImpl does NOT do any
60 // threading, so calling "RunUntilStopped()" will block whatever thread you
61 // are calling it on.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070062 void RunUntilStopped();
Jordan Baylesa8e96772019-04-08 10:53:54 -070063
64 // Thread-safe method for requesting the TaskRunnerImpl to stop running. This
65 // sets a flag that will get checked in the run loop, typically after
66 // completing the current task.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070067 void RequestStopSoon();
68
69 // Execute all tasks immediately, useful for testing only. Note: this method
70 // will schedule any delayed tasks that are ready to run, but does not block
71 // waiting for delayed tasks to become eligible.
72 void RunUntilIdleForTesting();
73
74 private:
Jordan Baylesa8e96772019-04-08 10:53:54 -070075 // Run all tasks already in the task queue. If the queue is empty, wait for
76 // either (1) a delayed task to become available, or (2) a task to be added
77 // to the queue.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070078 void RunCurrentTasksBlocking();
Jordan Baylesa8e96772019-04-08 10:53:54 -070079
80 // Run tasks already in the queue, for testing. If the queue is empty, this
81 // method does not block but instead returns immediately.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070082 void RunCurrentTasksForTesting();
83
Jordan Baylesa8e96772019-04-08 10:53:54 -070084 // Loop method that runs tasks in the current thread, until the
85 // RequestStopSoon method is called.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070086 void RunTasksUntilStopped();
87
88 // Look at all tasks in the delayed task queue, then schedule them if the
89 // minimum delay time has elapsed.
90 void ScheduleDelayedTasks();
91
Jordan Bayles5d72bc22019-04-09 13:33:52 -070092 // Look at the current state of the TaskRunner and determine if the run loop
93 // should be woken up
94 bool ShouldWakeUpRunLoop();
95
Jordan Baylesa8e96772019-04-08 10:53:54 -070096 // Takes the task_mutex_ lock, returning immediately if work is available. If
97 // no work is available, this places the task running thread into a waiting
98 // state until we stop running or work is available.
Jordan Baylesb0c191e2019-03-26 15:49:57 -070099 std::unique_lock<std::mutex> WaitForWorkAndAcquireLock();
100
101 const platform::ClockNowFunctionPtr now_function_;
102
103 // Atomic so that we can perform atomic exchanges.
104 std::atomic_bool is_running_;
105
Yuri Wiitalab929b832019-06-05 17:13:15 -0700106 // This mutex is used for |tasks_| and |delayed_tasks_|, and also for
107 // notifying the run loop to wake up when it is waiting for a task to be added
108 // to the queue in |run_loop_wakeup_|.
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700109 std::mutex task_mutex_;
Yuri Wiitalab929b832019-06-05 17:13:15 -0700110 std::vector<Task> tasks_ GUARDED_BY(task_mutex_);
111 std::multimap<Clock::time_point, Task> delayed_tasks_ GUARDED_BY(task_mutex_);
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700112
btolschd94fe622019-05-09 14:21:40 -0700113 // When |task_waiter_| is nullptr, |run_loop_wakeup_| is used for sleeping the
114 // task runner. Otherwise, |run_loop_wakeup_| isn't used and |task_waiter_|
115 // is used instead (along with |waiter_timeout_|).
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700116 std::condition_variable run_loop_wakeup_;
btolschd94fe622019-05-09 14:21:40 -0700117 TaskWaiter* const task_waiter_;
118 Clock::duration waiter_timeout_;
Yuri Wiitalab929b832019-06-05 17:13:15 -0700119
120 // To prevent excessive re-allocation of the underlying array of the |tasks_|
121 // vector, use an A/B vector-swap mechanism. |running_tasks_| starts out
122 // empty, and is swapped with |tasks_| when it is time to run the Tasks.
123 std::vector<Task> running_tasks_;
Jordan Baylesb0c191e2019-03-26 15:49:57 -0700124};
125} // namespace platform
126} // namespace openscreen
127
btolschc92ba2f2019-04-10 11:46:01 -0700128#endif // PLATFORM_BASE_TASK_RUNNER_IMPL_H_