@@ -2,6 +2,7 @@ export module CppUtils.Thread.Scheduler;
22
33import std;
44import CppUtils.Chrono.Concept;
5+ import CppUtils.Execution.ScopeGuard;
56import CppUtils.Thread.UniqueLocker;
67import CppUtils.Thread.SharedLocker;
78import CppUtils.Thread.ThreadLoop;
@@ -31,17 +32,20 @@ export namespace CppUtils::Thread
3132 inline auto operator()(const std::shared_ptr<Item>& a,
3233 const std::shared_ptr<Item>& b) const noexcept -> bool
3334 {
34- return a->time < b->time;
35+ if (a->time != b->time)
36+ return a->time < b->time;
37+ return a->id < b->id;
3538 }
3639 };
3740
3841 struct Items final
3942 {
40- using Set = std::multiset <std::shared_ptr<Item>, Compare>;
43+ using Set = std::set <std::shared_ptr<Item>, Compare>;
4144 using Map = std::unordered_map<std::size_t, std::shared_ptr<Item>>;
4245
4346 Set set;
4447 Map map;
48+ std::size_t processingTasks = 0;
4549 };
4650
4751 public:
@@ -52,37 +56,43 @@ export namespace CppUtils::Thread
5256 std::function<void()> finally = nullptr):
5357 m_step{step},
5458 m_finally{std::move(finally)},
55- m_threadLoop{
56- [this] { runLoop(); },
57- [this] { m_condition.notify_all(); },
58- onError},
5959 m_threadPool{
6060 numberThreads,
6161 onError,
6262 [this] {
63+ auto _ = Execution::ScopeGuard{[&] {
64+ auto accessor = m_items.access();
65+ if (--accessor.value().processingTasks == 0)
66+ m_finishedCondition.notify_all();
67+ }};
6368 if (m_finally)
6469 m_finally();
65- if (--m_processingTasks == 0)
66- m_condition.notify_all();
67- }}
70+ }},
71+ m_threadLoop{
72+ [this] { runLoop(); },
73+ [this] {
74+ m_workCondition.notify_all();
75+ m_finishedCondition.notify_all();
76+ },
77+ onError}
6878 {
6979 m_threadLoop.start();
7080 }
7181
7282 inline ~Scheduler() noexcept
7383 {
74- cancelAll();
7584 m_threadLoop.requestStop();
85+ cancelAll();
7686 }
7787
7888 inline auto schedule(Task task, TimePoint when) -> std::size_t
7989 {
8090 auto item = std::make_shared<Item>(m_nextId.fetch_add(1, std::memory_order_relaxed), when, std::move(task));
8191 auto accessor = m_items.access();
82- auto& [set, map] = accessor.value();
92+ auto& [set, map, processingTasks ] = accessor.value();
8393 set.insert(item);
8494 map.emplace(item->id, item);
85- m_condition.notify_one ();
95+ m_workCondition.notify_all ();
8696 return item->id;
8797 }
8898
@@ -94,101 +104,103 @@ export namespace CppUtils::Thread
94104 inline auto waitUntilFinished() -> void
95105 {
96106 auto accessor = m_items.access();
97- m_condition .wait(accessor.getLockGuard(), [&] {
98- return std::empty(accessor.value().set) and m_processingTasks == 0;
107+ m_finishedCondition .wait(accessor.getLockGuard(), [&] {
108+ return std::empty(accessor.value().set) and accessor.value().processingTasks == 0;
99109 });
100110 }
101111
102112 inline auto cancel(std::size_t id) -> void
103113 {
104114 auto accessor = m_items.access();
105- auto& [set, map] = accessor.value();
115+ auto& [set, map, processingTasks ] = accessor.value();
106116 if (auto it = map.find(id); it != std::end(map))
107117 {
108118 it->second->cancelled = true;
119+ set.erase(it->second);
109120 map.erase(it);
121+ m_workCondition.notify_all();
122+ m_finishedCondition.notify_all();
110123 }
111124 }
112125
113126 inline auto cancelAll() -> void
114127 {
115128 auto accessor = m_items.access();
116- auto& [set, map] = accessor.value();
129+ auto& [set, map, processingTasks ] = accessor.value();
117130 for (auto& [_, item] : map)
118131 item->cancelled = true;
119132 map.clear();
120133 set.clear();
121- m_condition.notify_all();
134+ m_workCondition.notify_all();
135+ m_finishedCondition.notify_all();
122136 }
123137
124138 private:
125139 inline auto runLoop() -> void
126140 {
127- auto tasksToRun = std::vector<Task >{};
141+ auto tasksToRun = std::vector<std::shared_ptr<Item> >{};
128142 {
129143 auto accessor = m_items.access();
130- auto& [set, map] = accessor.value();
144+ auto& [set, map, processingTasks ] = accessor.value();
131145
132146 if (std::empty(set))
133- {
134- m_condition.wait(accessor.getLockGuard(), [this, &accessor] {
135- return not std::empty(accessor.value().set) or m_threadLoop.isStopRequested();
147+ m_workCondition.wait(accessor.getLockGuard(), [this, &set] {
148+ return not std::empty(set) or m_threadLoop.isStopRequested();
136149 });
137- if (m_threadLoop.isStopRequested())
138- return;
139- if (std::empty(set))
140- {
141- m_condition.notify_all();
142- return;
143- }
144- }
145-
146- auto first = *std::begin(set);
147-
148- if (first->time > Clock::now())
149- {
150- m_condition.wait_until(accessor.getLockGuard(), first->time, [&, first] {
151- return m_threadLoop.isStopRequested() or first->cancelled or std::empty(set) or (*std::begin(set) != first) or first->time <= Clock::now();
150+ else if (auto first = *std::begin(set); first->time > Clock::now())
151+ m_workCondition.wait_until(accessor.getLockGuard(), first->time, [this, &set, first] {
152+ return m_threadLoop.isStopRequested() or std::empty(set) or (*std::begin(set) != first) or ((*std::begin(set))->time <= Clock::now());
152153 });
154+
155+ if (m_threadLoop.isStopRequested() or std::empty(set) or (*std::begin(set))->time > Clock::now())
153156 return;
154- }
155157
158+ auto first = *std::begin(set);
156159 const auto endTime = (m_step == Clock::duration::zero()) ? first->time : first->time + m_step;
157160
158161 for (auto it = std::begin(set); it != std::end(set);)
159- {
160162 if ((*it)->time <= endTime)
161163 {
162- if (not(*it)->cancelled)
163- tasksToRun.push_back(std::move((*it)->task));
164+ tasksToRun.push_back(*it);
164165 map.erase((*it)->id);
165166 it = set.erase(it);
167+ ++accessor.value().processingTasks;
166168 }
167169 else
168- ++it;
169- }
170- }
170+ break;
171171
172- if (std::empty(tasksToRun))
173- return;
172+ if (std::empty(set))
173+ m_finishedCondition.notify_all();
174+ }
174175
175- m_processingTasks += std::size(tasksToRun);
176- for (auto&& task : tasksToRun)
177- m_threadPool.call([task = std::move(task)] {
178- std::invoke(task);
179- });
176+ for (auto&& item : tasksToRun)
177+ {
178+ try
179+ {
180+ m_threadPool.call([item] {
181+ if (not item->cancelled)
182+ std::invoke(item->task);
183+ });
184+ }
185+ catch (...)
186+ {
187+ auto accessor = m_items.access();
188+ if (--accessor.value().processingTasks == 0)
189+ m_finishedCondition.notify_all();
190+ }
191+ }
180192 }
181193
182194 private:
183195 std::atomic_size_t m_nextId = 0;
184196 Clock::duration m_step;
185197 UniqueLocker<Items> m_items;
186- std::atomic_size_t m_processingTasks = 0;
187198
188199 std::function<void()> m_finally;
189200
190- std::condition_variable m_condition ;
191- ThreadLoop m_threadLoop ;
201+ std::condition_variable m_workCondition ;
202+ std::condition_variable m_finishedCondition ;
192203 ThreadPool m_threadPool;
204+ ThreadLoop m_threadLoop;
193205 };
194206}
0 commit comments