1 // Copyright 2018 The Fuchsia 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 5 // Can't compile this for Zircon userspace yet since libstdc++ isn't available. 6 #ifndef FIT_NO_STD_FOR_ZIRCON_USERSPACE 7 8 #include <lib/fit/scheduler.h> 9 10 #include <map> 11 #include <queue> 12 #include <utility> 13 14 namespace fit { 15 namespace subtle { 16 17 scheduler::scheduler() = default; 18 19 scheduler::~scheduler() = default; 20 schedule_task(pending_task task)21void scheduler::schedule_task(pending_task task) { 22 assert(task); 23 runnable_tasks_.push(std::move(task)); 24 } 25 obtain_ticket(uint32_t initial_refs)26suspended_task::ticket scheduler::obtain_ticket(uint32_t initial_refs) { 27 suspended_task::ticket ticket = next_ticket_++; 28 tickets_.emplace(ticket, ticket_record(initial_refs)); 29 return ticket; 30 } 31 finalize_ticket(suspended_task::ticket ticket,pending_task * task)32void scheduler::finalize_ticket(suspended_task::ticket ticket, 33 pending_task* task) { 34 auto it = tickets_.find(ticket); 35 assert(it != tickets_.end()); 36 assert(!it->second.task); 37 assert(it->second.ref_count > 0); 38 assert(task); 39 40 it->second.ref_count--; 41 if (!*task) { 42 // task already finished 43 } else if (it->second.was_resumed) { 44 // task immediately became runnable 45 runnable_tasks_.push(std::move(*task)); 46 } else if (it->second.ref_count > 0) { 47 // task remains suspended 48 it->second.task = std::move(*task); 49 suspended_task_count_++; 50 } // else, task was abandoned and caller retains ownership of it 51 if (it->second.ref_count == 0) { 52 tickets_.erase(it); 53 } 54 } 55 duplicate_ticket(suspended_task::ticket ticket)56void scheduler::duplicate_ticket(suspended_task::ticket ticket) { 57 auto it = tickets_.find(ticket); 58 assert(it != tickets_.end()); 59 assert(it->second.ref_count > 0); 60 61 it->second.ref_count++; 62 assert(it->second.ref_count != 0); // did we really make 4 billion refs?! 63 } 64 release_ticket(suspended_task::ticket ticket)65pending_task scheduler::release_ticket(suspended_task::ticket ticket) { 66 auto it = tickets_.find(ticket); 67 assert(it != tickets_.end()); 68 assert(it->second.ref_count > 0); 69 70 it->second.ref_count--; 71 if (it->second.ref_count == 0) { 72 pending_task task = std::move(it->second.task); 73 if (task) { 74 assert(suspended_task_count_ > 0); 75 suspended_task_count_--; 76 } 77 tickets_.erase(it); 78 return task; 79 } 80 return pending_task(); 81 } 82 resume_task_with_ticket(suspended_task::ticket ticket)83bool scheduler::resume_task_with_ticket(suspended_task::ticket ticket) { 84 auto it = tickets_.find(ticket); 85 assert(it != tickets_.end()); 86 assert(it->second.ref_count > 0); 87 88 bool did_resume = false; 89 it->second.ref_count--; 90 if (!it->second.was_resumed) { 91 it->second.was_resumed = true; 92 if (it->second.task) { 93 did_resume = true; 94 assert(suspended_task_count_ > 0); 95 suspended_task_count_--; 96 runnable_tasks_.push(std::move(it->second.task)); 97 } 98 } 99 if (it->second.ref_count == 0) { 100 tickets_.erase(it); 101 } 102 return did_resume; 103 } 104 take_runnable_tasks(task_queue * tasks)105void scheduler::take_runnable_tasks(task_queue* tasks) { 106 assert(tasks && tasks->empty()); 107 runnable_tasks_.swap(*tasks); 108 } 109 take_all_tasks(task_queue * tasks)110void scheduler::take_all_tasks(task_queue* tasks) { 111 assert(tasks && tasks->empty()); 112 113 runnable_tasks_.swap(*tasks); 114 if (suspended_task_count_ > 0) { 115 for (auto& item : tickets_) { 116 if (item.second.task) { 117 assert(suspended_task_count_ > 0); 118 suspended_task_count_--; 119 tasks->push(std::move(item.second.task)); 120 } 121 } 122 } 123 } 124 125 } // namespace subtle 126 } // namespace fit 127 128 #endif // FIT_NO_STD_FOR_ZIRCON_USERSPACE 129