blob: 5dee6e613d2e249e6a82388b3e181570507623bc [file] [log] [blame]
tommic06b1332016-05-14 11:31:40 -07001/*
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Henrik Kjellanderc0362762017-06-29 08:03:04 +020011#ifndef WEBRTC_RTC_BASE_TASK_QUEUE_H_
12#define WEBRTC_RTC_BASE_TASK_QUEUE_H_
tommic06b1332016-05-14 11:31:40 -070013
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020014#include <list>
15#include <memory>
16#include <queue>
eladalonffe2e142017-08-31 04:36:05 -070017#include <type_traits>
tommic06b1332016-05-14 11:31:40 -070018
perkj650fdae2017-08-25 05:00:11 -070019#if defined(WEBRTC_MAC)
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020020#include <dispatch/dispatch.h>
21#endif
22
kjellandere96c45b2017-06-30 10:45:21 -070023#include "webrtc/rtc_base/constructormagic.h"
24#include "webrtc/rtc_base/criticalsection.h"
kjellandere96c45b2017-06-30 10:45:21 -070025#include "webrtc/rtc_base/scoped_ref_ptr.h"
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020026
perkj650fdae2017-08-25 05:00:11 -070027#if defined(WEBRTC_WIN)
28#include "webrtc/rtc_base/platform_thread.h"
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +020029#endif
30
31namespace rtc {
32
33// Base interface for asynchronously executed tasks.
34// The interface basically consists of a single function, Run(), that executes
35// on the target queue. For more details see the Run() method and TaskQueue.
36class QueuedTask {
37 public:
38 QueuedTask() {}
39 virtual ~QueuedTask() {}
40
41 // Main routine that will run when the task is executed on the desired queue.
42 // The task should return |true| to indicate that it should be deleted or
43 // |false| to indicate that the queue should consider ownership of the task
44 // having been transferred. Returning |false| can be useful if a task has
45 // re-posted itself to a different queue or is otherwise being re-used.
46 virtual bool Run() = 0;
47
48 private:
49 RTC_DISALLOW_COPY_AND_ASSIGN(QueuedTask);
50};
51
52// Simple implementation of QueuedTask for use with rtc::Bind and lambdas.
53template <class Closure>
54class ClosureTask : public QueuedTask {
55 public:
56 explicit ClosureTask(const Closure& closure) : closure_(closure) {}
57
58 private:
59 bool Run() override {
60 closure_();
61 return true;
62 }
63
64 Closure closure_;
65};
66
67// Extends ClosureTask to also allow specifying cleanup code.
68// This is useful when using lambdas if guaranteeing cleanup, even if a task
69// was dropped (queue is too full), is required.
70template <class Closure, class Cleanup>
71class ClosureTaskWithCleanup : public ClosureTask<Closure> {
72 public:
73 ClosureTaskWithCleanup(const Closure& closure, Cleanup cleanup)
74 : ClosureTask<Closure>(closure), cleanup_(cleanup) {}
75 ~ClosureTaskWithCleanup() { cleanup_(); }
76
77 private:
78 Cleanup cleanup_;
79};
80
81// Convenience function to construct closures that can be passed directly
82// to methods that support std::unique_ptr<QueuedTask> but not template
83// based parameters.
84template <class Closure>
85static std::unique_ptr<QueuedTask> NewClosure(const Closure& closure) {
86 return std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(closure));
87}
88
89template <class Closure, class Cleanup>
90static std::unique_ptr<QueuedTask> NewClosure(const Closure& closure,
91 const Cleanup& cleanup) {
92 return std::unique_ptr<QueuedTask>(
93 new ClosureTaskWithCleanup<Closure, Cleanup>(closure, cleanup));
94}
95
96// Implements a task queue that asynchronously executes tasks in a way that
97// guarantees that they're executed in FIFO order and that tasks never overlap.
98// Tasks may always execute on the same worker thread and they may not.
99// To DCHECK that tasks are executing on a known task queue, use IsCurrent().
100//
101// Here are some usage examples:
102//
103// 1) Asynchronously running a lambda:
104//
105// class MyClass {
106// ...
107// TaskQueue queue_("MyQueue");
108// };
109//
110// void MyClass::StartWork() {
111// queue_.PostTask([]() { Work(); });
112// ...
113//
114// 2) Doing work asynchronously on a worker queue and providing a notification
115// callback on the current queue, when the work has been done:
116//
117// void MyClass::StartWorkAndLetMeKnowWhenDone(
118// std::unique_ptr<QueuedTask> callback) {
119// DCHECK(TaskQueue::Current()) << "Need to be running on a queue";
120// queue_.PostTaskAndReply([]() { Work(); }, std::move(callback));
121// }
122// ...
123// my_class->StartWorkAndLetMeKnowWhenDone(
124// NewClosure([]() { LOG(INFO) << "The work is done!";}));
125//
126// 3) Posting a custom task on a timer. The task posts itself again after
127// every running:
128//
129// class TimerTask : public QueuedTask {
130// public:
131// TimerTask() {}
132// private:
133// bool Run() override {
134// ++count_;
135// TaskQueue::Current()->PostDelayedTask(
136// std::unique_ptr<QueuedTask>(this), 1000);
137// // Ownership has been transferred to the next occurance,
138// // so return false to prevent from being deleted now.
139// return false;
140// }
141// int count_ = 0;
142// };
143// ...
144// queue_.PostDelayedTask(
145// std::unique_ptr<QueuedTask>(new TimerTask()), 1000);
146//
147// For more examples, see task_queue_unittests.cc.
148//
149// A note on destruction:
150//
151// When a TaskQueue is deleted, pending tasks will not be executed but they will
152// be deleted. The deletion of tasks may happen asynchronously after the
153// TaskQueue itself has been deleted or it may happen synchronously while the
154// TaskQueue instance is being deleted. This may vary from one OS to the next
155// so assumptions about lifetimes of pending tasks should not be made.
156class LOCKABLE TaskQueue {
157 public:
158 // TaskQueue priority levels. On some platforms these will map to thread
159 // priorities, on others such as Mac and iOS, GCD queue priorities.
160 enum class Priority {
161 NORMAL = 0,
162 HIGH,
163 LOW,
164 };
165
166 explicit TaskQueue(const char* queue_name,
167 Priority priority = Priority::NORMAL);
168 ~TaskQueue();
169
170 static TaskQueue* Current();
171
172 // Used for DCHECKing the current queue.
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200173 bool IsCurrent() const;
174
175 // TODO(tommi): For better debuggability, implement RTC_FROM_HERE.
176
177 // Ownership of the task is passed to PostTask.
178 void PostTask(std::unique_ptr<QueuedTask> task);
179 void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
180 std::unique_ptr<QueuedTask> reply,
181 TaskQueue* reply_queue);
182 void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
183 std::unique_ptr<QueuedTask> reply);
184
185 // Schedules a task to execute a specified number of milliseconds from when
186 // the call is made. The precision should be considered as "best effort"
187 // and in some cases, such as on Windows when all high precision timers have
188 // been used up, can be off by as much as 15 millseconds (although 8 would be
189 // more likely). This can be mitigated by limiting the use of delayed tasks.
190 void PostDelayedTask(std::unique_ptr<QueuedTask> task, uint32_t milliseconds);
191
eladalonffe2e142017-08-31 04:36:05 -0700192 // std::enable_if is used here to make sure that calls to PostTask() with
193 // std::unique_ptr<SomeClassDerivedFromQueuedTask> would not end up being
194 // caught by this template.
195 template <class Closure,
196 typename std::enable_if<
197 std::is_copy_constructible<Closure>::value>::type* = nullptr>
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200198 void PostTask(const Closure& closure) {
199 PostTask(std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(closure)));
200 }
201
202 // See documentation above for performance expectations.
203 template <class Closure>
204 void PostDelayedTask(const Closure& closure, uint32_t milliseconds) {
205 PostDelayedTask(
206 std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(closure)),
207 milliseconds);
208 }
209
210 template <class Closure1, class Closure2>
211 void PostTaskAndReply(const Closure1& task,
212 const Closure2& reply,
213 TaskQueue* reply_queue) {
214 PostTaskAndReply(
215 std::unique_ptr<QueuedTask>(new ClosureTask<Closure1>(task)),
216 std::unique_ptr<QueuedTask>(new ClosureTask<Closure2>(reply)),
217 reply_queue);
218 }
219
220 template <class Closure>
221 void PostTaskAndReply(std::unique_ptr<QueuedTask> task,
222 const Closure& reply) {
223 PostTaskAndReply(std::move(task), std::unique_ptr<QueuedTask>(
224 new ClosureTask<Closure>(reply)));
225 }
226
227 template <class Closure>
228 void PostTaskAndReply(const Closure& task,
229 std::unique_ptr<QueuedTask> reply) {
230 PostTaskAndReply(
231 std::unique_ptr<QueuedTask>(new ClosureTask<Closure>(task)),
232 std::move(reply));
233 }
234
235 template <class Closure1, class Closure2>
236 void PostTaskAndReply(const Closure1& task, const Closure2& reply) {
237 PostTaskAndReply(
238 std::unique_ptr<QueuedTask>(new ClosureTask<Closure1>(task)),
239 std::unique_ptr<QueuedTask>(new ClosureTask<Closure2>(reply)));
240 }
241
242 private:
perkj650fdae2017-08-25 05:00:11 -0700243#if defined(WEBRTC_MAC)
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200244 struct QueueContext;
245 struct TaskContext;
246 struct PostTaskAndReplyContext;
247 dispatch_queue_t queue_;
248 QueueContext* const context_;
249#elif defined(WEBRTC_WIN)
250 class ThreadState;
251 void RunPendingTasks();
252 static void ThreadMain(void* context);
253
254 class WorkerThread : public PlatformThread {
255 public:
256 WorkerThread(ThreadRunFunction func,
257 void* obj,
258 const char* thread_name,
259 ThreadPriority priority)
260 : PlatformThread(func, obj, thread_name, priority) {}
261
262 bool QueueAPC(PAPCFUNC apc_function, ULONG_PTR data) {
263 return PlatformThread::QueueAPC(apc_function, data);
264 }
265 };
266 WorkerThread thread_;
267 rtc::CriticalSection pending_lock_;
268 std::queue<std::unique_ptr<QueuedTask>> pending_ GUARDED_BY(pending_lock_);
269 HANDLE in_queue_;
270#else
perkj650fdae2017-08-25 05:00:11 -0700271 class Impl;
272 const scoped_refptr<Impl> impl_;
Henrik Kjellanderec78f1c2017-06-29 07:52:50 +0200273#endif
274
275 RTC_DISALLOW_COPY_AND_ASSIGN(TaskQueue);
276};
277
278} // namespace rtc
tommic06b1332016-05-14 11:31:40 -0700279
Henrik Kjellanderc0362762017-06-29 08:03:04 +0200280#endif // WEBRTC_RTC_BASE_TASK_QUEUE_H_