Anklang-0.3.0.dev551+gad1415e2 anklang-0.3.0.dev551+gad1415e2
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
247void
249{
250 if (idp) {
251 if (*idp)
252 try_remove (*idp);
253 *idp = 0;
254 }
255}
256
257bool
259{
260 return_unless (id_pointer, false);
261 const bool removal = try_remove (*id_pointer);
262 *id_pointer = 0;
263 return removal;
264}
265
266void
268{
269 if (!try_remove (id))
270 warning ("%s: failed to remove loop source: %u", __func__, id);
271}
272
273bool
274EventLoop::exec_once (uint delay_ms, uint *once_id, const VoidSlot &vfunc, int priority)
275{
276 assert_return (once_id != nullptr, false);
277 assert_return (priority >= 1 && priority <= PRIORITY_CEILING, false);
278 if (!vfunc) {
279 clear_source (once_id);
280 return false;
281 }
282 auto once_handler = [vfunc,once_id]() { *once_id = 0; vfunc(); };
283 EventSourceP source = TimedSource::create (once_handler, delay_ms, 0);
284 source->loop_ = this;
285 source->id_ = alloc_id();
286 source->loop_state_ = WAITING;
287 source->priority_ = priority;
288 uint warn_id = 0;
289 {
290 std::lock_guard<std::mutex> locker (main_loop_->mutex());
291 if (*once_id) {
292 EventSourceP &source = find_source_L (*once_id);
293 if (source)
294 remove_source_Lm (source);
295 else
296 warn_id = *once_id;
297 }
298 sources_.push_back (source);
299 *once_id = source->id_;
300 }
301 if (warn_id)
302 warning ("%s: failed to remove loop source: %u", __func__, once_id);
303 wakeup();
304 return true;
305}
306
307/* void EventLoop::change_priority (EventSource *source, int priority) {
308 * // ensure that source belongs to this
309 * // reset all source->pfds[].idx = UINT_MAX
310 * // unlink source
311 * // poke priority
312 * // re-add source
313 */
314
315void
316EventLoop::kill_sources_Lm()
317{
318 for (;;)
319 {
320 EventSourceP &source = find_first_L();
321 if (source == NULL)
322 break;
323 remove_source_Lm (source);
324 }
325 std::mutex &LOCK = main_loop_->mutex();
326 LOCK.unlock();
327 unpoll_sources_U(); // unlocked
328 LOCK.lock();
329}
330
341void
343{
344 assert_return (main_loop_ != NULL);
345 // guard main_loop_ pointer *before* locking, so dtor is called after unlock
346 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop*> (main_loop_);
347 std::lock_guard<std::mutex> locker (main_loop_->mutex());
348 if (this != main_loop_)
349 main_loop_->kill_loop_Lm (*this);
350 else
351 main_loop_->kill_loops_Lm();
352 assert_return (main_loop_ == NULL);
353}
354
355void
357{
358 // this needs to work unlocked
359 main_loop_->wakeup_poll();
360}
361
362// === MainLoop ===
363MainLoop::MainLoop() :
364 EventLoop (*this), // sets *this as MainLoop on self
365 rr_index_ (0), running_ (false), has_quit_ (false), quit_code_ (0), gcontext_ (NULL)
366{
367 std::lock_guard<std::mutex> locker (main_loop_->mutex());
368 const int err = eventfd_.open();
369 if (err < 0)
370 fatal_error ("MainLoop: failed to create wakeup pipe: %s", strerror (-err));
371 // has_quit_ and eventfd_ need to be setup here, so calling quit() before run() works
372}
373
378MainLoopP
380{
381 MainLoopP main_loop = make_shared();
383 main_loop->add_loop_L (*main_loop);
384 return main_loop;
385}
386
387MainLoop::~MainLoop()
388{
389 set_g_main_context (NULL); // acquires mutex_
390 std::lock_guard<std::mutex> locker (mutex_);
391 if (main_loop_)
392 kill_loops_Lm();
393 assert_return (loops_.empty() == true);
394}
395
396void
397MainLoop::wakeup_poll()
398{
399 if (eventfd_.opened())
400 eventfd_.wakeup();
401}
402
403void
404MainLoop::add_loop_L (EventLoop &loop)
405{
406 assert_return (this == loop.main_loop_);
407 loops_.push_back (shared_ptr_cast<EventLoop> (&loop));
408 wakeup_poll();
409}
410
411void
412MainLoop::kill_loop_Lm (EventLoop &loop)
413{
414 assert_return (this == loop.main_loop_);
415 loop.kill_sources_Lm();
416 if (loop.main_loop_) // guard against nested kill_loop_Lm (same) calls
417 {
418 loop.main_loop_ = NULL;
419 std::vector<EventLoopP>::iterator it = std::find_if (loops_.begin(), loops_.end(),
420 [&loop] (EventLoopP &lp) { return lp.get() == &loop; });
421 if (this == &loop) // MainLoop->destroy_loop()
422 {
423 if (loops_.size()) // MainLoop must be the last loop to be destroyed
424 assert_return (loops_[0].get() == this);
425 }
426 else
427 assert_return (it != loops_.end());
428 if (it != loops_.end())
429 loops_.erase (it);
430 wakeup_poll();
431 }
432}
433
434void
435MainLoop::kill_loops_Lm()
436{
437 while (loops_.size() > 1 || loops_[0].get() != this)
438 {
439 EventLoopP loop = loops_[0].get() != this ? loops_[0] : loops_[loops_.size() - 1];
440 kill_loop_Lm (*loop);
441 }
442 kill_loop_Lm (*this);
443}
444
445int
447{
448 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
449 std::lock_guard<std::mutex> locker (mutex_);
450 LoopState state;
451 running_ = !has_quit_;
452 while (ISLIKELY (running_))
453 iterate_loops_Lm (state, true, true);
454 const int last_quit_code = quit_code_;
455 running_ = false;
456 has_quit_ = false; // allow loop resumption
457 quit_code_ = 0; // allow loop resumption
458 return last_quit_code;
459}
460
461bool
463{
464 std::lock_guard<std::mutex> locker (mutex_);
465 return running_;
466}
467
468void
469MainLoop::quit (int quit_code)
470{
471 std::lock_guard<std::mutex> locker (mutex_);
472 quit_code_ = quit_code;
473 has_quit_ = true; // cancel run() upfront, or
474 running_ = false; // interrupt current iteration
475 wakeup();
476}
477
478bool
479MainLoop::finishable_L()
480{
481 // loop list shouldn't be modified by querying finishable state
482 bool found_primary = primary_;
483 for (size_t i = 0; !found_primary && i < loops_.size(); i++)
484 if (loops_[i]->has_primary_L())
485 found_primary = true;
486 return !found_primary; // finishable if no primary sources remain
487}
488
489bool
491{
492 std::lock_guard<std::mutex> locker (mutex_);
493 return finishable_L();
494}
495
506bool
507MainLoop::iterate (bool may_block)
508{
509 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
510 std::lock_guard<std::mutex> locker (mutex_);
511 LoopState state;
512 const bool was_running = running_; // guard for recursion
513 running_ = true;
514 const bool sources_pending = iterate_loops_Lm (state, may_block, true);
515 running_ = was_running && !has_quit_;
516 return sources_pending;
517}
518
519void
521{
522 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
523 std::lock_guard<std::mutex> locker (mutex_);
524 LoopState state;
525 const bool was_running = running_; // guard for recursion
526 running_ = true;
527 while (ISLIKELY (running_))
528 if (!iterate_loops_Lm (state, false, true))
529 break;
530 running_ = was_running && !has_quit_;
531}
532
533bool
535{
536 std::lock_guard<std::mutex> locker (mutex_);
537 LoopState state;
538 EventLoopP main_loop_guard = shared_ptr_cast<EventLoop> (this);
539 return iterate_loops_Lm (state, false, false);
540}
541
542bool
543MainLoop::set_g_main_context (GlibGMainContext *glib_main_context)
544{
545#ifdef __G_LIB_H__
546 std::lock_guard<std::mutex> locker (mutex_);
547 if (glib_main_context)
548 {
549 if (gcontext_)
550 return false;
551 if (!g_main_context_acquire (glib_main_context))
552 return false;
553 gcontext_ = g_main_context_ref (glib_main_context);
554 }
555 else if (gcontext_)
556 {
557 glib_main_context = gcontext_;
558 gcontext_ = NULL;
559 g_main_context_release (glib_main_context);
560 g_main_context_unref (glib_main_context);
561 }
562 return true;
563#else
564 return false;
565#endif
566}
567
568#ifdef __G_LIB_H__
569static GPollFD*
570mk_gpollfd (PollFD *pfd)
571{
572 GPollFD *gpfd = (GPollFD*) pfd;
573 static_assert (sizeof (GPollFD) == sizeof (PollFD), "");
574 static_assert (sizeof (gpfd->fd) == sizeof (pfd->fd), "");
575 static_assert (sizeof (gpfd->events) == sizeof (pfd->events), "");
576 static_assert (sizeof (gpfd->revents) == sizeof (pfd->revents), "");
577 static_assert (offsetof (GPollFD, fd) == offsetof (PollFD, fd), "");
578 static_assert (offsetof (GPollFD, events) == offsetof (PollFD, events), "");
579 static_assert (offsetof (GPollFD, revents) == offsetof (PollFD, revents), "");
580 static_assert (PollFD::IN == int (G_IO_IN), "");
581 static_assert (PollFD::PRI == int (G_IO_PRI), "");
582 static_assert (PollFD::OUT == int (G_IO_OUT), "");
583 // static_assert (PollFD::RDNORM == int (G_IO_RDNORM), "");
584 // static_assert (PollFD::RDBAND == int (G_IO_RDBAND), "");
585 // static_assert (PollFD::WRNORM == int (G_IO_WRNORM), "");
586 // static_assert (PollFD::WRBAND == int (G_IO_WRBAND), "");
587 static_assert (PollFD::ERR == int (G_IO_ERR), "");
588 static_assert (PollFD::HUP == int (G_IO_HUP), "");
589 static_assert (PollFD::NVAL == int (G_IO_NVAL), "");
590 return gpfd;
591}
592#endif
593
594void
595EventLoop::unpoll_sources_U() // must be unlocked!
596{
597 // clear poll sources
598 poll_sources_.resize (0);
599}
600
601void
602EventLoop::collect_sources_Lm (LoopState &state)
603{
604 // enforce clean slate
605 if (UNLIKELY (!poll_sources_.empty()))
606 {
607 std::mutex &LOCK = main_loop_->mutex();
608 LOCK.unlock();
609 unpoll_sources_U(); // unlocked
610 LOCK.lock();
611 assert_return (poll_sources_.empty());
612 }
613 if (UNLIKELY (!state.seen_primary && primary_))
614 state.seen_primary = true;
615 EventSourceP* arraymem[7]; // using a vector+malloc here shows up in the profiles
616 QuickSourcePArray poll_candidates (ARRAY_SIZE (arraymem), arraymem);
617 // determine dispatch priority & collect sources for preparing
618 dispatch_priority_ = UNDEFINED_PRIORITY; // initially, consider sources at *all* priorities
619 for (SourceList::iterator lit = sources_.begin(); lit != sources_.end(); lit++)
620 {
621 EventSource &source = **lit;
622 if (UNLIKELY (!state.seen_primary && source.primary_))
623 state.seen_primary = true;
624 if (source.loop_ != this || // ignore destroyed and
625 (source.dispatching_ && !source.may_recurse_)) // avoid unallowed recursion
626 continue;
627 if (source.priority_ > dispatch_priority_ && // ignore lower priority sources
628 source.loop_state_ == NEEDS_DISPATCH) // if NEEDS_DISPATCH sources remain
629 dispatch_priority_ = source.priority_; // so raise dispatch_priority_
630 if (source.priority_ > dispatch_priority_ || // add source if it is an eligible
631 (source.priority_ == dispatch_priority_ && // candidate, baring future raises
632 source.loop_state_ == NEEDS_DISPATCH)) // of dispatch_priority_...
633 poll_candidates.push (&*lit); // collect only, adding ref() later
634 }
635 // ensure ref counts on all prepare sources
636 assert_return (poll_sources_.empty());
637 for (size_t i = 0; i < poll_candidates.size(); i++)
638 if ((*poll_candidates[i])->priority_ > dispatch_priority_ || // throw away lower priority sources
639 ((*poll_candidates[i])->priority_ == dispatch_priority_ &&
640 (*poll_candidates[i])->loop_state_ == NEEDS_DISPATCH)) // re-poll sources that need dispatching
641 poll_sources_.push_back (*poll_candidates[i]);
642 /* here, poll_sources_ contains either all sources, or only the highest priority
643 * NEEDS_DISPATCH sources plus higher priority sources. giving precedence to the
644 * remaining NEEDS_DISPATCH sources ensures round-robin processing.
645 */
646}
647
648bool
649EventLoop::prepare_sources_Lm (LoopState &state, QuickPfdArray &pfda)
650{
651 std::mutex &LOCK = main_loop_->mutex();
652 // prepare sources, up to NEEDS_DISPATCH priority
653 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
654 {
655 EventSource &source = **lit;
656 if (source.loop_ != this) // test undestroyed
657 continue;
658 int64 timeout = -1;
659 LOCK.unlock();
660 const bool need_dispatch = source.prepare (state, &timeout);
661 LOCK.lock();
662 if (source.loop_ != this)
663 continue; // ignore newly destroyed sources
664 if (need_dispatch)
665 {
666 dispatch_priority_ = std::max (dispatch_priority_, source.priority_); // upgrade dispatch priority
667 source.loop_state_ = NEEDS_DISPATCH;
668 continue;
669 }
670 source.loop_state_ = PREPARED;
671 if (timeout >= 0)
672 state.timeout_usecs = std::min (state.timeout_usecs, timeout);
673 uint npfds = source.n_pfds();
674 for (uint i = 0; i < npfds; i++)
675 if (source.pfds_[i].pfd->fd >= 0)
676 {
677 uint idx = pfda.size();
678 source.pfds_[i].idx = idx;
679 pfda.push (*source.pfds_[i].pfd);
680 pfda[idx].revents = 0;
681 }
682 else
683 source.pfds_[i].idx = 4294967295U; // UINT_MAX
684 }
685 return dispatch_priority_ > UNDEFINED_PRIORITY;
686}
687
688bool
689EventLoop::check_sources_Lm (LoopState &state, const QuickPfdArray &pfda)
690{
691 std::mutex &LOCK = main_loop_->mutex();
692 // check polled sources
693 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
694 {
695 EventSource &source = **lit;
696 if (source.loop_ != this && // test undestroyed
697 source.loop_state_ != PREPARED)
698 continue; // only check prepared sources
699 uint npfds = source.n_pfds();
700 for (uint i = 0; i < npfds; i++)
701 {
702 uint idx = source.pfds_[i].idx;
703 if (idx < pfda.size() &&
704 source.pfds_[i].pfd->fd == pfda[idx].fd)
705 source.pfds_[i].pfd->revents = pfda[idx].revents;
706 else
707 source.pfds_[i].idx = 4294967295U; // UINT_MAX
708 }
709 LOCK.unlock();
710 bool need_dispatch = source.check (state);
711 LOCK.lock();
712 if (source.loop_ != this)
713 continue; // ignore newly destroyed sources
714 if (need_dispatch)
715 {
716 dispatch_priority_ = std::max (dispatch_priority_, source.priority_); // upgrade dispatch priority
717 source.loop_state_ = NEEDS_DISPATCH;
718 }
719 else
720 source.loop_state_ = WAITING;
721 }
722 return dispatch_priority_ > UNDEFINED_PRIORITY;
723}
724
725void
726EventLoop::dispatch_source_Lm (LoopState &state)
727{
728 std::mutex &LOCK = main_loop_->mutex();
729 // find a source to dispatch at dispatch_priority_
730 EventSourceP dispatch_source = NULL; // shared_ptr to keep alive even if everything else is destroyed
731 for (auto lit = poll_sources_.begin(); lit != poll_sources_.end(); lit++)
732 {
733 EventSourceP &source = *lit;
734 if (source->loop_ == this && // test undestroyed
735 source->priority_ == dispatch_priority_ && // only dispatch at dispatch priority
736 source->loop_state_ == NEEDS_DISPATCH)
737 {
738 dispatch_source = source;
739 break;
740 }
741 }
742 dispatch_priority_ = UNDEFINED_PRIORITY;
743 // dispatch single source
744 if (dispatch_source)
745 {
746 dispatch_source->loop_state_ = WAITING;
747 const bool old_was_dispatching = dispatch_source->was_dispatching_;
748 dispatch_source->was_dispatching_ = dispatch_source->dispatching_;
749 dispatch_source->dispatching_ = true;
750 LOCK.unlock();
751 const bool keep_alive = dispatch_source->dispatch (state);
752 LOCK.lock();
753 dispatch_source->dispatching_ = dispatch_source->was_dispatching_;
754 dispatch_source->was_dispatching_ = old_was_dispatching;
755 if (dispatch_source->loop_ == this && !keep_alive)
756 remove_source_Lm (dispatch_source);
757 }
758}
759
760bool
761MainLoop::iterate_loops_Lm (LoopState &state, bool may_block, bool may_dispatch)
762{
763 assert_return (state.phase == state.NONE, false);
764 std::mutex &LOCK = main_loop_->mutex();
765 PollFD reserved_pfd_mem[7]; // store PollFD array in stack memory, to reduce malloc overhead
766 QuickPfdArray pfda (ARRAY_SIZE (reserved_pfd_mem), reserved_pfd_mem); // pfda.size() == 0
767 // allow poll wakeups
768 const PollFD wakeup = { eventfd_.inputfd(), PollFD::IN, 0 };
769 const uint wakeup_idx = 0; // wakeup_idx = pfda.size();
770 pfda.push (wakeup);
771 // create pollable loop list
772 const size_t nrloops = loops_.size(); // number of Ase loops, *without* gcontext_
773 EventLoopP loops[nrloops];
774 for (size_t i = 0; i < nrloops; i++)
775 loops[i] = loops_[i];
776 // collect
777 state.phase = state.COLLECT;
778 state.seen_primary = false;
779 for (size_t i = 0; i < nrloops; i++)
780 loops[i]->collect_sources_Lm (state);
781 // prepare
782 bool any_dispatchable = false;
783 state.phase = state.PREPARE;
784 state.timeout_usecs = INT64_MAX;
785 state.current_time_usecs = timestamp_realtime();
786 bool dispatchable[nrloops + 1]; // +1 for gcontext_
787 for (size_t i = 0; i < nrloops; i++)
788 {
789 dispatchable[i] = loops[i]->prepare_sources_Lm (state, pfda);
790 any_dispatchable |= dispatchable[i];
791 }
792 // prepare GLib
793 ASE_UNUSED const int gfirstfd = pfda.size();
794 ASE_UNUSED int gpriority = INT32_MIN;
795 if (ASE_UNLIKELY (gcontext_))
796 {
797#ifdef __G_LIB_H__
798 dispatchable[nrloops] = g_main_context_prepare (gcontext_, &gpriority) != 0;
799 any_dispatchable |= dispatchable[nrloops];
800 int gtimeout = INT32_MAX;
801 pfda.resize (pfda.capacity());
802 int gnfds = g_main_context_query (gcontext_, gpriority, &gtimeout, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
803 while (gnfds >= 0 && size_t (gnfds) != pfda.size() - gfirstfd)
804 {
805 pfda.resize (gfirstfd + gnfds);
806 gtimeout = INT32_MAX;
807 gnfds = g_main_context_query (gcontext_, gpriority, &gtimeout, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
808 }
809 if (gtimeout >= 0)
810 state.timeout_usecs = MIN (state.timeout_usecs, gtimeout * int64 (1000));
811#endif
812 }
813 else
814 dispatchable[nrloops] = false;
815 // poll file descriptors
816 int64 timeout_msecs = state.timeout_usecs / 1000;
817 if (state.timeout_usecs > 0 && timeout_msecs <= 0)
818 timeout_msecs = 1;
819 if (!may_block || any_dispatchable)
820 timeout_msecs = 0;
821 state.timeout_usecs = 0;
822 LOCK.unlock();
823 int presult;
824 do
825 presult = poll ((struct pollfd*) &pfda[0], pfda.size(), std::min (timeout_msecs, int64 (2147483647))); // INT_MAX
826 while (presult < 0 && errno == EAGAIN); // EINTR may indicate a signal
827 LOCK.lock();
828 if (presult < 0 && errno != EINTR)
829 warning ("MainLoop: poll() failed: %s", strerror());
830 else if (pfda[wakeup_idx].revents)
831 eventfd_.flush(); // restart queueing wakeups, possibly triggered by dispatching
832 // check
833 state.phase = state.CHECK;
834 state.current_time_usecs = timestamp_realtime();
835 int16 max_dispatch_priority = -32768;
836 for (size_t i = 0; i < nrloops; i++)
837 {
838 dispatchable[i] |= loops[i]->check_sources_Lm (state, pfda);
839 if (!dispatchable[i])
840 continue;
841 any_dispatchable = true;
842 max_dispatch_priority = std::max (max_dispatch_priority, loops[i]->dispatch_priority_);
843 }
844 bool priority_ascension = false; // flag for priority elevation between loops
845 if (UNLIKELY (max_dispatch_priority >= PRIORITY_ASCENT) && any_dispatchable)
846 priority_ascension = true;
847 // check GLib
848 if (ASE_UNLIKELY (gcontext_))
849 {
850#ifdef __G_LIB_H__
851 dispatchable[nrloops] = g_main_context_check (gcontext_, gpriority, mk_gpollfd (&pfda[gfirstfd]), pfda.size() - gfirstfd);
852 any_dispatchable |= dispatchable[nrloops];
853#endif
854 }
855 // dispatch
856 if (may_dispatch && any_dispatchable)
857 {
858 const size_t gloop = ASE_UNLIKELY (gcontext_) && dispatchable[nrloops] ? 1 : 0;
859 const size_t maxloops = nrloops + gloop; // +1 for gcontext_
860 size_t index;
861 while (true) // pick loops in round-robin fashion
862 {
863 index = rr_index_++ % maxloops; // round-robin step
864 if (!dispatchable[index])
865 continue; // need to find next dispatchable
866 if (index < nrloops && loops[index]->dispatch_priority_ < max_dispatch_priority)
867 continue; // ignore loops without max-priority source, except for gcontext_ which can still benefit from round-robin
868 if (priority_ascension && index >= nrloops)
869 continue; // but there's no priority_ascension support for gcontext_
870 break; // DONE: index points to a dispatchable loop which meets priority requirements
871 }
872 state.phase = state.DISPATCH;
873 if (index >= nrloops)
874 {
875#ifdef __G_LIB_H__
876 g_main_context_dispatch (gcontext_);
877#endif
878 }
879 else
880 loops[index]->dispatch_source_Lm (state); // passes on shared_ptr to keep alive while locked
881 }
882 // cleanup
883 state.phase = state.NONE;
884 LOCK.unlock();
885 for (size_t i = 0; i < nrloops; i++)
886 {
887 loops[i]->unpoll_sources_U(); // unlocked
888 loops[i] = NULL; // dtor, unlocked
889 }
890 LOCK.lock();
891 return any_dispatchable; // need to dispatch or recheck
892}
893
894class SubLoop : public EventLoop {
895public:
896 SubLoop (MainLoopP main) :
897 EventLoop (*main)
898 {}
899 ~SubLoop()
900 {
901 assert_return (main_loop_ == NULL);
902 }
903};
904
907{
909 EventLoopP sub_loop = std::make_shared<SubLoop> (shared_ptr_cast<MainLoop> (this));
910 this->add_loop_L (*sub_loop);
911 return sub_loop;
912}
913
914// === EventSource ===
915EventSource::EventSource () :
916 loop_ (NULL),
917 pfds_ (NULL),
918 id_ (0),
919 priority_ (UNDEFINED_PRIORITY),
920 loop_state_ (0),
921 may_recurse_ (0),
922 dispatching_ (0),
923 was_dispatching_ (0),
924 primary_ (0)
925{}
926
927uint
928EventSource::n_pfds ()
929{
930 uint i = 0;
931 if (pfds_)
932 while (pfds_[i].pfd)
933 i++;
934 return i;
935}
936
937void
938EventSource::may_recurse (bool may_recurse)
939{
940 may_recurse_ = may_recurse;
941}
942
943bool
945{
946 return may_recurse_;
947}
948
949bool
951{
952 return primary_;
953}
954
955void
956EventSource::primary (bool is_primary)
957{
958 primary_ = is_primary;
959}
960
961bool
963{
964 return dispatching_ && was_dispatching_;
965}
966
967void
969{
970 const uint idx = n_pfds();
971 uint npfds = idx + 1;
972 pfds_ = (typeof (pfds_)) realloc (pfds_, sizeof (pfds_[0]) * (npfds + 1));
973 if (!pfds_)
974 fatal_error ("EventSource: out of memory");
975 pfds_[npfds].idx = 4294967295U; // UINT_MAX
976 pfds_[npfds].pfd = NULL;
977 pfds_[idx].idx = 4294967295U; // UINT_MAX
978 pfds_[idx].pfd = pfd;
979}
980
981void
983{
984 uint idx, npfds = n_pfds();
985 for (idx = 0; idx < npfds; idx++)
986 if (pfds_[idx].pfd == pfd)
987 break;
988 if (idx < npfds)
989 {
990 pfds_[idx].idx = 4294967295U; // UINT_MAX
991 pfds_[idx].pfd = pfds_[npfds - 1].pfd;
992 pfds_[idx].idx = pfds_[npfds - 1].idx;
993 pfds_[npfds - 1].idx = 4294967295U; // UINT_MAX
994 pfds_[npfds - 1].pfd = NULL;
995 }
996 else
997 warning ("EventSource: unremovable PollFD: %p (fd=%d)", pfd, pfd->fd);
998}
999
1000void
1001EventSource::destroy ()
1002{}
1003
1004void
1006{
1007 if (loop_)
1008 loop_->try_remove (source_id());
1009}
1010
1011EventSource::~EventSource ()
1012{
1013 assert_return (loop_ == NULL);
1014 if (pfds_)
1015 free (pfds_);
1016}
1017
1018// == DispatcherSource ==
1019DispatcherSource::DispatcherSource (const DispatcherSlot &slot) :
1020 slot_ (slot)
1021{}
1022
1023DispatcherSource::~DispatcherSource ()
1024{
1025 slot_ = NULL;
1026}
1027
1028bool
1029DispatcherSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1030{
1031 return slot_ (state);
1032}
1033
1034bool
1036{
1037 return slot_ (state);
1038}
1039
1040bool
1042{
1043 return slot_ (state);
1044}
1045
1046void
1047DispatcherSource::destroy()
1048{
1049 LoopState state;
1050 state.phase = state.DESTROY;
1051 slot_ (state);
1052}
1053
1054// == USignalSource ==
1055USignalSource::USignalSource (int8 signum, const USignalSlot &slot) :
1056 slot_ (slot), signum_ (signum)
1057{
1058 const uint s = 128 + signum_;
1059 index_ = s / 32;
1060 shift_ = s % 32;
1061}
1062
1063USignalSource::~USignalSource ()
1064{
1065 slot_ = NULL;
1066}
1067
1068static std::atomic<uint32> usignals_notified[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
1069
1071void
1073{
1074 const uint s = 128 + signum;
1075 const uint index = s / 32;
1076 const uint shift = s % 32;
1077 usignals_notified[index] |= 1 << shift;
1078}
1079
1080bool
1081USignalSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1082{
1083 return usignals_notified[index_] & (1 << shift_);
1084}
1085
1086bool
1088{
1089 return usignals_notified[index_] & (1 << shift_);
1090}
1091
1092bool
1094{
1095 usignals_notified[index_] &= ~(1 << shift_);
1096 return slot_ (signum_);
1097}
1098
1099void
1100USignalSource::destroy()
1101{}
1102
1104write_uint (uint32_t i)
1105{
1107 char *c = &a.back();
1108 ASE_ASSERT (c>=&a[0] && c<&a[a.size()]);
1109 *c-- = 0;
1110 *c = '0' + (i % 10);
1111 i /= 10;
1112 while (i != 0) {
1113 *(--c) = '0' + (i % 10);
1114 i /= 10;
1115 }
1116 if (c > &a[0])
1117 memmove (&a[0], c, &a.back() + 1 - c);
1118 ASE_ASSERT (c>=&a[0] && c<&a[a.size()]);
1119 return a;
1120}
1121
1122void
1123USignalSource::install_sigaction (int8 signum)
1124{
1125 struct sigaction action;
1126 action.sa_handler = [] (int signum) {
1127 if (0) { // DEBUG
1128 constexpr size_t N = 1024;
1129 char buf[N] = __FILE__ ":";
1130 strncat (buf, &write_uint (__LINE__)[0], N);
1131 strncat (buf, ": sa_handler: signal=", N);
1132 strncat (buf, &write_uint (signum)[0], N);
1133 strncat (buf, "\n", N);
1134 ::write (2, buf, strlen (buf));
1135 }
1136 USignalSource::raise (signum);
1137 };
1138 sigemptyset (&action.sa_mask);
1139 action.sa_flags = SA_NOMASK;
1140 sigaction (signum, &action, nullptr);
1141}
1142
1143// === SigchldSource ===
1144static std::atomic<uint64_t> sigchld_counter = 0;
1145
1146SigchldSource::SigchldSource (int64_t pid, const SigchldSlot &slot) :
1147 slot_ (slot), pid_ (pid)
1148{
1149 if (uint64_t unused = 0; sigchld_counter.compare_exchange_strong (unused, 1)) {
1150 struct sigaction action;
1151 action.sa_handler = [] (int signum) {
1152 sigchld_counter++;
1153 };
1154 sigemptyset (&action.sa_mask);
1155 action.sa_flags = SA_NOMASK;
1156 sigaction (SIGCHLD, &action, nullptr);
1157 }
1158}
1159
1160SigchldSource::~SigchldSource()
1161{}
1162
1163bool
1164SigchldSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1165{
1166 return pid_ && sigchld_counter_ != sigchld_counter;
1167}
1168
1169bool
1171{
1172 return pid_ && sigchld_counter_ != sigchld_counter;
1173}
1174
1175bool
1177{
1178 if (pid_) {
1179 sigchld_counter_ = sigchld_counter;
1180 // Use pid_ to avoid reaping unknown children
1181 int status = 0;
1182 const pid_t child_pid = wait4 (pid_, &status, WNOHANG, nullptr);
1183 if (child_pid > 0) {
1184 slot_ (pid_, status);
1185#if 0
1186 struct rusage ru {}; // wait4 (..., &ru);
1187 printf (" Child Pid %d user time: %ld.%06ld sec\n", child_pid, ru.ru_utime.tv_sec, ru.ru_utime.tv_usec);
1188 printf (" System time: %ld.%06ld sec\n", ru.ru_stime.tv_sec, ru.ru_stime.tv_usec);
1189 printf (" Max RSS: %ld KB\n", ru.ru_maxrss);
1190 printf (" Page faults: %ld\n", ru.ru_minflt);
1191 printf (" I/O operations: %ld\n", ru.ru_inblock + ru.ru_oublock);
1192 printf (" Voluntary context switches: %ld\n", ru.ru_nvcsw);
1193 printf (" Involuntary context switches: %ld\n", ru.ru_nivcsw);
1194 printf ("\n");
1195#endif
1196 if (WIFEXITED (status) || WIFSIGNALED (status)) {
1197 // Child exited
1198 pid_ = 0;
1199 return false; // destroy
1200 }
1201 }
1202 }
1203 return true; // keep_alive
1204}
1205
1206void
1207SigchldSource::destroy ()
1208{
1209 pid_ = 0;
1210}
1211
1212// == TimedSource ==
1213TimedSource::TimedSource (const VoidSlot &slot, uint initial_interval_msecs, uint repeat_interval_msecs) :
1214 expiration_usecs_ (timestamp_realtime() + 1000ULL * initial_interval_msecs),
1215 interval_msecs_ (repeat_interval_msecs), first_interval_ (true),
1216 oneshot_ (true), void_slot_ (slot)
1217{}
1218
1219TimedSource::TimedSource (const BoolSlot &slot, uint initial_interval_msecs, uint repeat_interval_msecs) :
1220 expiration_usecs_ (timestamp_realtime() + 1000ULL * initial_interval_msecs),
1221 interval_msecs_ (repeat_interval_msecs), first_interval_ (true),
1222 oneshot_ (false), bool_slot_ (slot)
1223{}
1224
1225bool
1226TimedSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1227{
1228 if (state.current_time_usecs >= expiration_usecs_)
1229 return true; /* timeout expired */
1230 if (!first_interval_)
1231 {
1232 uint64 interval = interval_msecs_ * 1000ULL;
1233 if (state.current_time_usecs + interval < expiration_usecs_)
1234 expiration_usecs_ = state.current_time_usecs + interval; /* clock warped back in time */
1235 }
1236 *timeout_usecs_p = std::min (expiration_usecs_ - state.current_time_usecs, uint64 (2147483647)); // INT_MAX
1237 return 0 == *timeout_usecs_p;
1238}
1239
1240bool
1242{
1243 return state.current_time_usecs >= expiration_usecs_;
1244}
1245
1246bool
1248{
1249 bool repeat = false;
1250 first_interval_ = false;
1251 if (oneshot_ && void_slot_ != NULL)
1252 void_slot_ ();
1253 else if (!oneshot_ && bool_slot_ != NULL)
1254 repeat = bool_slot_ ();
1255 if (repeat)
1256 expiration_usecs_ = timestamp_realtime() + 1000ULL * interval_msecs_;
1257 return repeat;
1258}
1259
1260TimedSource::~TimedSource ()
1261{
1262 if (oneshot_)
1263 void_slot_.~VoidSlot();
1264 else
1265 bool_slot_.~BoolSlot();
1266}
1267
1268// == PollFDSource ==
1281PollFDSource::PollFDSource (const BPfdSlot &slot, int fd, const String &mode) :
1282 pfd_ ((PollFD) { fd, 0, 0 }),
1283 never_close_ (strchr (mode.c_str(), 'C') != NULL),
1284 oneshot_ (false), bool_poll_slot_ (slot)
1285{
1286 construct (mode);
1287}
1288
1289PollFDSource::PollFDSource (const VPfdSlot &slot, int fd, const String &mode) :
1290 pfd_ ((PollFD) { fd, 0, 0 }),
1291 never_close_ (strchr (mode.c_str(), 'C') != NULL),
1292 oneshot_ (true), void_poll_slot_ (slot)
1293{
1294 construct (mode);
1295}
1296
1297void
1298PollFDSource::construct (const String &mode)
1299{
1300 add_poll (&pfd_);
1301 pfd_.events |= strchr (mode.c_str(), 'w') ? PollFD::OUT : 0;
1302 pfd_.events |= strchr (mode.c_str(), 'r') ? PollFD::IN : 0;
1303 pfd_.events |= strchr (mode.c_str(), 'p') ? PollFD::PRI : 0;
1304 pfd_.events |= strchr (mode.c_str(), 'd') ? PollFD::WRBAND : 0;
1305 if (pfd_.fd >= 0)
1306 {
1307 const long lflags = fcntl (pfd_.fd, F_GETFL, 0);
1308 long nflags = lflags;
1309 if (strchr (mode.c_str(), 'b'))
1310 nflags &= ~long (O_NONBLOCK);
1311 else if (strchr (mode.c_str(), 'B'))
1312 nflags |= O_NONBLOCK;
1313 if (nflags != lflags)
1314 {
1315 int err;
1316 do
1317 err = fcntl (pfd_.fd, F_SETFL, nflags);
1318 while (err < 0 && (errno == EINTR || errno == EAGAIN));
1319 }
1320 }
1321}
1322
1323bool
1324PollFDSource::prepare (const LoopState &state, int64 *timeout_usecs_p)
1325{
1326 pfd_.revents = 0;
1327 return pfd_.fd < 0;
1328}
1329
1330bool
1332{
1333 return pfd_.fd < 0 || pfd_.revents != 0;
1334}
1335
1336bool
1338{
1339 bool keep_alive = !oneshot_;
1340 if (oneshot_ && void_poll_slot_ != NULL)
1341 void_poll_slot_ (pfd_);
1342 else if (!oneshot_ && bool_poll_slot_ != NULL)
1343 keep_alive = bool_poll_slot_ (pfd_);
1344 /* close down */
1345 if (!keep_alive)
1346 {
1347 if (!never_close_ && pfd_.fd >= 0)
1348 close (pfd_.fd);
1349 pfd_.fd = -1;
1350 }
1351 return keep_alive;
1352}
1353
1354void
1355PollFDSource::destroy()
1356{
1357 /* close down */
1358 if (!never_close_ && pfd_.fd >= 0)
1359 close (pfd_.fd);
1360 pfd_.fd = -1;
1361}
1362
1363PollFDSource::~PollFDSource ()
1364{
1365 if (oneshot_)
1366 void_poll_slot_.~VPfdSlot();
1367 else
1368 bool_poll_slot_.~BPfdSlot();
1369}
1370
1371} // Ase
1372
1373// == Loop Description ==
T begin(T... args)
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1041
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1029
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1035
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:356
void destroy_loop(void)
Remove all sources from a loop and prevent any further execution.
Definition loop.cc:342
bool clear_source(uint *id_pointer)
Remove source if id_pointer and *id_pointer are valid.
Definition loop.cc:258
void remove(uint id)
Removes a source from loop, the source must be present.
Definition loop.cc:267
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:274
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:950
void add_poll(PollFD *const pfd)
Add a PollFD descriptors for poll(2) and check().
Definition loop.cc:968
void loop_remove()
Remove this source from its event loop if any.
Definition loop.cc:1005
void remove_poll(PollFD *const pfd)
Remove a previously added PollFD.
Definition loop.cc:982
bool recursion() const
Indicates wether the source is currently in recursion.
Definition loop.cc:962
bool may_recurse() const
Indicates if this source may recurse.
Definition loop.cc:944
An EventLoop implementation that offers public API for running the loop.
Definition loop.hh:135
EventLoopP create_sub_loop()
Creates a new event loop that is run as part of this main loop.
Definition loop.cc:906
void quit(int quit_code=0)
Cause run() to return with quit_code.
Definition loop.cc:469
static MainLoopP create()
Create a MainLoop shared pointer handle.
Definition loop.cc:379
bool pending()
Check if iterate() needs to be called for dispatching.
Definition loop.cc:534
int run()
Run loop iterations until a call to quit() or finishable becomes true.
Definition loop.cc:446
bool set_g_main_context(GlibGMainContext *glib_main_context)
Set context to integrate with a GLib GMainContext loop.
Definition loop.cc:543
void iterate_pending()
Call iterate() until no immediate dispatching is needed.
Definition loop.cc:520
bool running()
Indicates if quit() has been called already.
Definition loop.cc:462
bool finishable()
Indicates wether this loop has no primary sources left to process.
Definition loop.cc:490
std::mutex & mutex()
Provide access to the mutex associated with this main loop.
Definition loop.hh:164
bool iterate(bool block)
Perform one loop iteration and return whether more iterations are needed.
Definition loop.cc:507
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1337
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1324
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1331
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1176
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1164
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1170
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1241
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1226
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1247
virtual bool dispatch(const LoopState &state)
Dispatch source, returns if it should be kept alive.
Definition loop.cc:1093
virtual bool prepare(const LoopState &state, int64 *timeout_usecs_p)
Prepare the source for dispatching (true return) or polling (false).
Definition loop.cc:1081
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:1072
virtual bool check(const LoopState &state)
Check the source and its PollFD descriptors for dispatching (true return).
Definition loop.cc:1087
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:579
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:174
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)