| OLD | NEW | 
 | (Empty) | 
|    1 /* |  | 
|    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  |  | 
|   11 // This file contains the implementation of TaskQueue for Mac and iOS. |  | 
|   12 // The implementation uses Grand Central Dispatch queues (GCD) to |  | 
|   13 // do the actual task queuing. |  | 
|   14  |  | 
|   15 #include "webrtc/base/task_queue.h" |  | 
|   16  |  | 
|   17 #include <string.h> |  | 
|   18  |  | 
|   19 #include "webrtc/base/checks.h" |  | 
|   20 #include "webrtc/base/logging.h" |  | 
|   21 #include "webrtc/base/task_queue_posix.h" |  | 
|   22  |  | 
|   23 namespace rtc { |  | 
|   24 namespace { |  | 
|   25  |  | 
|   26 using Priority = TaskQueue::Priority; |  | 
|   27  |  | 
|   28 int TaskQueuePriorityToGCD(Priority priority) { |  | 
|   29   switch (priority) { |  | 
|   30     case Priority::NORMAL: |  | 
|   31       return DISPATCH_QUEUE_PRIORITY_DEFAULT; |  | 
|   32     case Priority::HIGH: |  | 
|   33       return DISPATCH_QUEUE_PRIORITY_HIGH; |  | 
|   34     case Priority::LOW: |  | 
|   35       return DISPATCH_QUEUE_PRIORITY_LOW; |  | 
|   36   } |  | 
|   37 } |  | 
|   38 } |  | 
|   39  |  | 
|   40 using internal::GetQueuePtrTls; |  | 
|   41 using internal::AutoSetCurrentQueuePtr; |  | 
|   42  |  | 
|   43 struct TaskQueue::QueueContext { |  | 
|   44   explicit QueueContext(TaskQueue* q) : queue(q), is_active(true) {} |  | 
|   45  |  | 
|   46   static void SetNotActive(void* context) { |  | 
|   47     QueueContext* qc = static_cast<QueueContext*>(context); |  | 
|   48     qc->is_active = false; |  | 
|   49   } |  | 
|   50  |  | 
|   51   static void DeleteContext(void* context) { |  | 
|   52     QueueContext* qc = static_cast<QueueContext*>(context); |  | 
|   53     delete qc; |  | 
|   54   } |  | 
|   55  |  | 
|   56   TaskQueue* const queue; |  | 
|   57   bool is_active; |  | 
|   58 }; |  | 
|   59  |  | 
|   60 struct TaskQueue::TaskContext { |  | 
|   61   TaskContext(QueueContext* queue_ctx, std::unique_ptr<QueuedTask> task) |  | 
|   62       : queue_ctx(queue_ctx), task(std::move(task)) {} |  | 
|   63   virtual ~TaskContext() {} |  | 
|   64  |  | 
|   65   static void RunTask(void* context) { |  | 
|   66     std::unique_ptr<TaskContext> tc(static_cast<TaskContext*>(context)); |  | 
|   67     if (tc->queue_ctx->is_active) { |  | 
|   68       AutoSetCurrentQueuePtr set_current(tc->queue_ctx->queue); |  | 
|   69       if (!tc->task->Run()) |  | 
|   70         tc->task.release(); |  | 
|   71     } |  | 
|   72   } |  | 
|   73  |  | 
|   74   QueueContext* const queue_ctx; |  | 
|   75   std::unique_ptr<QueuedTask> task; |  | 
|   76 }; |  | 
|   77  |  | 
|   78 // Special case context for holding two tasks, a |first_task| + the task |  | 
|   79 // that's owned by the parent struct, TaskContext, that then becomes the |  | 
|   80 // second (i.e. 'reply') task. |  | 
|   81 struct TaskQueue::PostTaskAndReplyContext : public TaskQueue::TaskContext { |  | 
|   82   explicit PostTaskAndReplyContext(QueueContext* first_queue_ctx, |  | 
|   83                                    std::unique_ptr<QueuedTask> first_task, |  | 
|   84                                    QueueContext* second_queue_ctx, |  | 
|   85                                    std::unique_ptr<QueuedTask> second_task) |  | 
|   86       : TaskContext(second_queue_ctx, std::move(second_task)), |  | 
|   87         first_queue_ctx(first_queue_ctx), |  | 
|   88         first_task(std::move(first_task)), |  | 
|   89         reply_queue_(second_queue_ctx->queue->queue_) { |  | 
|   90     // Retain the reply queue for as long as this object lives. |  | 
|   91     // If we don't, we may have memory leaks and/or failures. |  | 
|   92     dispatch_retain(reply_queue_); |  | 
|   93   } |  | 
|   94   ~PostTaskAndReplyContext() override { dispatch_release(reply_queue_); } |  | 
|   95  |  | 
|   96   static void RunTask(void* context) { |  | 
|   97     auto* rc = static_cast<PostTaskAndReplyContext*>(context); |  | 
|   98     if (rc->first_queue_ctx->is_active) { |  | 
|   99       AutoSetCurrentQueuePtr set_current(rc->first_queue_ctx->queue); |  | 
|  100       if (!rc->first_task->Run()) |  | 
|  101         rc->first_task.release(); |  | 
|  102     } |  | 
|  103     // Post the reply task.  This hands the work over to the parent struct. |  | 
|  104     // This task will eventually delete |this|. |  | 
|  105     dispatch_async_f(rc->reply_queue_, rc, &TaskContext::RunTask); |  | 
|  106   } |  | 
|  107  |  | 
|  108   QueueContext* const first_queue_ctx; |  | 
|  109   std::unique_ptr<QueuedTask> first_task; |  | 
|  110   dispatch_queue_t reply_queue_; |  | 
|  111 }; |  | 
|  112  |  | 
|  113 TaskQueue::TaskQueue(const char* queue_name, Priority priority /*= NORMAL*/) |  | 
|  114     : queue_(dispatch_queue_create(queue_name, DISPATCH_QUEUE_SERIAL)), |  | 
|  115       context_(new QueueContext(this)) { |  | 
|  116   RTC_DCHECK(queue_name); |  | 
|  117   RTC_CHECK(queue_); |  | 
|  118   dispatch_set_context(queue_, context_); |  | 
|  119   // Assign a finalizer that will delete the context when the last reference |  | 
|  120   // to the queue is released.  This may run after the TaskQueue object has |  | 
|  121   // been deleted. |  | 
|  122   dispatch_set_finalizer_f(queue_, &QueueContext::DeleteContext); |  | 
|  123  |  | 
|  124   dispatch_set_target_queue( |  | 
|  125       queue_, dispatch_get_global_queue(TaskQueuePriorityToGCD(priority), 0)); |  | 
|  126 } |  | 
|  127  |  | 
|  128 TaskQueue::~TaskQueue() { |  | 
|  129   RTC_DCHECK(!IsCurrent()); |  | 
|  130   // Implementation/behavioral note: |  | 
|  131   // Dispatch queues are reference counted via calls to dispatch_retain and |  | 
|  132   // dispatch_release. Pending blocks submitted to a queue also hold a |  | 
|  133   // reference to the queue until they have finished. Once all references to a |  | 
|  134   // queue have been released, the queue will be deallocated by the system. |  | 
|  135   // This is why we check the context before running tasks. |  | 
|  136  |  | 
|  137   // Use dispatch_sync to set the context to null to guarantee that there's not |  | 
|  138   // a race between checking the context and using it from a task. |  | 
|  139   dispatch_sync_f(queue_, context_, &QueueContext::SetNotActive); |  | 
|  140   dispatch_release(queue_); |  | 
|  141 } |  | 
|  142  |  | 
|  143 // static |  | 
|  144 TaskQueue* TaskQueue::Current() { |  | 
|  145   return static_cast<TaskQueue*>(pthread_getspecific(GetQueuePtrTls())); |  | 
|  146 } |  | 
|  147  |  | 
|  148 // static |  | 
|  149 bool TaskQueue::IsCurrent(const char* queue_name) { |  | 
|  150   TaskQueue* current = Current(); |  | 
|  151   return current && |  | 
|  152          strcmp(queue_name, dispatch_queue_get_label(current->queue_)) == 0; |  | 
|  153 } |  | 
|  154  |  | 
|  155 bool TaskQueue::IsCurrent() const { |  | 
|  156   RTC_DCHECK(queue_); |  | 
|  157   return this == Current(); |  | 
|  158 } |  | 
|  159  |  | 
|  160 void TaskQueue::PostTask(std::unique_ptr<QueuedTask> task) { |  | 
|  161   auto* context = new TaskContext(context_, std::move(task)); |  | 
|  162   dispatch_async_f(queue_, context, &TaskContext::RunTask); |  | 
|  163 } |  | 
|  164  |  | 
|  165 void TaskQueue::PostDelayedTask(std::unique_ptr<QueuedTask> task, |  | 
|  166                                 uint32_t milliseconds) { |  | 
|  167   auto* context = new TaskContext(context_, std::move(task)); |  | 
|  168   dispatch_after_f( |  | 
|  169       dispatch_time(DISPATCH_TIME_NOW, milliseconds * NSEC_PER_MSEC), queue_, |  | 
|  170       context, &TaskContext::RunTask); |  | 
|  171 } |  | 
|  172  |  | 
|  173 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, |  | 
|  174                                  std::unique_ptr<QueuedTask> reply, |  | 
|  175                                  TaskQueue* reply_queue) { |  | 
|  176   auto* context = new PostTaskAndReplyContext( |  | 
|  177       context_, std::move(task), reply_queue->context_, std::move(reply)); |  | 
|  178   dispatch_async_f(queue_, context, &PostTaskAndReplyContext::RunTask); |  | 
|  179 } |  | 
|  180  |  | 
|  181 void TaskQueue::PostTaskAndReply(std::unique_ptr<QueuedTask> task, |  | 
|  182                                  std::unique_ptr<QueuedTask> reply) { |  | 
|  183   return PostTaskAndReply(std::move(task), std::move(reply), Current()); |  | 
|  184 } |  | 
|  185  |  | 
|  186 }  // namespace rtc |  | 
| OLD | NEW |