Anklang 0.3.0-460-gc4ef46ba
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 "utils.hh"
4#include "platform.hh"
5#include "internal.hh"
6#include "strings.hh"
7#include <sys/poll.h>
8#include <sys/wait.h>
9#include <errno.h>
10#include <atomic>
11#include <unistd.h>
12#include <fcntl.h>
13#include <sys/time.h>
14#include <unistd.h>
15#include <signal.h>
16#include <algorithm>
17#include <list>
18
19namespace Ase {
20
21enum {
22 WAITING = 0,
23 PREPARED,
24 NEEDS_DISPATCH,
25};
26
27// == PollFD invariants ==
28static_assert (PollFD::IN == POLLIN);
29static_assert (PollFD::PRI == POLLPRI);
30static_assert (PollFD::OUT == POLLOUT);
31static_assert (PollFD::RDNORM == POLLRDNORM);
32static_assert (PollFD::RDBAND == POLLRDBAND);
33static_assert (PollFD::WRNORM == POLLWRNORM);
34static_assert (PollFD::WRBAND == POLLWRBAND);
35static_assert (PollFD::ERR == POLLERR);
36static_assert (PollFD::HUP == POLLHUP);
37static_assert (PollFD::NVAL == POLLNVAL);
38static_assert (sizeof (PollFD) == sizeof (struct pollfd));
39static_assert (offsetof (PollFD, fd) == offsetof (struct pollfd, fd));
40static_assert (sizeof (((PollFD*) 0)->fd) == sizeof (((struct pollfd*) 0)->fd));
41static_assert (offsetof (PollFD, events) == offsetof (struct pollfd, events));
42static_assert (sizeof (((PollFD*) 0)->events) == sizeof (((struct pollfd*) 0)->events));
43static_assert (offsetof (PollFD, revents) == offsetof (struct pollfd, revents));
44static_assert (sizeof (((PollFD*) 0)->revents) == sizeof (((struct pollfd*) 0)->revents));
45
46// === Stupid ID allocator ===
47static volatile int global_id_counter = 65536;
48static uint
49alloc_id ()
50{
51 uint id = __sync_fetch_and_add (&global_id_counter, +1);
52 if (!id)
53 fatal_error ("EventLoop: id counter overflow, please report"); // TODO: use global ID allcoator?
54 return id;
55}
56
57static void
58release_id (uint id)
59{
60 assert_return (id != 0);
61 // TODO: use global ID allcoator?
62}
63
64// === QuickArray ===
65template<class Data>
67 Data *data_;
68 uint n_elements_;
69 uint n_reserved_;
70 Data *reserved_;
71 template<class D> void swap (D &a, D &b) { D t = a; a = b; b = t; }
72public:
73 typedef Data* iterator;
74 QuickArray (uint n_reserved, Data *reserved) : data_ (reserved), n_elements_ (0), n_reserved_ (n_reserved), reserved_ (reserved) {}
75 ~QuickArray() { if (ISLIKELY (data_) && UNLIKELY (data_ != reserved_)) free (data_); }
76 uint size () const { return n_elements_; }
77 uint capacity () const { return std::max (n_elements_, n_reserved_); }
78 bool empty () const { return n_elements_ == 0; }
79 Data* data () const { return data_; }
80 Data& operator[] (uint n) { return data_[n]; }
81 const Data& operator[] (uint n) const { return data_[n]; }
82 iterator begin () { return &data_[0]; }
83 iterator end () { return &data_[n_elements_]; }
84 void shrink (uint n) { n_elements_ = std::min (n_elements_, n); }
85 void swap (QuickArray &o)
86 {
87 swap (data_, o.data_);
88 swap (n_elements_, o.n_elements_);
89 swap (n_reserved_, o.n_reserved_);
90 swap (reserved_, o.reserved_);
91 }
92 void push (const Data &d)
93 {
94 const uint idx = n_elements_;
95 resize (idx + 1);
96 data_[idx] = d;
97 }
98 void resize (uint n)
99 {
100 if (n <= capacity())
101 {
102 n_elements_ = n;
103 return;
104 }
105 // n > n_reserved_ && n > n_elements_
106 const size_t sz = n * sizeof (Data);
107 const bool migrate_reserved = UNLIKELY (data_ == reserved_); // migrate from reserved to malloced
108 Data *mem = (Data*) (migrate_reserved ? malloc (sz) : realloc (data_, sz));
109 if (UNLIKELY (!mem))
110 fatal_error ("OOM");
111 if (migrate_reserved)
112 {
113 memcpy (mem, data_, n_elements_ * sizeof (Data));
114 reserved_ = NULL;
115 n_reserved_ = 0;
116 }
117 data_ = mem;
118 n_elements_ = n;
119 }
120};
121struct EventLoop::QuickPfdArray : public QuickArray<PollFD> {
122 QuickPfdArray (uint n_reserved, PollFD *reserved) : QuickArray (n_reserved, reserved) {}
123};
124struct QuickSourcePArray : public QuickArray<EventSourceP*> {
125 QuickSourcePArray (uint n_reserved, EventSourceP **reserved) : QuickArray (n_reserved, reserved) {}
126};
127
128// === EventLoop ===
129EventLoop::EventLoop (MainLoop &main) :
130 main_loop_ (&main), dispatch_priority_ (0), primary_ (false)
131{
132 poll_sources_.reserve (7);
133 // we cannot *use* main_loop_ yet, because we might be called from within MainLoop::MainLoop(), see SubLoop()
134 assert_return (main_loop_ && main_loop_->main_loop_); // sanity checks
135}
136
137EventLoop::~EventLoop ()
138{
139 unpoll_sources_U();
140 // we cannot *use* main_loop_ anymore, because we might be called from within MainLoop::MainLoop(), see ~SubLoop()
141}
142
143inline EventSourceP&
144EventLoop::find_first_L()
145{
146 static EventSourceP null_source;
147 return sources_.empty() ? null_source : sources_[0];
148}
149
150inline EventSourceP&
151EventLoop::find_source_L (uint id)
152{
153 for (SourceList::iterator lit = sources_.begin(); lit != sources_.end(); lit++)
154 if (id == (*lit)->id_)
155 return *lit;
156 static EventSourceP null_source;
157 return null_source;
158}
159
160bool
161EventLoop::has_primary_L()
162{
163 if (primary_)
164 return true;
165 for (SourceList::iterator lit = sources_.begin(); lit != sources_.end(); lit++)
166 if ((*lit)->primary())
167 return true;
168 return false;
169}
170
171bool
173{
174 std::lock_guard<std::mutex> locker (main_loop_->mutex());
175 return has_primary_L();
176}
177
178bool
179EventLoop::flag_primary (bool on)
180{
181 std::lock_guard<std::mutex> locker (main_loop_->mutex());
182 const bool was_primary = primary_;
183 primary_ = on;
184 if (primary_ != was_primary)
185 wakeup();
186 return was_primary;
187}
188
189MainLoop*
191{
192 return main_loop_;
193}
194
195static const int16 UNDEFINED_PRIORITY = -32768;
196
197uint
198EventLoop::add (EventSourceP source, int priority)
199{
200 static_assert (UNDEFINED_PRIORITY < 1, "");
201 assert_return (priority >= 1 && priority <= PRIORITY_CEILING, 0);
202 assert_return (source != NULL, 0);
203 assert_return (source->loop_ == NULL, 0);
204 source->loop_ = this;
205 source->id_ = alloc_id();
206 source->loop_state_ = WAITING;
207 source->priority_ = priority;
208 {
209 std::lock_guard<std::mutex> locker (main_loop_->mutex());
210 sources_.push_back (source);
211 }
212 wakeup();
213 return source->id_;
214}
215
216void
217EventLoop::remove_source_Lm (EventSourceP source)
218{
219 std::mutex &LOCK = main_loop_->mutex();
220 assert_return (source->loop_ == this);
221 source->loop_ = NULL;
222 source->loop_state_ = WAITING;
223 auto pos = find (sources_.begin(), sources_.end(), source);
224 assert_return (pos != sources_.end());
225 sources_.erase (pos);
226 release_id (source->id_);
227 source->id_ = 0;
228 LOCK.unlock();
229 source->destroy();
230 LOCK.lock();
231}
232
233bool
235{
236 {
237 std::lock_guard<std::mutex> locker (main_loop_->mutex());
238 EventSourceP &source = find_source_L (id);
239 if (!source)
240 return false;
241 remove_source_Lm (source);
242 }
243 wakeup();
244 return true;
245}
246
247bool
249{
250 return_unless (id_pointer, false);
251 const bool removal = try_remove (*id_pointer);
252 *id_pointer = 0;
253 return removal;
254}
255
256void
258{
259 if (!try_remove (id))
260 warning ("%s: failed to remove loop source: %u", __func__, id);
261}
262
263bool
264EventLoop::exec_once (uint delay_ms, uint *once_id, const VoidSlot &vfunc, int priority)
265{
266 assert_return (once_id != nullptr, false);
267 assert_return (priority >= 1 && priority <= PRIORITY_CEILING, false);
268 if (!vfunc) {
269 clear_source (once_id);
270 return false;
271 }
272 auto once_handler = [vfunc,once_id]() { *once_id = 0; vfunc(); };
273 EventSourceP source = TimedSource::create (once_handler, delay_ms, 0);
274 source->loop_ = this;
275 source->id_ = alloc_id();
276 source->loop_state_ = WAITING;
277 source->priority_ = priority;
278 uint warn_id = 0;
279 {
280 std::lock_guard<std::mutex> locker (main_loop_->mutex());
281 if (*once_id) {
282 EventSourceP &source = find_source_L (*once_id);
283 if (source)
284 remove_source_Lm (source);
285 else
286 warn_id = *once_id;
287 }
288 sources_.push_back (source);
289 *once_id = source->id_;
290 }
291 if (warn_id)
292 warning ("%s: failed to remove loop source: %u", __func__, once_id);
293 wakeup();
294 return true;
295}
296
297/* void EventLoop::change_priority (EventSource *source, int priority) {
298 * // ensure that source belongs to this
299 * // reset all source->pfds[].idx = UINT_MAX
300 * // unlink source
301 * // poke priority
302 * // re-add source
303 */
304
305void
306EventLoop::kill_sources_Lm()
307{
308 for (;;)
309 {
310 EventSourceP &source = find_first_L();
311 if (source == NULL)
312 break;
313 remove_source_Lm (source);
314 }
315 std::mutex &LOCK = main_loop_->mutex();
316 LOCK.unlock();
317 unpoll_sources_U(); // unlocked
318 LOCK.lock();
319}
320
331void
333{
334 assert_return (main_loop_ != NULL);
335 // guard main_loop_ pointer *before* locking, so dtor is called after unlock
336 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop*> (main_loop_);
337 std::lock_guard<std::mutex> locker (main_loop_->mutex());
338 if (this != main_loop_)
339 main_loop_->kill_loop_Lm (*this);
340 else
341 main_loop_->kill_loops_Lm();
342 assert_return (main_loop_ == NULL);
343}
344
345void
347{
348 // this needs to work unlocked
349 main_loop_->wakeup_poll();
350}
351
352// === MainLoop ===
353MainLoop::MainLoop() :
354 EventLoop (*this), // sets *this as MainLoop on self
355 rr_index_ (0), running_ (false), has_quit_ (false), quit_code_ (0), gcontext_ (NULL)
356{
357 std::lock_guard<std::mutex> locker (main_loop_->mutex());
358 const int err = eventfd_.open();
359 if (err < 0)
360 fatal_error ("MainLoop: failed to create wakeup pipe: %s", strerror (-err));
361 // has_quit_ and eventfd_ need to be setup here, so calling quit() before run() works
362}
363
368MainLoopP
370{
371 MainLoopP main_loop = make_shared();
373 main_loop->add_loop_L (*main_loop);
374 return main_loop;
375}
376
377MainLoop::~MainLoop()
378{
379 set_g_main_context (NULL); // acquires mutex_
380 std::lock_guard<std::mutex> locker (mutex_);
381 if (main_loop_)
382 kill_loops_Lm();
383 assert_return (loops_.empty() == true);
384}
385
386void
387MainLoop::wakeup_poll()
388{
389 if (eventfd_.opened())
390 eventfd_.wakeup();
391}
392
393void
394MainLoop::add_loop_L (EventLoop &loop)
395{
396 assert_return (this == loop.main_loop_);
397 loops_.push_back (shared_ptr_cast<EventLoop> (&loop));
398 wakeup_poll();
399}
400
401void
402MainLoop::kill_loop_Lm (EventLoop &loop)
403{
404 assert_return (this == loop.main_loop_);
405 loop.kill_sources_Lm();
406 if (loop.main_loop_) // guard against nested kill_loop_Lm (same) calls
407 {
408 loop.main_loop_ = NULL;
409 std::vector<EventLoopP>::iterator it = std::find_if (loops_.begin(), loops_.end(),
410 [&loop] (EventLoopP &lp) { return lp.get() == &loop; });
411 if (this == &loop) // MainLoop->destroy_loop()
412 {
413 if (loops_.size()) // MainLoop must be the last loop to be destroyed
414 assert_return (loops_[0].get() == this);
415 }
416 else
417 assert_return (it != loops_.end());
418 if (it != loops_.end())
419 loops_.erase (it);
420 wakeup_poll();
421 }
422}
423
424void
425MainLoop::kill_loops_Lm()
426{
427 while (loops_.size() > 1 || loops_[0].get() != this)
428 {
429 EventLoopP loop = loops_[0].get() != this ? loops_[0] : loops_[loops_.size() - 1];
430 kill_loop_Lm (*loop);
431 }
432 kill_loop_Lm (*this);
433}
434
435int
437{
438 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
439 std::lock_guard<std::mutex> locker (mutex_);
440 LoopState state;
441 running_ = !has_quit_;
442 while (ISLIKELY (running_))
443 iterate_loops_Lm (state, true, true);
444 const int last_quit_code = quit_code_;
445 running_ = false;
446 has_quit_ = false; // allow loop resumption
447 quit_code_ = 0; // allow loop resumption
448 return last_quit_code;
449}
450
451bool
453{
454 std::lock_guard<std::mutex> locker (mutex_);
455 return running_;
456}
457
458void
459MainLoop::quit (int quit_code)
460{
461 std::lock_guard<std::mutex> locker (mutex_);
462 quit_code_ = quit_code;
463 has_quit_ = true; // cancel run() upfront, or
464 running_ = false; // interrupt current iteration
465 wakeup();
466}
467
468bool
469MainLoop::finishable_L()
470{
471 // loop list shouldn't be modified by querying finishable state
472 bool found_primary = primary_;
473 for (size_t i = 0; !found_primary && i < loops_.size(); i++)
474 if (loops_[i]->has_primary_L())
475 found_primary = true;
476 return !found_primary; // finishable if no primary sources remain
477}
478
479bool
481{
482 std::lock_guard<std::mutex> locker (mutex_);
483 return finishable_L();
484}
485
496bool
497MainLoop::iterate (bool may_block)
498{
499 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
500 std::lock_guard<std::mutex> locker (mutex_);
501 LoopState state;
502 const bool was_running = running_; // guard for recursion
503 running_ = true;
504 const bool sources_pending = iterate_loops_Lm (state, may_block, true);
505 running_ = was_running && !has_quit_;
506 return sources_pending;
507}
508
509void
511{
512 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
513 std::lock_guard<std::mutex> locker (mutex_);
514 LoopState state;
515 const bool was_running = running_; // guard for recursion
516 running_ = true;
517 while (ISLIKELY (running_))
518 if (!iterate_loops_Lm (state, false, true))
519 break;
520 running_ = was_running && !has_quit_;
521}
522
523bool
525{
526 std::lock_guard<std::mutex> locker (mutex_);
527 LoopState state;
528 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
529 return iterate_loops_Lm (state, false, false);
530}
531
532bool
533MainLoop::set_g_main_context (GlibGMainContext *glib_main_context)
534{
535#ifdef __G_LIB_H__
536 std::lock_guard<std::mutex> locker (mutex_);
537 if (glib_main_context)
538 {
539 if (gcontext_)
540 return false;
541 if (!g_main_context_acquire (glib_main_context))
542 return false;
543 gcontext_ = g_main_context_ref (glib_main_context);
544 }
545 else if (gcontext_)
546 {
547 glib_main_context = gcontext_;
548 gcontext_ = NULL;
549 g_main_context_release (glib_main_context);
550 g_main_context_unref (glib_main_context);
551 }
552 return true;
553#else
554 return false;
555#endif
556}
557
558#ifdef __G_LIB_H__
559static GPollFD*
560mk_gpollfd (PollFD *pfd)
561{
562 GPollFD *gpfd = (GPollFD*) pfd;
563 static_assert (sizeof (GPollFD) == sizeof (PollFD), "");
564 static_assert (sizeof (gpfd->fd) == sizeof (pfd->fd), "");
565 static_assert (sizeof (gpfd->events) == sizeof (pfd->events), "");
566 static_assert (sizeof (gpfd->revents) == sizeof (pfd->revents), "");
567 static_assert (offsetof (GPollFD, fd) == offsetof (PollFD, fd), "");
568 static_assert (offsetof (GPollFD, events) == offsetof (PollFD, events), "");
569 static_assert (offsetof (GPollFD, revents) == offsetof (PollFD, revents), "");
570 static_assert (PollFD::IN == int (G_IO_IN), "");
571 static_assert (PollFD::PRI == int (G_IO_PRI), "");
572 static_assert (PollFD::OUT == int (G_IO_OUT), "");
573 // static_assert (PollFD::RDNORM == int (G_IO_RDNORM), "");
574 // static_assert (PollFD::RDBAND == int (G_IO_RDBAND), "");
575 // static_assert (PollFD::WRNORM == int (G_IO_WRNORM), "");
576 // static_assert (PollFD::WRBAND == int (G_IO_WRBAND), "");
577 static_assert (PollFD::ERR == int (G_IO_ERR), "");
578 static_assert (PollFD::HUP == int (G_IO_HUP), "");
579 static_assert (PollFD::NVAL == int (G_IO_NVAL), "");
580 return gpfd;
581}
582#endif
583
584void
585EventLoop::unpoll_sources_U() // must be unlocked!
586{
587 // clear poll sources
588 poll_sources_.resize (0);
589}
590
591void
592EventLoop::collect_sources_Lm (LoopState &state)
593{
594 // enforce clean slate
595 if (UNLIKELY (!poll_sources_.empty()))
596 {
597 std::mutex &LOCK = main_loop_->mutex();
598 LOCK.unlock();
599 unpoll_sources_U(); // unlocked
600 LOCK.lock();
601 assert_return (poll_sources_.empty());
602 }
603 if (UNLIKELY (!state.seen_primary && primary_))
604 state.seen_primary = true;
605 EventSourceP* arraymem[7]; // using a vector+malloc here shows up in the profiles
606 QuickSourcePArray poll_candidates (ARRAY_SIZE (arraymem), arraymem);
607 // determine dispatch priority & collect sources for preparing
608 dispatch_priority_ = UNDEFINED_PRIORITY; // initially, consider sources at *all* priorities
609 for (SourceList::iterator lit = sources_.begin(); lit != sources_.end(); lit++)
610 {
611 EventSource &source = **lit;
612 if (UNLIKELY (!state.seen_primary && source.primary_))
613 state.seen_primary = true;
614 if (source.loop_ != this || // ignore destroyed and
615 (source.dispatching_ && !source.may_recurse_)) // avoid unallowed recursion
616 continue;
617 if (source.priority_ > dispatch_priority_ && // ignore lower priority sources
618 source.loop_state_ == NEEDS_DISPATCH) // if NEEDS_DISPATCH sources remain
619 dispatch_priority_ = source.priority_; // so raise dispatch_priority_
620 if (source.priority_ > dispatch_priority_ || // add source if it is an eligible
621 (source.priority_ == dispatch_priority_ && // candidate, baring future raises
622 source.loop_state_ == NEEDS_DISPATCH)) // of dispatch_priority_...
623 poll_candidates.push (&*lit); // collect only, adding ref() later
624 }
625 // ensure ref counts on all prepare sources
626 assert_return (poll_sources_.empty());
627 for (size_t i = 0; i < poll_candidates.size(); i++)
628 if ((*poll_candidates[i])->priority_ > dispatch_priority_ || // throw away lower priority sources
629 ((*poll_candidates[i])->priority_ == dispatch_priority_ &&
630 (*poll_candidates[i])->loop_state_ == NEEDS_DISPATCH)) // re-poll sources that need dispatching
631 poll_sources_.push_back (*poll_candidates[i]);
632 /* here, poll_sources_ contains either all sources, or only the highest priority
633 * NEEDS_DISPATCH sources plus higher priority sources. giving precedence to the
634 * remaining NEEDS_DISPATCH sources ensures round-robin processing.
635 */
636}
637
638bool
639EventLoop::prepare_sources_Lm (LoopState &state, QuickPfdArray &pfda)
640{
641 std::mutex &LOCK = main_loop_->mutex();
642 // prepare sources, up to NEEDS_DISPATCH priority
643 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
644 {
645 EventSource &source = **lit;
646 if (source.loop_ != this) // test undestroyed
647 continue;
648 int64 timeout = -1;
649 LOCK.unlock();
650 const bool need_dispatch = source.prepare (state, &timeout);
651 LOCK.lock();
652 if (source.loop_ != this)
653 continue; // ignore newly destroyed sources
654 if (need_dispatch)
655 {
656 dispatch_priority_ = std::max (dispatch_priority_, source.priority_); // upgrade dispatch priority
657 source.loop_state_ = NEEDS_DISPATCH;
658 continue;
659 }
660 source.loop_state_ = PREPARED;
661 if (timeout >= 0)
662 state.timeout_usecs = std::min (state.timeout_usecs, timeout);
663 uint npfds = source.n_pfds();
664 for (uint i = 0; i < npfds; i++)
665 if (source.pfds_[i].pfd->fd >= 0)
666 {
667 uint idx = pfda.size();
668 source.pfds_[i].idx = idx;
669 pfda.push (*source.pfds_[i].pfd);
670 pfda[idx].revents = 0;
671 }
672 else
673 source.pfds_[i].idx = 4294967295U; // UINT_MAX
674 }
675 return dispatch_priority_ > UNDEFINED_PRIORITY;
676}
677
678bool
679EventLoop::check_sources_Lm (LoopState &state, const QuickPfdArray &pfda)
680{
681 std::mutex &LOCK = main_loop_->mutex();
682 // check polled sources
683 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
684 {
685 EventSource &source = **lit;
686 if (source.loop_ != this && // test undestroyed
687 source.loop_state_ != PREPARED)
688 continue; // only check prepared sources
689 uint npfds = source.n_pfds();
690 for (uint i = 0; i < npfds; i++)
691 {
692 uint idx = source.pfds_[i].idx;
693 if (idx < pfda.size() &&
694 source.pfds_[i].pfd->fd == pfda[idx].fd)
695 source.pfds_[i].pfd->revents = pfda[idx].revents;
696 else
697 source.pfds_[i].idx = 4294967295U; // UINT_MAX
698 }
699 LOCK.unlock();
700 bool need_dispatch = source.check (state);
701 LOCK.lock();
702 if (source.loop_ != this)
703 continue; // ignore newly destroyed sources
704 if (need_dispatch)
705 {
706 dispatch_priority_ = std::max (dispatch_priority_, source.priority_); // upgrade dispatch priority
707 source.loop_state_ = NEEDS_DISPATCH;
708 }
709 else
710 source.loop_state_ = WAITING;
711 }
712 return dispatch_priority_ > UNDEFINED_PRIORITY;
713}
714
715void
716EventLoop::dispatch_source_Lm (LoopState &state)
717{
718 std::mutex &LOCK = main_loop_->mutex();
719 // find a source to dispatch at dispatch_priority_
720 EventSourceP dispatch_source = NULL; // shared_ptr to keep alive even if everything else is destroyed
721 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
722 {
723 EventSourceP &source = *lit;
724 if (source->loop_ == this && // test undestroyed
725 source->priority_ == dispatch_priority_ && // only dispatch at dispatch priority
726 source->loop_state_ == NEEDS_DISPATCH)
727 {
728 dispatch_source = source;
729 break;
730 }
731 }
732 dispatch_priority_ = UNDEFINED_PRIORITY;
733 // dispatch single source
734 if (dispatch_source)
735 {
736 dispatch_source->loop_state_ = WAITING;
737 const bool old_was_dispatching = dispatch_source->was_dispatching_;
738 dispatch_source->was_dispatching_ = dispatch_source->dispatching_;
739 dispatch_source->dispatching_ = true;
740 LOCK.unlock();
741 const bool keep_alive = dispatch_source->dispatch (state);
742 LOCK.lock();
743 dispatch_source->dispatching_ = dispatch_source->was_dispatching_;
744 dispatch_source->was_dispatching_ = old_was_dispatching;
745 if (dispatch_source->loop_ == this && !keep_alive)
746 remove_source_Lm (dispatch_source);
747 }
748}
749
750bool
751MainLoop::iterate_loops_Lm (LoopState &state, bool may_block, bool may_dispatch)
752{
753 assert_return (state.phase == state.NONE, false);
754 std::mutex &LOCK = main_loop_->mutex();
755 PollFD reserved_pfd_mem[7]; // store PollFD array in stack memory, to reduce malloc overhead
756 QuickPfdArray pfda (ARRAY_SIZE (reserved_pfd_mem), reserved_pfd_mem); // pfda.size() == 0
757 // allow poll wakeups
758 const PollFD wakeup = { eventfd_.inputfd(), PollFD::IN, 0 };
759 const uint wakeup_idx = 0; // wakeup_idx = pfda.size();
760 pfda.push (wakeup);
761 // create pollable loop list
762 const size_t nrloops = loops_.size(); // number of Ase loops, *without* gcontext_
763 EventLoopP loops[nrloops];
764 for (size_t i = 0; i < nrloops; i++)
765 loops[i] = loops_[i];
766 // collect
767 state.phase = state.COLLECT;
768 state.seen_primary = false;
769 for (size_t i = 0; i < nrloops; i++)
770 loops[i]->collect_sources_Lm (state);
771 // prepare
772 bool any_dispatchable = false;
773 state.phase = state.PREPARE;
774 state.timeout_usecs = INT64_MAX;
775 state.current_time_usecs = timestamp_realtime();
776 bool dispatchable[nrloops + 1]; // +1 for gcontext_
777 for (size_t i = 0; i < nrloops; i++)
778 {
779 dispatchable[i] = loops[i]->prepare_sources_Lm (state, pfda);
780 any_dispatchable |= dispatchable[i];
781 }
782 // prepare GLib
783 ASE_UNUSED const int gfirstfd = pfda.size();
784 ASE_UNUSED int gpriority = INT32_MIN;
785 if (ASE_UNLIKELY (gcontext_))
786 {
787#ifdef __G_LIB_H__
788 dispatchable[nrloops] = g_main_context_prepare (gcontext_, &gpriority) != 0;
789 any_dispatchable |= dispatchable[nrloops];
790 int gtimeout = INT32_MAX;
791 pfda.resize (pfda.capacity());
792 int gnfds = g_main_context_query (gcontext_, gpriority, &gtimeout, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
793 while (gnfds >= 0 && size_t (gnfds) != pfda.size() - gfirstfd)
794 {
795 pfda.resize (gfirstfd + gnfds);
796 gtimeout = INT32_MAX;
797 gnfds = g_main_context_query (gcontext_, gpriority, &gtimeout, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
798 }
799 if (gtimeout >= 0)
800 state.timeout_usecs = MIN (state.timeout_usecs, gtimeout * int64 (1000));
801#endif
802 }
803 else
804 dispatchable[nrloops] = false;
805 // poll file descriptors
806 int64 timeout_msecs = state.timeout_usecs / 1000;
807 if (state.timeout_usecs > 0 && timeout_msecs <= 0)
808 timeout_msecs = 1;
809 if (!may_block || any_dispatchable)
810 timeout_msecs = 0;
811 state.timeout_usecs = 0;
812 LOCK.unlock();
813 int presult;
814 do
815 presult = poll ((struct pollfd*) &pfda[0], pfda.size(), std::min (timeout_msecs, int64 (2147483647))); // INT_MAX
816 while (presult < 0 && errno == EAGAIN); // EINTR may indicate a signal
817 LOCK.lock();
818 if (presult < 0 && errno != EINTR)
819 warning ("MainLoop: poll() failed: %s", strerror());
820 else if (pfda[wakeup_idx].revents)
821 eventfd_.flush(); // restart queueing wakeups, possibly triggered by dispatching
822 // check
823 state.phase = state.CHECK;
824 state.current_time_usecs = timestamp_realtime();
825 int16 max_dispatch_priority = -32768;
826 for (size_t i = 0; i < nrloops; i++)
827 {
828 dispatchable[i] |= loops[i]->check_sources_Lm (state, pfda);
829 if (!dispatchable[i])
830 continue;
831 any_dispatchable = true;
832 max_dispatch_priority = std::max (max_dispatch_priority, loops[i]->dispatch_priority_);
833 }
834 bool priority_ascension = false; // flag for priority elevation between loops
835 if (UNLIKELY (max_dispatch_priority >= PRIORITY_ASCENT) && any_dispatchable)
836 priority_ascension = true;
837 // check GLib
838 if (ASE_UNLIKELY (gcontext_))
839 {
840#ifdef __G_LIB_H__
841 dispatchable[nrloops] = g_main_context_check (gcontext_, gpriority, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
842 any_dispatchable |= dispatchable[nrloops];
843#endif
844 }
845 // dispatch
846 if (may_dispatch && any_dispatchable)
847 {
848 const size_t gloop = ASE_UNLIKELY (gcontext_) && dispatchable[nrloops] ? 1 : 0;
849 const size_t maxloops = nrloops + gloop; // +1 for gcontext_
850 size_t index;
851 while (true) // pick loops in round-robin fashion
852 {
853 index = rr_index_++ % maxloops; // round-robin step
854 if (!dispatchable[index])
855 continue; // need to find next dispatchable
856 if (index < nrloops && loops[index]->dispatch_priority_ < max_dispatch_priority)
857 continue; // ignore loops without max-priority source, except for gcontext_ which can still benefit from round-robin
858 if (priority_ascension && index >= nrloops)
859 continue; // but there's no priority_ascension support for gcontext_
860 break; // DONE: index points to a dispatchable loop which meets priority requirements
861 }
862 state.phase = state.DISPATCH;
863 if (index >= nrloops)
864 {
865#ifdef __G_LIB_H__
866 g_main_context_dispatch (gcontext_);
867#endif
868 }
869 else
870 loops[index]->dispatch_source_Lm (state); // passes on shared_ptr to keep alive while locked
871 }
872 // cleanup
873 state.phase = state.NONE;
874 LOCK.unlock();
875 for (size_t i = 0; i < nrloops; i++)
876 {
877 loops[i]->unpoll_sources_U(); // unlocked
878 loops[i] = NULL; // dtor, unlocked
879 }
880 LOCK.lock();
881 return any_dispatchable; // need to dispatch or recheck
882}
883
884class SubLoop : public EventLoop {
885public:
886 SubLoop (MainLoopP main) :
887 EventLoop (*main)
888 {}
889 ~SubLoop()
890 {
891 assert_return (main_loop_ == NULL);
892 }
893};
894
897{
899 EventLoopP sub_loop = std::make_shared<SubLoop> (shared_ptr_cast<MainLoop> (this));
900 this->add_loop_L (*sub_loop);
901 return sub_loop;
902}
903
904// === EventSource ===
905EventSource::EventSource () :
906 loop_ (NULL),
907 pfds_ (NULL),
908 id_ (0),
909 priority_ (UNDEFINED_PRIORITY),
910 loop_state_ (0),
911 may_recurse_ (0),
912 dispatching_ (0),
913 was_dispatching_ (0),
914 primary_ (0)
915{}
916
917uint
918EventSource::n_pfds ()
919{
920 uint i = 0;
921 if (pfds_)
922 while (pfds_[i].pfd)
923 i++;
924 return i;
925}
926
927void
928EventSource::may_recurse (bool may_recurse)
929{
930 may_recurse_ = may_recurse;
931}
932
933bool
935{
936 return may_recurse_;
937}
938
939bool
941{
942 return primary_;
943}
944
945void
946EventSource::primary (bool is_primary)
947{
948 primary_ = is_primary;
949}
950
951bool
953{
954 return dispatching_ && was_dispatching_;
955}
956
957void
959{
960 const uint idx = n_pfds();
961 uint npfds = idx + 1;
962 pfds_ = (typeof (pfds_)) realloc (pfds_, sizeof (pfds_[0]) * (npfds + 1));
963 if (!pfds_)
964 fatal_error ("EventSource: out of memory");
965 pfds_[npfds].idx = 4294967295U; // UINT_MAX
966 pfds_[npfds].pfd = NULL;
967 pfds_[idx].idx = 4294967295U; // UINT_MAX
968 pfds_[idx].pfd = pfd;
969}
970
971void
973{
974 uint idx, npfds = n_pfds();
975 for (idx = 0; idx < npfds; idx++)
976 if (pfds_[idx].pfd == pfd)
977 break;
978 if (idx < npfds)
979 {
980 pfds_[idx].idx = 4294967295U; // UINT_MAX
981 pfds_[idx].pfd = pfds_[npfds - 1].pfd;
982 pfds_[idx].idx = pfds_[npfds - 1].idx;
983 pfds_[npfds - 1].idx = 4294967295U; // UINT_MAX
984 pfds_[npfds - 1].pfd = NULL;
985 }
986 else
987 warning ("EventSource: unremovable PollFD: %p (fd=%d)", pfd, pfd->fd);
988}
989
990void
991EventSource::destroy ()
992{}
993
994void
996{
997 if (loop_)
998 loop_->try_remove (source_id());
999}
1000
1001EventSource::~EventSource ()
1002{
1003 assert_return (loop_ == NULL);
1004 if (pfds_)
1005 free (pfds_);
1006}
1007
1008// == DispatcherSource ==
1009DispatcherSource::DispatcherSource (const DispatcherSlot &slot) :
1010 slot_ (slot)
1011{}
1012
1013DispatcherSource::~DispatcherSource ()
1014{
1015 slot_ = NULL;
1016}
1017
1018bool
1019DispatcherSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1020{
1021 return slot_ (state);
1022}
1023
1024bool
1026{
1027 return slot_ (state);
1028}
1029
1030bool
1032{
1033 return slot_ (state);
1034}
1035
1036void
1037DispatcherSource::destroy()
1038{
1039 LoopState state;
1040 state.phase = state.DESTROY;
1041 slot_ (state);
1042}
1043
1044// == USignalSource ==
1045USignalSource::USignalSource (int8 signum, const USignalSlot &slot) :
1046 slot_ (slot), signum_ (signum)
1047{
1048 const uint s = 128 + signum_;
1049 index_ = s / 32;
1050 shift_ = s % 32;
1051}
1052
1053USignalSource::~USignalSource ()
1054{
1055 slot_ = NULL;
1056}
1057
1058static std::atomic<uint32> usignals_notified[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
1059
1061void
1063{
1064 const uint s = 128 + signum;
1065 const uint index = s / 32;
1066 const uint shift = s % 32;
1067 usignals_notified[index] |= 1 << shift;
1068}
1069
1070bool
1071USignalSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1072{
1073 return usignals_notified[index_] & (1 << shift_);
1074}
1075
1076bool
1078{
1079 return usignals_notified[index_] & (1 << shift_);
1080}
1081
1082bool
1084{
1085 usignals_notified[index_] &= ~(1 << shift_);
1086 return slot_ (signum_);
1087}
1088
1089void
1090USignalSource::destroy()
1091{}
1092
1094write_uint (uint32_t i)
1095{
1097 char *c = &a.back();
1098 ASE_ASSERT (c>=&a[0] && c<&a[a.size()]);
1099 *c-- = 0;
1100 *c = '0' + (i % 10);
1101 i /= 10;
1102 while (i != 0) {
1103 *(--c) = '0' + (i % 10);
1104 i /= 10;
1105 }
1106 if (c > &a[0])
1107 memmove (&a[0], c, &a.back() + 1 - c);
1108 ASE_ASSERT (c>=&a[0] && c<&a[a.size()]);
1109 return a;
1110}
1111
1112void
1113USignalSource::install_sigaction (int8 signum)
1114{
1115 struct sigaction action;
1116 action.sa_handler = [] (int signum) {
1117 if (0) { // DEBUG
1118 constexpr size_t N = 1024;
1119 char buf[N] = __FILE__ ":";
1120 strncat (buf, &write_uint (__LINE__)[0], N);
1121 strncat (buf, ": sa_handler: signal=", N);
1122 strncat (buf, &write_uint (signum)[0], N);
1123 strncat (buf, "\n", N);
1124 ::write (2, buf, strlen (buf));
1125 }
1126 USignalSource::raise (signum);
1127 };
1128 sigemptyset (&action.sa_mask);
1129 action.sa_flags = SA_NOMASK;
1130 sigaction (signum, &action, nullptr);
1131}
1132
1133// === SigchldSource ===
1134static std::atomic<uint64_t> sigchld_counter = 0;
1135
1136SigchldSource::SigchldSource (int64_t pid, const SigchldSlot &slot) :
1137 slot_ (slot), pid_ (pid)
1138{
1139 if (uint64_t unused = 0; sigchld_counter.compare_exchange_strong (unused, 1)) {
1140 struct sigaction action;
1141 action.sa_handler = [] (int signum) {
1142 sigchld_counter++;
1143 };
1144 sigemptyset (&action.sa_mask);
1145 action.sa_flags = SA_NOMASK;
1146 sigaction (SIGCHLD, &action, nullptr);
1147 }
1148}
1149
1150SigchldSource::~SigchldSource()
1151{}
1152
1153bool
1154SigchldSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1155{
1156 return pid_ && sigchld_counter_ != sigchld_counter;
1157}
1158
1159bool
1161{
1162 return pid_ && sigchld_counter_ != sigchld_counter;
1163}
1164
1165bool
1167{
1168 if (pid_) {
1169 sigchld_counter_ = sigchld_counter;
1170 // Use pid_ to avoid reaping unknown children
1171 int status = 0;
1172 const pid_t child_pid = wait4 (pid_, &status, WNOHANG, nullptr);
1173 if (child_pid > 0) {
1174 slot_ (pid_, status);
1175#if 0
1176 struct rusage ru {}; // wait4 (..., &ru);
1177 printf (" Child Pid %d user time: %ld.%06ld sec\n", child_pid, ru.ru_utime.tv_sec, ru.ru_utime.tv_usec);
1178 printf (" System time: %ld.%06ld sec\n", ru.ru_stime.tv_sec, ru.ru_stime.tv_usec);
1179 printf (" Max RSS: %ld KB\n", ru.ru_maxrss);
1180 printf (" Page faults: %ld\n", ru.ru_minflt);
1181 printf (" I/O operations: %ld\n", ru.ru_inblock + ru.ru_oublock);
1182 printf (" Voluntary context switches: %ld\n", ru.ru_nvcsw);
1183 printf (" Involuntary context switches: %ld\n", ru.ru_nivcsw);
1184 printf ("\n");
1185#endif
1186 if (WIFEXITED (status) || WIFSIGNALED (status)) {
1187 // Child exited
1188 pid_ = 0;
1189 return false; // destroy
1190 }
1191 }
1192 }
1193 return true; // keep_alive
1194}
1195
1196void
1197SigchldSource::destroy ()
1198{
1199 pid_ = 0;
1200}
1201
1202// == TimedSource ==
1203TimedSource::TimedSource (const VoidSlot &slot, uint initial_interval_msecs, uint repeat_interval_msecs) :
1204 expiration_usecs_ (timestamp_realtime() + 1000ULL * initial_interval_msecs),
1205 interval_msecs_ (repeat_interval_msecs), first_interval_ (true),
1206 oneshot_ (true), void_slot_ (slot)
1207{}
1208
1209TimedSource::TimedSource (const BoolSlot &slot, uint initial_interval_msecs, uint repeat_interval_msecs) :
1210 expiration_usecs_ (timestamp_realtime() + 1000ULL * initial_interval_msecs),
1211 interval_msecs_ (repeat_interval_msecs), first_interval_ (true),
1212 oneshot_ (false), bool_slot_ (slot)
1213{}
1214
1215bool
1216TimedSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1217{
1218 if (state.current_time_usecs >= expiration_usecs_)
1219 return true; /* timeout expired */
1220 if (!first_interval_)
1221 {
1222 uint64 interval = interval_msecs_ * 1000ULL;
1223 if (state.current_time_usecs + interval < expiration_usecs_)
1224 expiration_usecs_ = state.current_time_usecs + interval; /* clock warped back in time */
1225 }
1226 *timeout_usecs_p = std::min (expiration_usecs_ - state.current_time_usecs, uint64 (2147483647)); // INT_MAX
1227 return 0 == *timeout_usecs_p;
1228}
1229
1230bool
1232{
1233 return state.current_time_usecs >= expiration_usecs_;
1234}
1235
1236bool
1238{
1239 bool repeat = false;
1240 first_interval_ = false;
1241 if (oneshot_ && void_slot_ != NULL)
1242 void_slot_ ();
1243 else if (!oneshot_ && bool_slot_ != NULL)
1244 repeat = bool_slot_ ();
1245 if (repeat)
1246 expiration_usecs_ = timestamp_realtime() + 1000ULL * interval_msecs_;
1247 return repeat;
1248}
1249
1250TimedSource::~TimedSource ()
1251{
1252 if (oneshot_)
1253 void_slot_.~VoidSlot();
1254 else
1255 bool_slot_.~BoolSlot();
1256}
1257
1258// == PollFDSource ==
1271PollFDSource::PollFDSource (const BPfdSlot &slot, int fd, const String &mode) :
1272 pfd_ ((PollFD) { fd, 0, 0 }),
1273 never_close_ (strchr (mode.c_str(), 'C') != NULL),
1274 oneshot_ (false), bool_poll_slot_ (slot)
1275{
1276 construct (mode);
1277}
1278
1279PollFDSource::PollFDSource (const VPfdSlot &slot, int fd, const String &mode) :
1280 pfd_ ((PollFD) { fd, 0, 0 }),
1281 never_close_ (strchr (mode.c_str(), 'C') != NULL),
1282 oneshot_ (true), void_poll_slot_ (slot)
1283{
1284 construct (mode);
1285}
1286
1287void
1288PollFDSource::construct (const String &mode)
1289{
1290 add_poll (&pfd_);
1291 pfd_.events |= strchr (mode.c_str(), 'w') ? PollFD::OUT : 0;
1292 pfd_.events |= strchr (mode.c_str(), 'r') ? PollFD::IN : 0;
1293 pfd_.events |= strchr (mode.c_str(), 'p') ? PollFD::PRI : 0;
1294 pfd_.events |= strchr (mode.c_str(), 'd') ? PollFD::WRBAND : 0;
1295 if (pfd_.fd >= 0)
1296 {
1297 const long lflags = fcntl (pfd_.fd, F_GETFL, 0);
1298 long nflags = lflags;
1299 if (strchr (mode.c_str(), 'b'))
1300 nflags &= ~long (O_NONBLOCK);
1301 else if (strchr (mode.c_str(), 'B'))
1302 nflags |= O_NONBLOCK;
1303 if (nflags != lflags)
1304 {
1305 int err;
1306 do
1307 err = fcntl (pfd_.fd, F_SETFL, nflags);
1308 while (err < 0 && (errno == EINTR || errno == EAGAIN));
1309 }
1310 }
1311}
1312
1313bool
1314PollFDSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1315{
1316 pfd_.revents = 0;
1317 return pfd_.fd < 0;
1318}
1319
1320bool
1322{
1323 return pfd_.fd < 0 || pfd_.revents != 0;
1324}
1325
1326bool
1328{
1329 bool keep_alive = !oneshot_;
1330 if (oneshot_ && void_poll_slot_ != NULL)
1331 void_poll_slot_ (pfd_);
1332 else if (!oneshot_ && bool_poll_slot_ != NULL)
1333 keep_alive = bool_poll_slot_ (pfd_);
1334 /* close down */
1335 if (!keep_alive)
1336 {
1337 if (!never_close_ && pfd_.fd >= 0)
1338 close (pfd_.fd);
1339 pfd_.fd = -1;
1340 }
1341 return keep_alive;
1342}
1343
1344void
1345PollFDSource::destroy()
1346{
1347 /* close down */
1348 if (!never_close_ && pfd_.fd >= 0)
1349 close (pfd_.fd);
1350 pfd_.fd = -1;
1351}
1352
1353PollFDSource::~PollFDSource ()
1354{
1355 if (oneshot_)
1356 void_poll_slot_.~VPfdSlot();
1357 else
1358 bool_poll_slot_.~BPfdSlot();
1359}
1360
1361} // Ase
1362
1363// == Loop Description ==
T begin(T... args)
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1031
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1019
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1025
bool opened()
Indicates whether eventfd has been opened.
Definition utils.cc:246
void flush()
Clear pending wakeups.
Definition utils.cc:280
int inputfd()
Returns the file descriptor for POLLIN.
Definition utils.cc:240
void wakeup()
Wakeup polling end.
Definition utils.cc:263
Loop object, polling for events and executing callbacks in accordance.
Definition loop.hh:58
bool has_primary(void)
Indicates whether loop contains primary sources.
Definition loop.cc:172
void wakeup()
Wakeup loop from polling.
Definition loop.cc:346
void destroy_loop(void)
Definition loop.cc:332
bool clear_source(uint *id_pointer)
Remove source if id_pointer and *id_pointer are valid.
Definition loop.cc:248
void remove(uint id)
Removes a source from loop, the source must be present.
Definition loop.cc:257
static const int16 PRIORITY_CEILING
Internal upper limit, don't use.
Definition loop.hh:88
bool exec_once(uint delay_ms, uint *once_id, const VoidSlot &vfunc, int priority=PRIORITY_NORMAL)
Execute a callback once on SIGCHLD for pid.
Definition loop.cc:264
MainLoop * main_loop() const
Get the main loop for this loop.
Definition loop.cc:190
static const int16 PRIORITY_ASCENT
Threshold for priorization across different loops.
Definition loop.hh:90
bool try_remove(uint id)
Tries to remove a source, returns if successfull.
Definition loop.cc:234
uint add(EventSourceP loop_source, int priority=PRIORITY_NORMAL)
Adds a new source to the loop with custom priority.
Definition loop.cc:198
bool primary() const
Indicate whether this source is primary.
Definition loop.cc:940
void add_poll(PollFD *const pfd)
Add a PollFD descriptors for poll(2) and check().
Definition loop.cc:958
void loop_remove()
Remove this source from its event loop if any.
Definition loop.cc:995
void remove_poll(PollFD *const pfd)
Remove a previously added PollFD.
Definition loop.cc:972
bool recursion() const
Indicates wether the source is currently in recursion.
Definition loop.cc:952
bool may_recurse() const
Indicates if this source may recurse.
Definition loop.cc:934
An EventLoop implementation that offers public API for running the loop.
Definition loop.hh:134
EventLoopP create_sub_loop()
Creates a new event loop that is run as part of this main loop.
Definition loop.cc:896
void quit(int quit_code=0)
Cause run() to return with quit_code.
Definition loop.cc:459
static MainLoopP create()
Create a MainLoop shared pointer handle.
Definition loop.cc:369
bool pending()
Check if iterate() needs to be called for dispatching.
Definition loop.cc:524
int run()
Run loop iterations until a call to quit() or finishable becomes true.
Definition loop.cc:436
bool set_g_main_context(GlibGMainContext *glib_main_context)
Set context to integrate with a GLib GMainContext loop.
Definition loop.cc:533
void iterate_pending()
Call iterate() until no immediate dispatching is needed.
Definition loop.cc:510
bool running()
Indicates if quit() has been called already.
Definition loop.cc:452
bool finishable()
Indicates wether this loop has no primary sources left to process.
Definition loop.cc:480
std::mutex & mutex()
Provide access to the mutex associated with this main loop.
Definition loop.hh:163
bool iterate(bool block)
Perform one loop iteration and return whether more iterations are needed.
Definition loop.cc:497
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1327
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1314
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1321
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1166
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1154
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1160
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1231
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1216
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1237
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1083
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1071
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:1062
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1077
close
T compare_exchange_strong(T... args)
#define ASE_ASSERT(expr)
Issue an assertion warning if expr evaluates to false.
Definition cxxaux.hh:91
#define ASE_UNLIKELY(expr)
Compiler hint to optimize for expr evaluating to false.
Definition cxxaux.hh:46
printf
T empty(T... args)
T end(T... args)
T erase(T... args)
fcntl
T find_if(T... args)
free
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define MIN(a, b)
Yield minimum of a and b.
Definition internal.hh:55
#define return_unless(cond,...)
Return silently if cond does not evaluate to true with return value ...
Definition internal.hh:71
#define ARRAY_SIZE(array)
Yield the number of C array elements.
Definition internal.hh:26
#define UNLIKELY(cond)
Hint to the compiler to optimize for cond == FALSE.
Definition internal.hh:63
#define ISLIKELY(cond)
Hint to the compiler to optimize for cond == TRUE.
Definition internal.hh:61
typedef int
T lock(T... args)
malloc
T max(T... args)
memcpy
memmove
T min(T... args)
The Anklang C++ API namespace.
Definition api.hh:9
uint64_t uint64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:25
int16_t int16
A 16-bit signed integer.
Definition cxxaux.hh:27
int8_t int8
An 8-bit signed integer.
Definition cxxaux.hh:26
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:29
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
uint64 timestamp_realtime()
Return the current time as uint64 in µseconds.
Definition platform.cc:578
poll
T push_back(T... args)
realloc
T resize(T... args)
sigaction
sigemptyset
#define INT64_MAX
strchr
strerror
strncat
uint64 current_time_usecs
Equals timestamp_realtime() as of prepare() and check().
Definition loop.hh:173
Mirrors struct pollfd for poll(3posix)
Definition loop.hh:11
@ RDNORM
reading data will not block
Definition loop.hh:20
@ HUP
file descriptor closed
Definition loop.hh:26
@ IN
RDNORM || RDBAND.
Definition loop.hh:17
@ WRNORM
writing data will not block
Definition loop.hh:22
@ PRI
urgent data available
Definition loop.hh:18
@ NVAL
invalid PollFD
Definition loop.hh:27
@ RDBAND
reading priority data will not block
Definition loop.hh:21
@ OUT
writing data will not block
Definition loop.hh:19
@ ERR
error condition
Definition loop.hh:25
@ WRBAND
writing priority data will not block
Definition loop.hh:23
typedef pid_t
T unlock(T... args)