source: mainline/uspace/lib/cpp/include/impl/thread.hpp@ 48d9187

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 48d9187 was 48d9187, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: added generic joinable threads

  • Property mode set to 100644
File size: 8.5 KB
Line 
1/*
2 * Copyright (c) 2017 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_THREAD
30#define LIBCPP_THREAD
31
32#include <chrono>
33#include <internal/common.hpp>
34#include <ostream>
35
36namespace std
37{
38 extern "C" {
39 #include <fibril.h>
40 #include <fibril_synch.h>
41 }
42
43 namespace aux
44 {
45 template<class Callable>
46 int thread_main(void*);
47
48 /**
49 * Fibrils in HelenOS are not joinable. They were
50 * in the past, but that functionality was removed from them
51 * so we created a workaround using a conditional variable that
52 * comprises the following two wrapper classes.
53 */
54 class joinable_wrapper
55 {
56 public:
57 joinable_wrapper()
58 : join_mtx_{}, join_cv_{},
59 finished_{false}, detached_{false}
60 {
61 fibril_mutex_initialize(&join_mtx_);
62 fibril_condvar_initialize(&join_cv_);
63 }
64
65 void join()
66 {
67 fibril_mutex_lock(&join_mtx_);
68 while (!finished_)
69 fibril_condvar_wait(&join_cv_, &join_mtx_);
70 fibril_mutex_unlock(&join_mtx_);
71 }
72
73 bool finished() const
74 {
75 return finished_;
76 }
77
78 void detach()
79 {
80 detached_ = true;
81 }
82
83 bool detached() const
84 {
85 return detached_;
86 }
87
88 protected:
89 fibril_mutex_t join_mtx_;
90 fibril_condvar_t join_cv_;
91 bool finished_;
92 bool detached_;
93 };
94
95 template<class Callable>
96 class callable_wrapper: public joinable_wrapper
97 {
98 public:
99 callable_wrapper(Callable&& clbl)
100 : joinable_wrapper{}, callable_{forward<Callable>(clbl)}
101 { /* DUMMY BODY */ }
102
103 void operator()()
104 {
105 callable_();
106
107 fibril_mutex_lock(&join_mtx_);
108 finished_ = true;
109 fibril_mutex_unlock(&join_mtx_);
110
111 fibril_condvar_broadcast(&join_cv_);
112 }
113
114 private:
115 Callable callable_;
116 };
117 }
118
119 /**
120 * 30.3.1, class thread:
121 */
122
123 class thread
124 {
125 public:
126 class id;
127
128 using native_handle_type = fibril_t*;
129
130 /**
131 * 30.3.1.2, thread constructors:
132 * 30.3.1.3, thread destructor:
133 * 30.3.1.4, thread assignment:
134 */
135
136 thread() noexcept;
137
138 ~thread();
139
140 // TODO: check the remark in the standard
141 template<class F, class... Args>
142 explicit thread(F&& f, Args&&... args)
143 : id_{}
144 {
145 auto callable = [&](){
146 return f(forward<Args>(args)...);
147 };
148
149 auto callable_wrapper = new aux::callable_wrapper<decltype(callable)>{move(callable)};
150 joinable_wrapper_ = static_cast<aux::joinable_wrapper*>(callable_wrapper);
151
152 id_ = fibril_create(
153 aux::thread_main<decltype(callable_wrapper)>,
154 static_cast<void*>(callable_wrapper)
155 );
156 fibril_add_ready(id_);
157 }
158
159 thread(const thread&) = delete;
160 thread& operator=(const thread&) = delete;
161
162 thread(thread&& other) noexcept;
163 thread& operator=(thread&& other) noexcept;
164
165 /**
166 * 30.3.1.5, thread members:
167 */
168
169 void swap(thread& other) noexcept;
170
171 bool joinable() const noexcept;
172
173 void join();
174
175 void detach();
176
177 id get_id() const noexcept;
178
179 native_handle_type native_handle();
180
181 static unsigned hardware_concurrency() noexcept;
182
183 private:
184 fid_t id_;
185 aux::joinable_wrapper* joinable_wrapper_{nullptr};
186
187 template<class Callable>
188 friend int aux::thread_main(void*);
189 };
190
191 namespace aux
192 {
193 template<class CallablePtr>
194 int thread_main(void* clbl)
195 {
196 if (!clbl)
197 return 1;
198
199 auto callable = static_cast<CallablePtr>(clbl);
200 (*callable)();
201
202 if (callable->detached()) // No thread owns the wrapper.
203 delete callable;
204
205 return 0;
206 }
207 }
208
209 void swap(thread& x, thread& y) noexcept;
210
211 /**
212 * 30.3.2, namespace this_thread:
213 */
214
215 namespace this_thread
216 {
217 thread::id get_id() noexcept;
218
219 void yield() noexcept;
220
221 template<class Clock, class Duration>
222 void sleep_until(const chrono::time_point<Clock, Duration>& abs_time)
223 {
224 auto now = Clock::now();
225 auto usecs = chrono::duration_cast<chrono::duration<typename Duration::rep, micro>>(abs_time - now);
226
227 std::fibril_usleep(usecs.count());
228 }
229
230 template<class Rep, class Period>
231 void sleep_for(const chrono::duration<Rep, Period>& rel_time)
232 {
233 if (rel_time <= chrono::duration<Rep, Period>::zero())
234 return;
235
236 // TODO: timeouts?
237 auto usecs = chrono::duration_cast<chrono::duration<Rep, micro>>(rel_time);
238 std::fibril_usleep(usecs.count());
239 }
240 }
241
242 template<class T>
243 struct hash;
244
245 class thread::id
246 {
247 public:
248 id() noexcept
249 : id_{}
250 { /* DUMMY BODY */ }
251
252 fid_t id_; // For testing atm public.
253 private:
254 id(fid_t id)
255 : id_{id}
256 { /* DUMMY BODY */ }
257
258 friend class thread;
259
260 friend bool operator==(thread::id, thread::id) noexcept;
261 friend bool operator!=(thread::id, thread::id) noexcept;
262 friend bool operator<(thread::id, thread::id) noexcept;
263 friend bool operator<=(thread::id, thread::id) noexcept;
264 friend bool operator>(thread::id, thread::id) noexcept;
265 friend bool operator>=(thread::id, thread::id) noexcept;
266
267 template<class Char, class Traits>
268 friend basic_ostream<Char, Traits>& operator<<(
269 basic_ostream<Char, Traits>&, thread::id);
270
271 friend struct hash<id>;
272
273 friend id this_thread::get_id() noexcept;
274 };
275
276 bool operator==(thread::id lhs, thread::id rhs) noexcept;
277 bool operator!=(thread::id lhs, thread::id rhs) noexcept;
278 bool operator<(thread::id lhs, thread::id rhs) noexcept;
279 bool operator<=(thread::id lhs, thread::id rhs) noexcept;
280 bool operator>(thread::id lhs, thread::id rhs) noexcept;
281 bool operator>=(thread::id lhs, thread::id rhs) noexcept;
282
283 template<class Char, class Traits>
284 basic_ostream<Char, Traits>& operator<<(basic_ostream<Char, Traits>& out, thread::id id)
285 {
286 out << id.id_;
287
288 return out;
289 }
290
291 template<> // TODO: implement
292 struct hash<thread::id>;
293}
294
295#endif
Note: See TracBrowser for help on using the repository browser.