Anklang-0.3.0-1416-gfa8ce8d2 anklang-0.3.0-1416-gfa8ce8d2
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
loop.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "loop.hh"
3#include "atomics.hh"
4#include "utils.hh"
5#include "platform.hh"
6#include "internal.hh"
7#include "strings.hh"
8#include <sys/poll.h>
9#include <sys/resource.h>
10#include <sys/wait.h>
11#include <errno.h>
12#include <atomic>
13#include <unistd.h>
14#include <fcntl.h>
15#include <sys/time.h>
16#include <unistd.h>
17#include <signal.h>
18#include <algorithm>
19#include <list>
20
21namespace Ase {
22
23enum {
24 WAITING = 0,
25 PREPARED,
26 NEEDS_DISPATCH,
27};
28
29static constexpr int16_t WILLQUIT = 0x8000; // quit() called before or during run()
30static constexpr int16_t HASQUIT = 0x4000; // last run() or iterate() was quit
31static constexpr int16_t UNDEFINED_PRIORITY = -32768;
32static constexpr auto PRIORITY_CEILING = LoopPriority::SYSALLOC;
33
34// == PollFD invariants ==
35static_assert (PollFD::IN == POLLIN);
36static_assert (PollFD::PRI == POLLPRI);
37static_assert (PollFD::OUT == POLLOUT);
38static_assert (PollFD::RDNORM == POLLRDNORM);
39static_assert (PollFD::RDBAND == POLLRDBAND);
40static_assert (PollFD::WRNORM == POLLWRNORM);
41static_assert (PollFD::WRBAND == POLLWRBAND);
42static_assert (PollFD::ERR == POLLERR);
43static_assert (PollFD::HUP == POLLHUP);
44static_assert (PollFD::NVAL == POLLNVAL);
45static_assert (sizeof (PollFD) == sizeof (struct pollfd));
46static_assert (offsetof (PollFD, fd) == offsetof (struct pollfd, fd));
47static_assert (sizeof (((PollFD*) 0)->fd) == sizeof (((struct pollfd*) 0)->fd));
48static_assert (offsetof (PollFD, events) == offsetof (struct pollfd, events));
49static_assert (sizeof (((PollFD*) 0)->events) == sizeof (((struct pollfd*) 0)->events));
50static_assert (offsetof (PollFD, revents) == offsetof (struct pollfd, revents));
51static_assert (sizeof (((PollFD*) 0)->revents) == sizeof (((struct pollfd*) 0)->revents));
52
53// === Stupid ID allocator ===
54static std::atomic<uint64_t> global_id_counter { 101000000 };
55static LoopID
56alloc_id ()
57{
58 const uint64_t id = global_id_counter.fetch_add (+1, std::memory_order_relaxed);
59 assert_return (id != 0, {});
60 return LoopID (id);
61}
62
63static void
64release_id (LoopID id)
65{
66 assert_return (id != LoopID::INVALID);
67}
68
69// === Loop ==
70Loop::Loop() {}
71Loop::~Loop() {}
72
73// === LoopImpl ===
75class LoopImpl : public Loop
76{
78 std::vector<PollFD> cached_pollfd_vector_;
79 std::vector<LoopSourceP*> cached_poll_candidates_;
80public:
83 SourceList sources_;
84 std::vector<LoopSourceP> poll_sources_;
85 AtomicStack<LoopSourceP> pending_add_stack_;
86 AtomicStack<LoopID> pending_cancel_stack_;
87 std::atomic<int16_t> quit_code_ = 0;
88 uint running_ = 0;
89 uint rr_index_ = 0;
90 int16 dispatch_priority_ = 0;
91 EventFd eventfd_;
92 GlibGMainContext *gcontext_;
93 bool finishable_L ();
94 int run () override;
95 bool running () override;
96 void wakeup () override;
97 void quit (int quit_code) override;
98 bool finishable () override;
99 bool iterate (bool may_block) override;
100 void iterate_pending () override;
101 bool pending () override;
102 bool set_g_main_context (GlibGMainContext *glib_main_context) override;
103 bool has_quit () override;
104 bool iterate_loops_Lm (LoopState&, bool b, bool d);
105 void destroy_loop () override;
106 LoopSourceP& find_first_L ();
107 LoopSourceP& find_source_L (LoopID id);
108 bool has_primary_L (void);
109 void remove_source_Lm (LoopSourceP source);
110 void kill_sources_Lm (void);
111 void unpoll_sources_U ();
112 void collect_sources_Lm (LoopState&);
113 bool prepare_sources_Lm (LoopState&, std::vector<PollFD>&);
114 bool check_sources_Lm (LoopState&, const std::vector<PollFD>&);
115 void dispatch_source_Lm (LoopState&);
116 void process_atomic_stacks ();
117 LoopID add_source (LoopSourceP loop_source, LoopPriority priority) override;
118 void cancel (LoopID id) override;
119 void cancel (LoopID *idp) override;
120 bool has_primary () override;
121 LoopID exec_sigchld (int64_t pid, const SigchldSlot &vfunc, LoopPriority priority) override;
122 bool exec_once (uint delay_ms, LoopID *once_id, const VoidSlot &vfunc, LoopPriority priority) override;
123 explicit LoopImpl ();
124 virtual ~LoopImpl ();
125};
126
127// === LoopImpl ===
128inline LoopSourceP&
129LoopImpl::find_first_L()
130{
131 static LoopSourceP null_source;
132 return sources_.empty() ? null_source : sources_[0];
133}
134
135inline LoopSourceP&
136LoopImpl::find_source_L (LoopID id)
137{
138 for (SourceList::iterator lit = sources_.begin(); lit != sources_.end(); lit++)
139 if (id == (*lit)->id_)
140 return *lit;
141 static LoopSourceP null_source;
142 return null_source;
143}
144
145bool
146LoopImpl::has_primary_L()
147{
148 for (SourceList::iterator lit = sources_.begin(); lit != sources_.end(); lit++)
149 if ((*lit)->primary())
150 return true;
151 return false;
152}
153
154void
155LoopImpl::process_atomic_stacks ()
156{
157 LoopSourceP source;
158 while (pending_add_stack_.pop (source))
159 sources_.push_back (source);
160 LoopID cancel_id;
161 while (pending_cancel_stack_.pop (cancel_id))
162 {
163 LoopSourceP &src = find_source_L (cancel_id);
164 if (src)
165 remove_source_Lm (src);
166 }
167}
168
169bool
171{
172 return has_primary_L();
173}
174
185LoopID
187{
188 static_assert (UNDEFINED_PRIORITY < 1, "");
189 assert_return (static_cast<uint16_t> (priority) >= 1 && priority <= PRIORITY_CEILING, LoopID::INVALID);
190 assert_return (source != NULL, LoopID::INVALID);
191 assert_return (source->loop_ == NULL, LoopID::INVALID);
192 source->loop_ = this;
193 const auto source_id = source->id_ = alloc_id();
194 source->loop_state_ = WAITING;
195 source->priority_ = static_cast<uint16_t> (priority);
196 if (pending_add_stack_.push (source))
197 wakeup();
198 return source_id;
199}
200
201void
202LoopImpl::remove_source_Lm (LoopSourceP source)
203{
204 assert_return (source->loop_ == this);
205 source->loop_ = NULL;
206 source->loop_state_ = WAITING;
207 auto pos = find (sources_.begin(), sources_.end(), source);
208 assert_return (pos != sources_.end());
209 sources_.erase (pos);
210 release_id (source->id_);
211 source->id_ = LoopID::INVALID;
212 source->destroy();
213}
214
222void
224{
225 return_unless (id != LoopID::INVALID);
226 if (pending_cancel_stack_.push (id))
227 wakeup();
228}
229
237void
238LoopImpl::cancel (LoopID *idp)
239{
240 if (idp) {
241 if (*idp != LoopID::INVALID)
242 cancel (*idp);
243 *idp = LoopID::INVALID;
244 }
245}
246
247bool
248LoopImpl::exec_once (uint delay_ms, LoopID *once_id, const VoidSlot &vfunc, LoopPriority priority)
249{
250 assert_return (once_id != nullptr, false);
251 assert_return (static_cast<uint16_t> (priority) >= 1 && priority <= PRIORITY_CEILING, false);
252 if (!vfunc) {
253 cancel (once_id);
254 return false;
255 }
256 auto once_handler = [vfunc,once_id]() { *once_id = LoopID::INVALID; vfunc(); };
257 LoopSourceP source = TimedSource::create (once_handler, delay_ms, 0);
258 source->loop_ = this;
259 source->id_ = alloc_id();
260 source->loop_state_ = WAITING;
261 source->priority_ = static_cast<uint16_t> (priority);
262 LoopID warn_id = LoopID::INVALID;
263 {
264 if (*once_id != LoopID::INVALID) {
265 LoopSourceP &source = find_source_L (*once_id);
266 if (source)
267 remove_source_Lm (source);
268 else
269 warn_id = *once_id;
270 }
271 sources_.push_back (source);
272 *once_id = source->id_;
273 }
274 if (warn_id != LoopID::INVALID)
275 warning ("%s: failed to remove loop source: %lu", __func__, static_cast<uint64_t> (warn_id));
276 wakeup();
277 return true;
278}
279
280LoopID
281LoopImpl::exec_sigchld (int64_t pid, const SigchldSlot &slot, LoopPriority priority)
282{
283 return add_source (SigchldSource::create (pid, slot), priority);
284}
285
286void
287LoopImpl::kill_sources_Lm()
288{
289 for (;;)
290 {
291 LoopSourceP &source = find_first_L();
292 if (source == NULL)
293 break;
294 remove_source_Lm (source);
295 }
296 unpoll_sources_U(); // unlocked
297}
298
306LoopP
308{
309 static thread_local LoopP thread_loop = LoopImpl::make_shared();
310 return thread_loop;
311}
312
316{
317 const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
318 auto promise = this->make_promise<uint64_t> ([&] (auto resolve) // std::function<void(uint64_t)>
319 {
320 this->add ([resolve, start]()
321 {
322 const std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
323 const uint64_t elapsed = std::chrono::duration_cast<std::chrono::milliseconds> (end - start).count();
324 resolve (elapsed);
325 return false;
326 }, ms);
327 });
328 return promise;
329}
330
331// === LoopImpl ===
332LoopImpl::LoopImpl() :
333 gcontext_ (nullptr)
334{
335 cached_pollfd_vector_.reserve (1);
336 cached_poll_candidates_.reserve (1);
337 const int err = eventfd_.open();
338 if (err < 0)
339 fatal_error ("LoopImpl: failed to create wakeup pipe: %s", strerror (-err));
340 // eventfd_ must work upfront for wakeup() to work
341}
342
343LoopImpl::~LoopImpl()
344{
345 destroy_loop();
346 assert_return (sources_.empty() == true);
347}
348
358void
360{
361 set_g_main_context (NULL);
362 // guard main_loop_ pointer across callbacks
363 LoopP main_loop_guard = shared_ptr_cast<Loop*> (this);
364 process_atomic_stacks();
365 kill_sources_Lm();
366}
367
372void
374{
375 if (eventfd_.opened())
376 eventfd_.wakeup();
377}
378
385int
387{
388 LoopP main_loop_guard = shared_ptr_cast<Loop> (this);
389 LoopState state;
390 quit_code_ &= ~HASQUIT; // reset old quit code, from former run()
391 running_ += 1;
392 while (ISLIKELY (!(WILLQUIT & quit_code_)))
393 iterate_loops_Lm (state, true, true);
394 running_ -= 1;
395 if (quit_code_ & WILLQUIT) // apply quit code from this run()
396 quit_code_ = HASQUIT | (quit_code_ & ~WILLQUIT);
397 return quit_code_ & ~HASQUIT; // return actual exit code, not internal flag
398}
399
400bool
402{
403 return running_ > 0;
404}
405
406bool
408{
409 return HASQUIT & quit_code_; // last run() was quit
410}
411
420void
421LoopImpl::quit (int quit_code)
422{
423 if (!(WILLQUIT & quit_code_)) {
424 quit_code_ = quit_code | WILLQUIT;
425 wakeup ();
426 }
427}
428
429bool
430LoopImpl::finishable_L()
431{
432 // finishable if no primary sources remain
433 return !has_primary_L();
434}
435
436bool
438{
439 return finishable_L();
440}
441
452bool
453LoopImpl::iterate (bool may_block)
454{
455 LoopP main_loop_guard = shared_ptr_cast<Loop> (this);
456 LoopState state;
457 running_ += 1;
458 const bool sources_pending = iterate_loops_Lm (state, may_block, true);
459 running_ -= 1;
460 return sources_pending;
461}
462
469void
471{
472 LoopP main_loop_guard = shared_ptr_cast<Loop> (this);
473 LoopState state;
474 const uint16_t saved_quit_code_ = quit_code_; // save former quit code
475 running_ += 1;
476 while (ISLIKELY (!(WILLQUIT & quit_code_))) // abort on quitting twice
477 if (!iterate_loops_Lm (state, false, true))
478 break;
479 running_ -= 1;
480 if (saved_quit_code_ & HASQUIT) // preserve former quit code
481 quit_code_ = saved_quit_code_ & ~WILLQUIT;
482 else if (quit_code_ & WILLQUIT) // or apply recent quit code
483 quit_code_ = HASQUIT | (quit_code_ & ~WILLQUIT);
484}
485
486bool
488{
489 LoopState state;
490 LoopP main_loop_guard = shared_ptr_cast<Loop> (this);
491 running_ += 1;
492 const bool more = iterate_loops_Lm (state, false, false);
493 running_ -= 1;
494 return more;
495}
496
497bool
498LoopImpl::set_g_main_context (GlibGMainContext *glib_main_context)
499{
500#ifdef __G_LIB_H__
501 if (glib_main_context)
502 {
503 if (gcontext_)
504 return false;
505 if (!g_main_context_acquire (glib_main_context))
506 return false;
507 gcontext_ = g_main_context_ref (glib_main_context);
508 }
509 else if (gcontext_)
510 {
511 glib_main_context = gcontext_;
512 gcontext_ = NULL;
513 g_main_context_release (glib_main_context);
514 g_main_context_unref (glib_main_context);
515 }
516 return true;
517#else
518 return false;
519#endif
520}
521
522#ifdef __G_LIB_H__
523static GPollFD*
524mk_gpollfd (PollFD *pfd)
525{
526 GPollFD *gpfd = (GPollFD*) pfd;
527 static_assert (sizeof (GPollFD) == sizeof (PollFD), "");
528 static_assert (sizeof (gpfd->fd) == sizeof (pfd->fd), "");
529 static_assert (sizeof (gpfd->events) == sizeof (pfd->events), "");
530 static_assert (sizeof (gpfd->revents) == sizeof (pfd->revents), "");
531 static_assert (offsetof (GPollFD, fd) == offsetof (PollFD, fd), "");
532 static_assert (offsetof (GPollFD, events) == offsetof (PollFD, events), "");
533 static_assert (offsetof (GPollFD, revents) == offsetof (PollFD, revents), "");
534 static_assert (PollFD::IN == int (G_IO_IN), "");
535 static_assert (PollFD::PRI == int (G_IO_PRI), "");
536 static_assert (PollFD::OUT == int (G_IO_OUT), "");
537 // static_assert (PollFD::RDNORM == int (G_IO_RDNORM), "");
538 // static_assert (PollFD::RDBAND == int (G_IO_RDBAND), "");
539 // static_assert (PollFD::WRNORM == int (G_IO_WRNORM), "");
540 // static_assert (PollFD::WRBAND == int (G_IO_WRBAND), "");
541 static_assert (PollFD::ERR == int (G_IO_ERR), "");
542 static_assert (PollFD::HUP == int (G_IO_HUP), "");
543 static_assert (PollFD::NVAL == int (G_IO_NVAL), "");
544 return gpfd;
545}
546#endif
547
548void
549LoopImpl::unpoll_sources_U() // must be unlocked!
550{
551 // clear poll sources
552 poll_sources_.resize (0);
553}
554
555void
556LoopImpl::collect_sources_Lm (LoopState &state)
557{
558 // enforce clean slate
559 if (UNLIKELY (!poll_sources_.empty()))
560 {
561 unpoll_sources_U(); // unlocked
562 assert_return (poll_sources_.empty());
563 }
564 if (UNLIKELY (!state.seen_primary))
565 state.seen_primary = true;
566 // cache vector, otherwise malloc shows up in the profiles
567 std::vector<LoopSourceP*> &poll_candidates = cached_poll_candidates_;
568 poll_candidates.resize (0);
569 // determine dispatch priority & collect sources for preparing
570 dispatch_priority_ = UNDEFINED_PRIORITY; // initially, consider sources at *all* priorities
571 for (SourceList::iterator lit = sources_.begin(); lit != sources_.end(); lit++)
572 {
573 LoopSource &source = **lit;
574 if (UNLIKELY (!state.seen_primary && source.primary_))
575 state.seen_primary = true;
576 if (source.loop_ != this || // ignore destroyed and
577 (source.dispatching_ && !source.may_recurse_)) // avoid unallowed recursion
578 continue;
579 if (source.priority_ > dispatch_priority_ && // ignore lower priority sources
580 source.loop_state_ == NEEDS_DISPATCH) // if NEEDS_DISPATCH sources remain
581 dispatch_priority_ = source.priority_; // so raise dispatch_priority_
582 if (source.priority_ > dispatch_priority_ || // add source if it is an eligible
583 (source.priority_ == dispatch_priority_ && // candidate, baring future raises
584 source.loop_state_ == NEEDS_DISPATCH)) // of dispatch_priority_...
585 poll_candidates.push_back (&*lit); // collect only, adding ref() later
586 }
587 // ensure ref counts on all prepare sources
588 assert_return (poll_sources_.empty());
589 for (size_t i = 0; i < poll_candidates.size(); i++)
590 if ((*poll_candidates[i])->priority_ > dispatch_priority_ || // throw away lower priority sources
591 ((*poll_candidates[i])->priority_ == dispatch_priority_ &&
592 (*poll_candidates[i])->loop_state_ == NEEDS_DISPATCH)) // re-poll sources that need dispatching
593 poll_sources_.push_back (*poll_candidates[i]);
594 /* here, poll_sources_ contains either all sources, or only the highest priority
595 * NEEDS_DISPATCH sources plus higher priority sources. giving precedence to the
596 * remaining NEEDS_DISPATCH sources ensures round-robin processing.
597 */
598}
599
600bool
601LoopImpl::prepare_sources_Lm (LoopState &state, std::vector<PollFD> &pfda)
602{
603 // prepare sources, up to NEEDS_DISPATCH priority
604 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
605 {
606 LoopSource &source = **lit;
607 if (source.loop_ != this) // test undestroyed
608 continue;
609 int64 timeout = -1;
610 const bool need_dispatch = source.prepare (state, &timeout);
611 if (source.loop_ != this)
612 continue; // ignore newly destroyed sources
613 if (need_dispatch)
614 {
615 dispatch_priority_ = std::max (dispatch_priority_, source.priority_); // upgrade dispatch priority
616 source.loop_state_ = NEEDS_DISPATCH;
617 continue;
618 }
619 source.loop_state_ = PREPARED;
620 if (timeout >= 0)
621 state.timeout_usecs = std::min (state.timeout_usecs, timeout);
622 uint npfds = source.n_pfds();
623 for (uint i = 0; i < npfds; i++)
624 if (source.pfds_[i].pfd->fd >= 0)
625 {
626 uint idx = pfda.size();
627 source.pfds_[i].idx = idx;
628 pfda.push_back (*source.pfds_[i].pfd);
629 pfda[idx].revents = 0;
630 }
631 else
632 source.pfds_[i].idx = 4294967295U; // UINT_MAX
633 }
634 return dispatch_priority_ > UNDEFINED_PRIORITY;
635}
636
637bool
638LoopImpl::check_sources_Lm (LoopState &state, const std::vector<PollFD> &pfda)
639{
640 // check polled sources
641 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
642 {
643 LoopSource &source = **lit;
644 if (source.loop_ != this && // test undestroyed
645 source.loop_state_ != PREPARED)
646 continue; // only check prepared sources
647 uint npfds = source.n_pfds();
648 for (uint i = 0; i < npfds; i++)
649 {
650 uint idx = source.pfds_[i].idx;
651 if (idx < pfda.size() &&
652 source.pfds_[i].pfd->fd == pfda[idx].fd)
653 source.pfds_[i].pfd->revents = pfda[idx].revents;
654 else
655 source.pfds_[i].idx = 4294967295U; // UINT_MAX
656 }
657 bool need_dispatch = source.check (state);
658 if (source.loop_ != this)
659 continue; // ignore newly destroyed sources
660 if (need_dispatch)
661 {
662 dispatch_priority_ = std::max (dispatch_priority_, source.priority_); // upgrade dispatch priority
663 source.loop_state_ = NEEDS_DISPATCH;
664 }
665 else
666 source.loop_state_ = WAITING;
667 }
668 return dispatch_priority_ > UNDEFINED_PRIORITY;
669}
670
671void
672LoopImpl::dispatch_source_Lm (LoopState &state)
673{
674 // find a source to dispatch at dispatch_priority_
675 LoopSourceP dispatch_source = NULL; // shared_ptr to keep alive even if everything else is destroyed
676 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
677 {
678 LoopSourceP &source = *lit;
679 if (source->loop_ == this && // test undestroyed
680 source->priority_ == dispatch_priority_ && // only dispatch at dispatch priority
681 source->loop_state_ == NEEDS_DISPATCH)
682 {
683 dispatch_source = source;
684 break;
685 }
686 }
687 dispatch_priority_ = UNDEFINED_PRIORITY;
688 // dispatch single source
689 if (dispatch_source)
690 {
691 dispatch_source->loop_state_ = WAITING;
692 const bool old_was_dispatching = dispatch_source->was_dispatching_;
693 dispatch_source->was_dispatching_ = dispatch_source->dispatching_;
694 dispatch_source->dispatching_ = true;
695 const bool keep_alive = dispatch_source->dispatch (state);
696 dispatch_source->dispatching_ = dispatch_source->was_dispatching_;
697 dispatch_source->was_dispatching_ = old_was_dispatching;
698 if (dispatch_source->loop_ == this && !keep_alive)
699 remove_source_Lm (dispatch_source);
700 }
701}
702
703bool
704LoopImpl::iterate_loops_Lm (LoopState &state, bool may_block, bool may_dispatch)
705{
706 assert_return (state.phase == state.NONE, false);
707 process_atomic_stacks ();
708 std::vector<PollFD> &pfda = cached_pollfd_vector_;
709 pfda.resize (0); // cache array between iterations, to reduce malloc overhead
710 // allow poll wakeups
711 const PollFD wakeup = { eventfd_.inputfd(), PollFD::IN, 0 };
712 const uint wakeup_idx = 0; // wakeup_idx = pfda.size();
713 pfda.push_back (wakeup);
714 // collect
715 state.phase = state.COLLECT;
716 state.seen_primary = false;
717 collect_sources_Lm (state);
718 // prepare
719 bool any_dispatchable = false;
720 state.phase = state.PREPARE;
721 state.timeout_usecs = INT64_MAX;
722 state.current_time_usecs = timestamp_realtime();
723 bool adispatchable = false;
724 bool gdispatchable = false;
725 adispatchable = prepare_sources_Lm (state, pfda);
726 any_dispatchable |= adispatchable;
727 // prepare GLib
728 ASE_UNUSED const int gfirstfd = pfda.size();
729 ASE_UNUSED int gpriority = INT32_MIN;
730 if (ASE_UNLIKELY (gcontext_))
731 {
732#ifdef __G_LIB_H__
733 gdispatchable = g_main_context_prepare (gcontext_, &gpriority) != 0;
734 any_dispatchable |= gdispatchable;
735 int gtimeout = INT32_MAX;
736 int gnfds = g_main_context_query (gcontext_, gpriority, &gtimeout, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
737 while (gnfds >= 0 && size_t (gnfds) != pfda.size() - gfirstfd)
738 {
739 pfda.resize (gfirstfd + gnfds);
740 gtimeout = INT32_MAX;
741 gnfds = g_main_context_query (gcontext_, gpriority, &gtimeout, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
742 }
743 if (gtimeout >= 0)
744 state.timeout_usecs = MIN (state.timeout_usecs, gtimeout * int64 (1000));
745#endif
746 }
747 // poll file descriptors
748 int64 timeout_msecs = state.timeout_usecs / 1000;
749 if (state.timeout_usecs > 0 && timeout_msecs <= 0)
750 timeout_msecs = 1;
751 if (!may_block || any_dispatchable)
752 timeout_msecs = 0;
753 state.timeout_usecs = 0;
754 int presult;
755 do
756 presult = poll ((struct pollfd*) &pfda[0], pfda.size(), std::min (timeout_msecs, int64 (2147483647))); // INT_MAX
757 while (presult < 0 && errno == EAGAIN); // EINTR may indicate a signal
758 if (presult < 0 && errno != EINTR)
759 warning ("LoopImpl: poll() failed: %s", strerror());
760 else if (pfda[wakeup_idx].revents)
761 eventfd_.flush(); // restart queueing wakeups, possibly triggered by dispatching
762 // check
763 state.phase = state.CHECK;
764 state.current_time_usecs = timestamp_realtime();
765 int16 max_dispatch_priority = -32768;
766 adispatchable |= check_sources_Lm (state, pfda);
767 if (adispatchable)
768 {
769 any_dispatchable = true;
770 max_dispatch_priority = std::max (max_dispatch_priority, dispatch_priority_);
771 }
772 // check GLib
773 if (ASE_UNLIKELY (gcontext_))
774 {
775#ifdef __G_LIB_H__
776 gdispatchable = g_main_context_check (gcontext_, gpriority, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
777 any_dispatchable |= gdispatchable;
778#endif
779 }
780 // dispatch
781 if (may_dispatch && any_dispatchable)
782 {
783 state.phase = state.DISPATCH;
784 if (gdispatchable && (!adispatchable || (rr_index_++ & 1)))
785 {
786#ifdef __G_LIB_H__
787 g_main_context_dispatch (gcontext_);
788#endif
789 }
790 else if (adispatchable)
791 dispatch_source_Lm (state); // passes on shared_ptr to keep alive while locked
792 }
793 // cleanup
794 state.phase = state.NONE;
795 unpoll_sources_U(); // unlocked
796 return any_dispatchable; // need to dispatch or recheck
797}
798
799// === LoopSource ===
800LoopSource::LoopSource () :
801 loop_ (NULL),
802 pfds_ (NULL),
803 id_ (LoopID::INVALID),
804 priority_ (UNDEFINED_PRIORITY),
805 loop_state_ (0),
806 may_recurse_ (0),
807 dispatching_ (0),
808 was_dispatching_ (0),
809 primary_ (true)
810{}
811
812uint
813LoopSource::n_pfds ()
814{
815 uint i = 0;
816 if (pfds_)
817 while (pfds_[i].pfd)
818 i++;
819 return i;
820}
821
822void
823LoopSource::may_recurse (bool may_recurse)
824{
825 may_recurse_ = may_recurse;
826}
827
828bool
830{
831 return may_recurse_;
832}
833
834bool
836{
837 return primary_;
838}
839
840void
841LoopSource::primary (bool is_primary)
842{
843 primary_ = is_primary;
844}
845
846bool
848{
849 return dispatching_ && was_dispatching_;
850}
851
852void
854{
855 const uint idx = n_pfds();
856 uint npfds = idx + 1;
857 pfds_ = (typeof (pfds_)) realloc (pfds_, sizeof (pfds_[0]) * (npfds + 1));
858 if (!pfds_)
859 fatal_error ("LoopSource: out of memory");
860 pfds_[npfds].idx = 4294967295U; // UINT_MAX
861 pfds_[npfds].pfd = NULL;
862 pfds_[idx].idx = 4294967295U; // UINT_MAX
863 pfds_[idx].pfd = pfd;
864}
865
866void
868{
869 uint idx, npfds = n_pfds();
870 for (idx = 0; idx < npfds; idx++)
871 if (pfds_[idx].pfd == pfd)
872 break;
873 if (idx < npfds)
874 {
875 pfds_[idx].idx = 4294967295U; // UINT_MAX
876 pfds_[idx].pfd = pfds_[npfds - 1].pfd;
877 pfds_[idx].idx = pfds_[npfds - 1].idx;
878 pfds_[npfds - 1].idx = 4294967295U; // UINT_MAX
879 pfds_[npfds - 1].pfd = NULL;
880 }
881 else
882 warning ("LoopSource: unremovable PollFD: %p (fd=%d)", pfd, pfd->fd);
883}
884
885void
886LoopSource::destroy ()
887{}
888
889void
891{
892 if (loop_)
893 loop_->cancel (source_id());
894}
895
896LoopSource::~LoopSource ()
897{
898 assert_return (loop_ == NULL);
899 if (pfds_)
900 free (pfds_);
901}
902
903// == DispatcherSource ==
904DispatcherSource::DispatcherSource (const DispatcherSlot &slot) :
905 slot_ (slot)
906{}
907
908DispatcherSource::~DispatcherSource ()
909{
910 slot_ = NULL;
911}
912
913bool
914DispatcherSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
915{
916 return slot_ (state);
917}
918
919bool
921{
922 return slot_ (state);
923}
924
925bool
927{
928 return slot_ (state);
929}
930
931void
932DispatcherSource::destroy()
933{
934 LoopState state;
935 state.phase = state.DESTROY;
936 slot_ (state);
937}
938
939// == USignalSource ==
940USignalSource::USignalSource (int8 signum, const USignalSlot &slot) :
941 slot_ (slot), signum_ (signum)
942{
943 const uint s = 128 + signum_;
944 index_ = s / 32;
945 shift_ = s % 32;
946}
947
948USignalSource::~USignalSource ()
949{
950 slot_ = NULL;
951}
952
953static std::atomic<uint32> usignals_notified[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
954
956void
958{
959 const uint s = 128 + signum;
960 const uint index = s / 32;
961 const uint shift = s % 32;
962 usignals_notified[index] |= 1 << shift;
963}
964
965bool
966USignalSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
967{
968 return usignals_notified[index_] & (1 << shift_);
969}
970
971bool
973{
974 return usignals_notified[index_] & (1 << shift_);
975}
976
977bool
979{
980 usignals_notified[index_] &= ~(1 << shift_);
981 return slot_ (signum_);
982}
983
984void
985USignalSource::destroy()
986{}
987
989write_uint (uint32_t i)
990{
992 char *c = &a.back();
993 ASE_ASSERT (c>=&a[0] && c<&a[a.size()]);
994 *c-- = 0;
995 *c = '0' + (i % 10);
996 i /= 10;
997 while (i != 0) {
998 *(--c) = '0' + (i % 10);
999 i /= 10;
1000 }
1001 if (c > &a[0])
1002 memmove (&a[0], c, &a.back() + 1 - c);
1003 ASE_ASSERT (c>=&a[0] && c<&a[a.size()]);
1004 return a;
1005}
1006
1007void
1008USignalSource::install_sigaction (int8 signum)
1009{
1010 struct sigaction action;
1011 action.sa_handler = [] (int signum) {
1012 if (0) { // DEBUG
1013 constexpr size_t N = 1024;
1014 char buf[N] = __FILE__ ":";
1015 strncat (buf, &write_uint (__LINE__)[0], N);
1016 strncat (buf, ": sa_handler: signal=", N);
1017 strncat (buf, &write_uint (signum)[0], N);
1018 strncat (buf, "\n", N);
1019 ::write (2, buf, strlen (buf));
1020 }
1021 USignalSource::raise (signum);
1022 };
1023 sigemptyset (&action.sa_mask);
1024 action.sa_flags = SA_NOMASK;
1025 sigaction (signum, &action, nullptr);
1026}
1027
1028// === SigchldSource ===
1029static std::atomic<uint64_t> sigchld_counter = 0;
1030
1031SigchldSource::SigchldSource (int64_t pid, const SigchldSlot &slot) :
1032 slot_ (slot), pid_ (pid)
1033{
1034 if (uint64_t unused = 0; sigchld_counter.compare_exchange_strong (unused, 1)) {
1035 struct sigaction action;
1036 action.sa_handler = [] (int signum) {
1037 sigchld_counter++;
1038 };
1039 sigemptyset (&action.sa_mask);
1040 action.sa_flags = SA_NOMASK;
1041 sigaction (SIGCHLD, &action, nullptr);
1042 }
1043}
1044
1045SigchldSource::~SigchldSource()
1046{}
1047
1048bool
1049SigchldSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1050{
1051 return pid_ && sigchld_counter_ != sigchld_counter;
1052}
1053
1054bool
1056{
1057 return pid_ && sigchld_counter_ != sigchld_counter;
1058}
1059
1060bool
1062{
1063 if (pid_) {
1064 sigchld_counter_ = sigchld_counter;
1065 // Use pid_ to avoid reaping unknown children
1066 int status = 0;
1067 struct rusage ru {};
1068 const pid_t child_pid = wait4 (pid_, &status, WNOHANG, &ru);
1069 if (child_pid > 0) {
1070 slot_ (pid_, status);
1071 if (debug_key_enabled ("sigchld")) {
1072 printerr (" Child Pid %d user time: %ld.%06ld sec\n", child_pid, ru.ru_utime.tv_sec, ru.ru_utime.tv_usec);
1073 printerr (" System time: %ld.%06ld sec\n", ru.ru_stime.tv_sec, ru.ru_stime.tv_usec);
1074 printerr (" Max RSS: %ld KB\n", ru.ru_maxrss);
1075 printerr (" Page faults: %ld\n", ru.ru_minflt);
1076 printerr (" I/O operations: %ld\n", ru.ru_inblock + ru.ru_oublock);
1077 printerr (" Voluntary context switches: %ld\n", ru.ru_nvcsw);
1078 printerr (" Involuntary context switches: %ld\n", ru.ru_nivcsw);
1079 printerr ("\n");
1080 }
1081 if (WIFEXITED (status) || WIFSIGNALED (status)) {
1082 // Child exited
1083 pid_ = 0;
1084 return false; // destroy
1085 }
1086 }
1087 }
1088 return true; // keep_alive
1089}
1090
1091void
1092SigchldSource::destroy ()
1093{
1094 pid_ = 0;
1095}
1096
1097// == TimedSource ==
1098TimedSource::TimedSource (const VoidSlot &slot, uint initial_interval_msecs, uint repeat_interval_msecs) :
1099 expiration_usecs_ (timestamp_realtime() + 1000ULL * initial_interval_msecs),
1100 interval_msecs_ (repeat_interval_msecs), first_interval_ (true),
1101 oneshot_ (true), void_slot_ (slot)
1102{}
1103
1104TimedSource::TimedSource (const BoolSlot &slot, uint initial_interval_msecs, uint repeat_interval_msecs) :
1105 expiration_usecs_ (timestamp_realtime() + 1000ULL * initial_interval_msecs),
1106 interval_msecs_ (repeat_interval_msecs), first_interval_ (true),
1107 oneshot_ (false), bool_slot_ (slot)
1108{}
1109
1110bool
1111TimedSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1112{
1113 if (state.current_time_usecs >= expiration_usecs_)
1114 return true; /* timeout expired */
1115 if (!first_interval_)
1116 {
1117 uint64 interval = interval_msecs_ * 1000ULL;
1118 if (state.current_time_usecs + interval < expiration_usecs_)
1119 expiration_usecs_ = state.current_time_usecs + interval; /* clock warped back in time */
1120 }
1121 *timeout_usecs_p = std::min (expiration_usecs_ - state.current_time_usecs, uint64 (2147483647)); // INT_MAX
1122 return 0 == *timeout_usecs_p;
1123}
1124
1125bool
1127{
1128 return state.current_time_usecs >= expiration_usecs_;
1129}
1130
1131bool
1133{
1134 bool repeat = false;
1135 first_interval_ = false;
1136 if (oneshot_ && void_slot_ != NULL)
1137 void_slot_ ();
1138 else if (!oneshot_ && bool_slot_ != NULL)
1139 repeat = bool_slot_ ();
1140 if (repeat)
1141 expiration_usecs_ = timestamp_realtime() + 1000ULL * interval_msecs_;
1142 return repeat;
1143}
1144
1145TimedSource::~TimedSource ()
1146{
1147 if (oneshot_)
1148 void_slot_.~VoidSlot();
1149 else
1150 bool_slot_.~BoolSlot();
1151}
1152
1153// == PollFDSource ==
1166PollFDSource::PollFDSource (const BPfdSlot &slot, int fd, const String &mode) :
1167 pfd_ ((PollFD) { fd, 0, 0 }),
1168 never_close_ (strchr (mode.c_str(), 'C') != NULL),
1169 oneshot_ (false), bool_poll_slot_ (slot)
1170{
1171 construct (mode);
1172}
1173
1174PollFDSource::PollFDSource (const VPfdSlot &slot, int fd, const String &mode) :
1175 pfd_ ((PollFD) { fd, 0, 0 }),
1176 never_close_ (strchr (mode.c_str(), 'C') != NULL),
1177 oneshot_ (true), void_poll_slot_ (slot)
1178{
1179 construct (mode);
1180}
1181
1182void
1183PollFDSource::construct (const String &mode)
1184{
1185 add_poll (&pfd_);
1186 pfd_.events |= strchr (mode.c_str(), 'w') ? PollFD::OUT : 0;
1187 pfd_.events |= strchr (mode.c_str(), 'r') ? PollFD::IN : 0;
1188 pfd_.events |= strchr (mode.c_str(), 'p') ? PollFD::PRI : 0;
1189 pfd_.events |= strchr (mode.c_str(), 'd') ? PollFD::WRBAND : 0;
1190 if (pfd_.fd >= 0)
1191 {
1192 const long lflags = fcntl (pfd_.fd, F_GETFL, 0);
1193 long nflags = lflags;
1194 if (strchr (mode.c_str(), 'b'))
1195 nflags &= ~long (O_NONBLOCK);
1196 else if (strchr (mode.c_str(), 'B'))
1197 nflags |= O_NONBLOCK;
1198 if (nflags != lflags)
1199 {
1200 int err;
1201 do
1202 err = fcntl (pfd_.fd, F_SETFL, nflags);
1203 while (err < 0 && (errno == EINTR || errno == EAGAIN));
1204 }
1205 }
1206}
1207
1208bool
1209PollFDSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1210{
1211 pfd_.revents = 0;
1212 return pfd_.fd < 0;
1213}
1214
1215bool
1217{
1218 return pfd_.fd < 0 || pfd_.revents != 0;
1219}
1220
1221bool
1223{
1224 bool keep_alive = !oneshot_;
1225 if (oneshot_ && void_poll_slot_ != NULL)
1226 void_poll_slot_ (pfd_);
1227 else if (!oneshot_ && bool_poll_slot_ != NULL)
1228 keep_alive = bool_poll_slot_ (pfd_);
1229 /* close down */
1230 if (!keep_alive)
1231 {
1232 if (!never_close_ && pfd_.fd >= 0)
1233 close (pfd_.fd);
1234 pfd_.fd = -1;
1235 }
1236 return keep_alive;
1237}
1238
1239void
1240PollFDSource::destroy()
1241{
1242 /* close down */
1243 if (!never_close_ && pfd_.fd >= 0)
1244 close (pfd_.fd);
1245 pfd_.fd = -1;
1246}
1247
1248PollFDSource::~PollFDSource ()
1249{
1250 if (oneshot_)
1251 void_poll_slot_.~VPfdSlot();
1252 else
1253 bool_poll_slot_.~BPfdSlot();
1254}
1255
1256} // Ase
1257
1258// == Loop Description ==
T begin(T... args)
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:926
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:914
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:920
Wakeup facility for IPC.
Definition utils.hh:51
bool opened()
Indicates whether eventfd has been opened.
Definition utils.cc:153
void flush()
Clear pending wakeups.
Definition utils.cc:187
int inputfd()
Returns the file descriptor for POLLIN.
Definition utils.cc:147
void wakeup()
Wakeup polling end.
Definition utils.cc:170
Loop implementation with internal state.
Definition loop.cc:76
void quit(int quit_code) override
Stop the event loop.
Definition loop.cc:421
bool has_quit() override
Check if quit() has been called.
Definition loop.cc:407
void wakeup() override
Wake up the event loop.
Definition loop.cc:373
void cancel(LoopID id) override
Cancel an event source.
Definition loop.cc:223
int run() override
Run the event loop.
Definition loop.cc:386
bool pending() override
Check if iterate() needs to be called for dispatching.
Definition loop.cc:487
LoopID exec_sigchld(int64_t pid, const SigchldSlot &vfunc, LoopPriority priority) override
Execute a signal callback for prepare, check, dispatch.
Definition loop.cc:281
bool set_g_main_context(GlibGMainContext *glib_main_context) override
Set context to integrate with a GLib GMainContext loop.
Definition loop.cc:498
void iterate_pending() override
Iterate pending sources.
Definition loop.cc:470
LoopID add_source(LoopSourceP loop_source, LoopPriority priority) override
Add an event source to the loop.
Definition loop.cc:186
bool exec_once(uint delay_ms, LoopID *once_id, const VoidSlot &vfunc, LoopPriority priority) override
Execute a callback once on SIGCHLD for pid.
Definition loop.cc:248
bool has_primary() override
Indicates whether loop contains primary sources.
Definition loop.cc:170
bool finishable() override
Indicates wether this loop has no primary sources left to process.
Definition loop.cc:437
bool running() override
Indicates if quit() has been called already.
Definition loop.cc:401
void destroy_loop() override
Remove all sources from a loop and prevent any further execution.
Definition loop.cc:359
bool iterate(bool may_block) override
Iterate the main loop once.
Definition loop.cc:453
bool recursion() const
Indicates wether the source is currently in recursion.
Definition loop.cc:847
void add_poll(PollFD *const pfd)
Add a PollFD descriptors for poll(2) and check().
Definition loop.cc:853
bool primary() const
Indicate whether this source is primary.
Definition loop.cc:835
bool may_recurse() const
Indicates if this source may recurse.
Definition loop.cc:829
void loop_remove()
Remove this source from its event loop if any.
Definition loop.cc:890
void remove_poll(PollFD *const pfd)
Remove a previously added PollFD.
Definition loop.cc:867
Loop object, polling for events and executing callbacks in accordance.
Definition loop.hh:77
std::shared_ptr< Promise< uint64_t > > delay(std::chrono::milliseconds ms)
Create a promise that resolves after ms milliseconds and returns the elapsed delay.
Definition loop.cc:315
virtual void cancel(LoopID id)=0
Cancel a source and remove it from the loop.
static LoopP current()
Return the thread-local singleton loop, created on first call.
Definition loop.cc:307
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1222
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1209
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1216
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1061
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1049
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1055
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1126
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1111
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1132
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:978
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:966
static void raise(int8 signum)
Flag a unix signal being raised, this function may be called from any thread at any time.
Definition loop.cc:957
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:972
close
T compare_exchange_strong(T... args)
#define ASE_ASSERT(expr)
Issue an assertion warning if expr evaluates to false.
Definition cxxaux.hh:90
#define ASE_DEFINE_MAKE_SHARED(CLASS)
Define a member function static shared_ptr<CLASS> make_shared(ctorargs...);.
Definition cxxaux.hh:274
#define ASE_UNLIKELY(expr)
Compiler hint to optimize for expr evaluating to false.
Definition cxxaux.hh:45
#define ASE_CLASS_NON_COPYABLE(ClassName)
Delete copy ctor and assignment operator.
Definition cxxaux.hh:110
T empty(T... args)
T end(T... args)
T erase(T... args)
fcntl
T fetch_add(T... args)
free
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:28
#define MIN(a, b)
Yield minimum of a and b.
Definition internal.hh:56
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:72
#define UNLIKELY(cond)
Hint to the compiler to optimize for cond == FALSE.
Definition internal.hh:64
#define ISLIKELY(cond)
Hint to the compiler to optimize for cond == TRUE.
Definition internal.hh:62
typedef int
T max(T... args)
memmove
T min(T... args)
The Anklang C++ API namespace.
Definition api.hh:8
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:24
int16_t int16
A 16-bit signed integer.
Definition cxxaux.hh:26
int8_t int8
An 8-bit signed integer.
Definition cxxaux.hh:25
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:28
LoopPriority
Definition loop.hh:35
@ SYSALLOC
Internal maintenance, don't use.
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:34
bool debug_key_enabled(const char *conditional) noexcept
Check if conditional is enabled by $ASE_DEBUG.
Definition logging.cc:320
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:17
uint64 timestamp_realtime()
Return the current time as uint64 in µseconds.
Definition platform.cc:579
poll
T push_back(T... args)
realloc
T resize(T... args)
sigaction
sigemptyset
typedef int16_t
strchr
strerror
strncat
Thread-safe, lock-free stack based on MpmcStack and an Allocator with allows_read_after_free.
Definition atomics.hh:178
bool pop(Value &value)
Pop value from top of the stack, returns if value was reassigned (true), otherwise the stack was empt...
Definition atomics.hh:197
bool push(Value &&value)
Add value to top of the stack, returns if the stack was empty (true).
Definition atomics.hh:183
uint64 current_time_usecs
Equals timestamp_realtime() as of prepare() and check().
Definition loop.hh:139
Mirrors struct pollfd for poll(3posix)
Definition loop.hh:14
@ RDNORM
reading data will not block
Definition loop.hh:23
@ HUP
file descriptor closed
Definition loop.hh:29
@ IN
RDNORM || RDBAND.
Definition loop.hh:20
@ WRNORM
writing data will not block
Definition loop.hh:25
@ PRI
urgent data available
Definition loop.hh:21
@ NVAL
invalid PollFD
Definition loop.hh:30
@ RDBAND
reading priority data will not block
Definition loop.hh:24
@ OUT
writing data will not block
Definition loop.hh:22
@ ERR
error condition
Definition loop.hh:28
@ WRBAND
writing priority data will not block
Definition loop.hh:26
typedef pid_t