Line | % of fetches | Source |
---|---|---|
1 | // Copyright (c) 2007-2016 Hartmut Kaiser | |
2 | // Copyright (c) 2011 Bryce Lelbach | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | ||
7 | #include <hpx/runtime/threads/thread_helpers.hpp> | |
8 | ||
9 | #include <hpx/error_code.hpp> | |
10 | #include <hpx/exception.hpp> | |
11 | #include <hpx/runtime.hpp> | |
12 | #include <hpx/state.hpp> | |
13 | #include <hpx/throw_exception.hpp> | |
14 | #include <hpx/runtime/threads/detail/set_thread_state.hpp> | |
15 | #include <hpx/runtime/threads/executors/current_executor.hpp> | |
16 | #include <hpx/runtime/threads/thread_data_fwd.hpp> | |
17 | #include <hpx/runtime/threads/thread_enums.hpp> | |
18 | #ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION | |
19 | #include <hpx/util/backtrace.hpp> | |
20 | #endif | |
21 | #ifdef HPX_HAVE_VERIFY_LOCKS | |
22 | # include <hpx/util/register_locks.hpp> | |
23 | #endif | |
24 | #include <hpx/util/steady_clock.hpp> | |
25 | #include <hpx/util/thread_description.hpp> | |
26 | #include <hpx/util/thread_specific_ptr.hpp> | |
27 | ||
28 | #include <cstddef> | |
29 | #include <limits> | |
30 | #include <sstream> | |
31 | #include <string> | |
32 | #include <utility> | |
33 | ||
34 | /////////////////////////////////////////////////////////////////////////////// | |
35 | namespace hpx { namespace threads | |
36 | { | |
37 | /////////////////////////////////////////////////////////////////////////// | |
38 | thread_state set_thread_state(thread_id_type const& id, thread_state_enum state, | |
39 | thread_state_ex_enum stateex, thread_priority priority, error_code& ec) | |
40 | { | |
41 | if (&ec != &throws) | |
42 | ec = make_success_code(); | |
43 | ||
44 | return detail::set_thread_state(id, state, stateex, | |
45 | priority, std::size_t(-1), ec); | |
46 | } | |
47 | ||
48 | /////////////////////////////////////////////////////////////////////////// | |
49 | thread_id_type set_thread_state(thread_id_type const& id, | |
50 | util::steady_time_point const& abs_time, thread_state_enum state, | |
51 | thread_state_ex_enum stateex, thread_priority priority, error_code& ec) | |
52 | { | |
53 | return detail::set_thread_state_timed(*id->get_scheduler_base(), abs_time, id, | |
54 | state, stateex, priority, std::size_t(-1), ec); | |
55 | } | |
56 | ||
57 | /////////////////////////////////////////////////////////////////////////// | |
58 | thread_state get_thread_state(thread_id_type const& id, error_code& ec) | |
59 | { | |
60 | return id ? id->get_state() : thread_state(terminated, wait_unknown); | |
61 | } | |
62 | ||
63 | /////////////////////////////////////////////////////////////////////////// | |
64 | std::size_t get_thread_phase(thread_id_type const& id, error_code& ec) | |
65 | { | |
66 | return id ? id->get_thread_phase() : std::size_t(~0);; | |
67 | } | |
68 | ||
69 | /////////////////////////////////////////////////////////////////////////// | |
70 | threads::thread_priority get_thread_priority(thread_id_type const& id, | |
71 | error_code& ec) | |
72 | { | |
73 | return id ? id->get_priority() : thread_priority_unknown; | |
74 | } | |
75 | ||
76 | /// The get_stack_size function is part of the thread related API. It | |
77 | std::ptrdiff_t get_stack_size(thread_id_type const& id, error_code& ec) | |
78 | { | |
79 | return id ? id->get_stack_size() : | |
80 | static_cast<std::ptrdiff_t>(thread_stacksize_unknown); | |
81 | } | |
82 | ||
83 | void interrupt_thread(thread_id_type const& id, bool flag, error_code& ec) | |
84 | { | |
85 | if (HPX_UNLIKELY(!id)) { | |
86 | HPX_THROWS_IF(ec, null_thread_id, | |
87 | "hpx::threads::interrupt_thread", | |
88 | "null thread id encountered"); | |
89 | return; | |
90 | } | |
91 | ||
92 | if (&ec != &throws) | |
93 | ec = make_success_code(); | |
94 | ||
95 | id->interrupt(flag); // notify thread | |
96 | ||
97 | // set thread state to pending, if the thread is currently active, | |
98 | // this will be rescheduled until it calls an interruption point | |
99 | set_thread_state(id, pending, wait_abort, | |
100 | thread_priority_normal, ec); | |
101 | } | |
102 | ||
103 | void interruption_point(thread_id_type const& id, error_code& ec) | |
104 | { | |
105 | if (HPX_UNLIKELY(!id)) { | |
106 | HPX_THROWS_IF(ec, null_thread_id, | |
107 | "hpx::threads::interruption_point", | |
108 | "null thread id encountered"); | |
109 | return; | |
110 | } | |
111 | ||
112 | if (&ec != &throws) | |
113 | ec = make_success_code(); | |
114 | ||
115 | id->interruption_point(); // notify thread | |
116 | } | |
117 | ||
118 | /////////////////////////////////////////////////////////////////////////// | |
119 | bool get_thread_interruption_enabled(thread_id_type const& id, | |
120 | error_code& ec) | |
121 | { | |
122 | if (HPX_UNLIKELY(!id)) { | |
123 | HPX_THROW_EXCEPTION(null_thread_id, | |
124 | "hpx::threads::get_thread_interruption_enabled", | |
125 | "null thread id encountered"); | |
126 | return false; | |
127 | } | |
128 | ||
129 | if (&ec != &throws) | |
130 | ec = make_success_code(); | |
131 | ||
132 | return id->interruption_enabled(); | |
133 | } | |
134 | ||
135 | bool set_thread_interruption_enabled(thread_id_type const& id, bool enable, | |
136 | error_code& ec) | |
137 | { | |
138 | if (HPX_UNLIKELY(!id)) { | |
139 | HPX_THROW_EXCEPTION(null_thread_id, | |
140 | "hpx::threads::get_thread_interruption_enabled", | |
141 | "null thread id encountered"); | |
142 | return false; | |
143 | } | |
144 | ||
145 | if (&ec != &throws) | |
146 | ec = make_success_code(); | |
147 | ||
148 | return id->set_interruption_enabled(enable); | |
149 | } | |
150 | ||
151 | bool get_thread_interruption_requested(thread_id_type const& id, | |
152 | error_code& ec) | |
153 | { | |
154 | if (HPX_UNLIKELY(!id)) { | |
155 | HPX_THROWS_IF(ec, null_thread_id, | |
156 | "hpx::threads::get_thread_interruption_requested", | |
157 | "null thread id encountered"); | |
158 | return false; | |
159 | } | |
160 | ||
161 | if (&ec != &throws) | |
162 | ec = make_success_code(); | |
163 | ||
164 | return id->interruption_requested(); | |
165 | } | |
166 | ||
167 | /////////////////////////////////////////////////////////////////////////// | |
168 | std::size_t get_thread_data(thread_id_type const& id, error_code& ec) | |
169 | { | |
170 | if (HPX_UNLIKELY(!id)) { | |
171 | HPX_THROWS_IF(ec, null_thread_id, | |
172 | "hpx::threads::get_thread_data", | |
173 | "null thread id encountered"); | |
174 | return 0; | |
175 | } | |
176 | ||
177 | return id->get_thread_data(); | |
178 | } | |
179 | ||
180 | std::size_t set_thread_data(thread_id_type const& id, std::size_t data, | |
181 | error_code& ec) | |
182 | { | |
183 | if (HPX_UNLIKELY(!id)) { | |
184 | HPX_THROWS_IF(ec, null_thread_id, | |
185 | "hpx::threads::set_thread_data", | |
186 | "null thread id encountered"); | |
187 | return 0; | |
188 | } | |
189 | ||
190 | return id->set_thread_data(data); | |
191 | } | |
192 | ||
193 | //////////////////////////////////////////////////////////////////////////// | |
194 | struct continuation_recursion_count_tag {}; | |
195 | static util::thread_specific_ptr< | |
196 | std::size_t, continuation_recursion_count_tag | |
197 | > continuation_recursion_count; | |
198 | ||
199 | std::size_t& get_continuation_recursion_count() | |
200 | { | |
201 | thread_self* self_ptr = get_self_ptr(); | |
202 | if (self_ptr) | |
203 | return self_ptr->get_continuation_recursion_count(); | |
204 | ||
205 | if (nullptr == continuation_recursion_count.get()) | |
206 | continuation_recursion_count.reset(new std::size_t(0)); | |
207 | ||
208 | return *continuation_recursion_count.get(); | |
209 | } | |
210 | ||
211 | void reset_continuation_recursion_count() | |
212 | { | |
213 | delete continuation_recursion_count.get(); | |
214 | } | |
215 | ||
216 | /////////////////////////////////////////////////////////////////////////// | |
217 | void run_thread_exit_callbacks(thread_id_type const& id, error_code& ec) | |
218 | { | |
219 | if (HPX_UNLIKELY(!id)) { | |
220 | HPX_THROWS_IF(ec, null_thread_id, | |
221 | "hpx::threads::run_thread_exit_callbacks", | |
222 | "null thread id encountered"); | |
223 | return; | |
224 | } | |
225 | ||
226 | if (&ec != &throws) | |
227 | ec = make_success_code(); | |
228 | ||
229 | id->run_thread_exit_callbacks(); | |
230 | } | |
231 | ||
232 | bool add_thread_exit_callback(thread_id_type const& id, | |
233 | util::function_nonser<void()> const& f, error_code& ec) | |
234 | { | |
235 | if (HPX_UNLIKELY(!id)) { | |
236 | HPX_THROWS_IF(ec, null_thread_id, | |
237 | "hpx::threads::add_thread_exit_callback", | |
238 | "null thread id encountered"); | |
239 | return false; | |
240 | } | |
241 | ||
242 | if (&ec != &throws) | |
243 | ec = make_success_code(); | |
244 | ||
245 | return id->add_thread_exit_callback(f); | |
246 | } | |
247 | ||
248 | void free_thread_exit_callbacks(thread_id_type const& id, error_code& ec) | |
249 | { | |
250 | if (HPX_UNLIKELY(!id)) { | |
251 | HPX_THROWS_IF(ec, null_thread_id, | |
252 | "hpx::threads::add_thread_exit_callback", | |
253 | "null thread id encountered"); | |
254 | return; | |
255 | } | |
256 | ||
257 | if (&ec != &throws) | |
258 | ec = make_success_code(); | |
259 | ||
260 | id->free_thread_exit_callbacks(); | |
261 | } | |
262 | ||
263 | /////////////////////////////////////////////////////////////////////////// | |
264 | /// The get_thread_description function is part of the thread related API and | |
265 | /// allows to query the description of one of the thread id | |
266 | util::thread_description get_thread_description(thread_id_type const& id, | |
267 | error_code& ec) | |
268 | { | |
269 | return id ? id->get_description() : util::thread_description("<unknown>"); | |
270 | } | |
271 | ||
272 | util::thread_description set_thread_description(thread_id_type const& id, | |
273 | util::thread_description const& desc, error_code& ec) | |
274 | { | |
275 | if (HPX_UNLIKELY(!id)) { | |
276 | HPX_THROWS_IF(ec, null_thread_id, | |
277 | "hpx::threads::set_thread_description", | |
278 | "null thread id encountered"); | |
279 | return util::thread_description(); | |
280 | } | |
281 | if (&ec != &throws) | |
282 | ec = make_success_code(); | |
283 | ||
284 | return id->set_description(desc); | |
285 | } | |
286 | ||
287 | util::thread_description get_thread_lco_description( | |
288 | thread_id_type const& id, error_code& ec) | |
289 | { | |
290 | if (HPX_UNLIKELY(!id)) { | |
291 | HPX_THROWS_IF(ec, null_thread_id, | |
292 | "hpx::threads::get_thread_lco_description", | |
293 | "null thread id encountered"); | |
294 | return nullptr; | |
295 | } | |
296 | ||
297 | if (&ec != &throws) | |
298 | ec = make_success_code(); | |
299 | ||
300 | return id ? id->get_lco_description() : "<unknown>"; | |
301 | } | |
302 | ||
303 | util::thread_description set_thread_lco_description( | |
304 | thread_id_type const& id, util::thread_description const& desc, | |
305 | error_code& ec) | |
306 | { | |
307 | if (HPX_UNLIKELY(!id)) { | |
308 | HPX_THROWS_IF(ec, null_thread_id, | |
309 | "hpx::threads::set_thread_lco_description", | |
310 | "null thread id encountered"); | |
311 | return nullptr; | |
312 | } | |
313 | ||
314 | if (&ec != &throws) | |
315 | ec = make_success_code(); | |
316 | ||
317 | if (id) | |
318 | return id->set_lco_description(desc); | |
319 | return nullptr; | |
320 | } | |
321 | ||
322 | /////////////////////////////////////////////////////////////////////////// | |
323 | #ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION | |
324 | char const* get_thread_backtrace(thread_id_type const& id, error_code& ec) | |
325 | #else | |
326 | util::backtrace const* get_thread_backtrace(thread_id_type const& id, | |
327 | error_code& ec) | |
328 | #endif | |
329 | { | |
330 | if (HPX_UNLIKELY(!id)) { | |
331 | HPX_THROWS_IF(ec, null_thread_id, | |
332 | "hpx::threads::get_thread_backtrace", | |
333 | "null thread id encountered"); | |
334 | return nullptr; | |
335 | } | |
336 | ||
337 | if (&ec != &throws) | |
338 | ec = make_success_code(); | |
339 | ||
340 | return id ? id->get_backtrace() : nullptr; | |
341 | } | |
342 | ||
343 | #ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION | |
344 | char const* set_thread_backtrace(thread_id_type const& id, | |
345 | char const* bt, error_code& ec) | |
346 | #else | |
347 | util::backtrace const* set_thread_backtrace(thread_id_type const& id, | |
348 | util::backtrace const* bt, error_code& ec) | |
349 | #endif | |
350 | { | |
351 | if (HPX_UNLIKELY(!id)) { | |
352 | HPX_THROWS_IF(ec, null_thread_id, | |
353 | "hpx::threads::set_thread_backtrace", | |
354 | "null thread id encountered"); | |
355 | return nullptr; | |
356 | } | |
357 | ||
358 | if (&ec != &throws) | |
359 | ec = make_success_code(); | |
360 | ||
361 | return id ? id->set_backtrace(bt) : nullptr; | |
362 | } | |
363 | ||
364 | threads::executors::current_executor | |
365 | get_executor(thread_id_type const& id, error_code& ec) | |
366 | { | |
367 | if (HPX_UNLIKELY(!id)) { | |
368 | HPX_THROWS_IF(ec, null_thread_id, | |
369 | "hpx::threads::get_executor", | |
370 | "null thread id encountered"); | |
371 | return executors::current_executor(nullptr); | |
372 | } | |
373 | ||
374 | if (&ec != &throws) | |
375 | ec = make_success_code(); | |
376 | ||
377 | return executors::current_executor(id->get_scheduler_base()); | |
378 | } | |
379 | }} | |
380 | ||
381 | namespace hpx { namespace this_thread | |
382 | { | |
383 | namespace detail | |
384 | { | |
385 | struct reset_lco_description | |
386 | { | |
387 | reset_lco_description(threads::thread_id_type const& id, | |
388 | util::thread_description const& description, | |
389 | error_code& ec) | |
390 | : id_(id), ec_(ec) | |
391 | { | |
392 | old_desc_ = threads::set_thread_lco_description(id_, | |
393 | description, ec_); | |
394 | } | |
395 | ||
396 | ~reset_lco_description() | |
397 | { | |
398 | threads::set_thread_lco_description(id_, old_desc_, ec_); | |
399 | } | |
400 | ||
401 | threads::thread_id_type id_; | |
402 | util::thread_description old_desc_; | |
403 | error_code& ec_; | |
404 | }; | |
405 | ||
406 | #ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION | |
407 | struct reset_backtrace | |
408 | { | |
409 | reset_backtrace(threads::thread_id_type const& id, error_code& ec) | |
410 | : id_(id), | |
411 | backtrace_(new hpx::util::backtrace()), | |
412 | #ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION | |
413 | full_backtrace_(backtrace_->trace()), | |
414 | #endif | |
415 | ec_(ec) | |
416 | { | |
417 | #ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION | |
418 | threads::set_thread_backtrace(id_, full_backtrace_.c_str(), ec_); | |
419 | #else | |
420 | threads::set_thread_backtrace(id_, backtrace_.get(), ec_); | |
421 | #endif | |
422 | } | |
423 | ~reset_backtrace() | |
424 | { | |
425 | threads::set_thread_backtrace(id_, 0, ec_); | |
426 | } | |
427 | ||
428 | threads::thread_id_type id_; | |
429 | boost::scoped_ptr<hpx::util::backtrace> backtrace_; | |
430 | #ifdef HPX_HAVE_THREAD_FULLBACKTRACE_ON_SUSPENSION | |
431 | std::string full_backtrace_; | |
432 | #endif | |
433 | error_code& ec_; | |
434 | }; | |
435 | #endif | |
436 | } | |
437 | ||
438 | /// The function \a suspend will return control to the thread manager | |
439 | /// (suspends the current thread). It sets the new state of this thread | |
440 | /// to the thread state passed as the parameter. | |
441 | /// | |
442 | /// If the suspension was aborted, this function will throw a | |
443 | /// \a yield_aborted exception. | |
444 | threads::thread_state_ex_enum suspend( | |
445 | threads::thread_state_enum state, | |
446 | threads::thread_id_type const& nextid, | |
447 | util::thread_description const& description, error_code& ec) | |
448 | { | |
449 | // let the thread manager do other things while waiting | |
450 | threads::thread_self& self = threads::get_self(); | |
451 | threads::thread_id_type id = threads::get_self_id(); | |
452 | ||
453 | // handle interruption, if needed | |
454 | threads::interruption_point(id, ec); | |
455 | if (ec) return threads::wait_unknown; | |
456 | ||
457 | threads::thread_state_ex_enum statex = threads::wait_unknown; | |
458 | ||
459 | { | |
460 | // verify that there are no more registered locks for this OS-thread | |
461 | #ifdef HPX_HAVE_VERIFY_LOCKS | |
462 | util::verify_no_locks(); | |
463 | #endif | |
464 | #ifdef HPX_HAVE_THREAD_DESCRIPTION | |
465 | detail::reset_lco_description desc(id, description, ec); | |
466 | #endif | |
467 | #ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION | |
468 | detail::reset_backtrace bt(id, ec); | |
469 | #endif | |
470 | ||
471 | // suspend the HPX-thread | |
472 | statex = self.yield(threads::thread_result_type(state, nextid)); | |
473 | } | |
474 | ||
475 | // handle interruption, if needed | |
476 | threads::interruption_point(id, ec); | |
477 | if (ec) return threads::wait_unknown; | |
478 | ||
479 | // handle interrupt and abort | |
480 | if (statex == threads::wait_abort) | |
481 | { | |
482 | std::ostringstream strm; | |
483 | strm << "thread(" << threads::get_self_id() << ", " | |
484 | << threads::get_thread_description(id) | |
485 | << ") aborted (yield returned wait_abort)"; | |
486 | HPX_THROWS_IF(ec, yield_aborted, "suspend", | |
487 | strm.str()); | |
488 | } | |
489 | ||
490 | if (&ec != &throws) | |
491 | ec = make_success_code(); | |
492 | ||
493 | return statex; | |
494 | } | |
495 | ||
496 | threads::thread_state_ex_enum suspend( | |
497 | util::steady_time_point const& abs_time, | |
498 | threads::thread_id_type const& nextid, | |
499 | util::thread_description const& description, error_code& ec) | |
500 | { | |
501 | // schedule a thread waking us up at_time | |
502 | threads::thread_self& self = threads::get_self(); | |
503 | threads::thread_id_type id = threads::get_self_id(); | |
504 | ||
505 | // handle interruption, if needed | |
506 | threads::interruption_point(id, ec); | |
507 | if (ec) return threads::wait_unknown; | |
508 | ||
509 | // let the thread manager do other things while waiting | |
510 | threads::thread_state_ex_enum statex = threads::wait_unknown; | |
511 | ||
512 | { | |
513 | #ifdef HPX_HAVE_VERIFY_LOCKS | |
514 | // verify that there are no more registered locks for this OS-thread | |
515 | util::verify_no_locks(); | |
516 | #endif | |
517 | #ifdef HPX_HAVE_THREAD_DESCRIPTION | |
518 | detail::reset_lco_description desc(id, description, ec); | |
519 | #endif | |
520 | #ifdef HPX_HAVE_THREAD_BACKTRACE_ON_SUSPENSION | |
521 | detail::reset_backtrace bt(id, ec); | |
522 | #endif | |
523 | threads::thread_id_type timer_id = threads::set_thread_state(id, | |
524 | abs_time, threads::pending, threads::wait_timeout, | |
525 | threads::thread_priority_boost, ec); | |
526 | if (ec) return threads::wait_unknown; | |
527 | ||
528 | // suspend the HPX-thread | |
529 | statex = self.yield( | |
530 | threads::thread_result_type(threads::suspended, nextid)); | |
531 | ||
532 | if (statex != threads::wait_timeout) | |
533 | { | |
534 | error_code ec1(lightweight); // do not throw | |
535 | threads::set_thread_state(timer_id, | |
536 | threads::pending, threads::wait_abort, | |
537 | threads::thread_priority_boost, ec1); | |
538 | } | |
539 | } | |
540 | ||
541 | // handle interruption, if needed | |
542 | threads::interruption_point(id, ec); | |
543 | if (ec) return threads::wait_unknown; | |
544 | ||
545 | // handle interrupt and abort | |
546 | if (statex == threads::wait_abort) { | |
547 | std::ostringstream strm; | |
548 | strm << "thread(" << threads::get_self_id() << ", " | |
549 | << threads::get_thread_description(id) | |
550 | << ") aborted (yield returned wait_abort)"; | |
551 | HPX_THROWS_IF(ec, yield_aborted, "suspend_at", | |
552 | strm.str()); | |
553 | } | |
554 | ||
555 | if (&ec != &throws) | |
556 | ec = make_success_code(); | |
557 | ||
558 | return statex; | |
559 | } | |
560 | ||
561 | /////////////////////////////////////////////////////////////////////////// | |
562 | threads::executors::current_executor get_executor(error_code& ec) | |
563 | { | |
564 | return threads::get_executor(threads::get_self_id(), ec); | |
565 | } | |
566 | ||
567 | std::ptrdiff_t get_available_stack_space() | |
568 | { | |
569 | threads::thread_self *self = threads::get_self_ptr(); | |
570 | if(self) | |
571 | { | |
572 | return self->get_available_stack_space(); | |
573 | } | |
574 | ||
575 | return (std::numeric_limits<std::ptrdiff_t>::max)(); | |
576 | } | |
577 | ||
578 | bool has_sufficient_stack_space(std::size_t space_needed) | |
579 | { | |
580 | if (nullptr == hpx::threads::get_self_ptr()) | |
581 | return false; | |
582 | ||
583 | #if defined(HPX_HAVE_THREADS_GET_STACK_POINTER) | |
584 | std::ptrdiff_t remaining_stack = get_available_stack_space(); | |
585 | if (remaining_stack < 0) | |
586 | { | |
587 | HPX_THROW_EXCEPTION(out_of_memory, | |
588 | "has_sufficient_stack_space", "Stack overflow"); | |
589 | } | |
590 | bool sufficient_stack_space = std::size_t(remaining_stack) >= space_needed; | |
591 | ||
592 | // We might find ourselves in the situation where we don't have enough | |
593 | // stack space, but can't really schedule a new thread. In this sitation, | |
594 | // it would be best to change the code that provoked this behaviour | |
595 | // instead of dynamically schedule a new thread. A such, we throw an | |
596 | // exception to point to that problem instead of silently hanging because | |
597 | // the thread will never be executed. | |
598 | if (!sufficient_stack_space && | |
599 | !hpx::threads::threadmanager_is(hpx::state::state_running)) | |
600 | { | |
601 | HPX_THROW_EXCEPTION(invalid_status, | |
602 | "has_sufficient_stack_space", | |
603 | "A potential stack overflow has been detected. Unable to " | |
604 | "schedule new thread during startup/shutdown."); | |
605 | } | |
606 | return sufficient_stack_space; | |
607 | #else | |
608 | return true; | |
609 | #endif | |
610 | } | |
611 | }} | |
612 |
Copyright (c) 2006-2012 Rogue Wave Software, Inc. All Rights Reserved.
Patents pending.