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)21 void scheduler::schedule_task(pending_task task) {
22     assert(task);
23     runnable_tasks_.push(std::move(task));
24 }
25 
obtain_ticket(uint32_t initial_refs)26 suspended_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)32 void 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)56 void 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)65 pending_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)83 bool 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)105 void 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)110 void 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