4

TL;DR

What is the best implementation for an event loop in C/C++ that manages the call stack well?

  • Why?
  • Benchmarks/Comparisons?
  • Literature/Publications/Documentation?

Event Loops, the Problem

They're an easy concept: Wait for an event, handle event, wait for more events.

I was looking back over some old projects and came across a simple (and kinda poor) implementation of a search engine, and my curiosity was sparked about the proper way to do event loops.

At the time I'd done something like this (very) simplified example:

int wait_for_query();
int handle_query();

int main(int argc, const char** argv) {
  return wait_for_query();
}

int wait_for_query() {
  // Do some stuff
  return handle_query();
}

int handle_query() {
  // Handle it
  // if query is quit, return quit();
  return wait_for_query();
}

int quit() {
  return 0;
}

This implementation relies on call-chaining to achieve an "event loop". I use quotes because while it's logically an event loop, the call stack continually increases and never unwinds:

                                            wait_for_query____...
                                           /
                       handle_query_______/
                      /
wait_for_query_______/

While it works, it's always adding new stack frames onto the stack and eventually, after enough events, it will cause a Stack Overflow error! (haha so meta).

What I want is something like this:

                       handle_query           handle_query
                      /            \         /            \
wait_for_query_______/              \_______/              \_____...

What I've got so far

I've always heard that an OS is just a while(true) loop that gets interrupted, so (since my OS hasn't gotten a SO error recently) here's what I thought would be good:

  • Replace main with:

    while(1)
        if (wait_for_query() == 0) break;
    return 0;
    
  • Change handle_query's return to 1

But does this actually make it more stack efficient? To my knowledge, while loops (and loops in general) still produce stack frames in assembly language (since they're all branches in execution with scope/local variables/etc.)

What is the best implementation for an event loop in C/C++ that manages the call stack well?

  • Why?
  • Benchmarks/Comparisons?
  • Literature/Publications/Documentation?

Notes

This question assumes a single threaded event loop. Parallelized answers are acceptable too, but I thought that'd would be a bit much to ask about all at once ;)

Fire away

wspurgin
  • 2,600
  • 2
  • 17
  • 20
  • 2
    The while loop will not increase the amount of stack usage over time per iteration. – Neil Kirk Oct 24 '15 at 23:17
  • Branches don't produce stack frames, only calls. It's even possible that your optimizer uses branches for tail calls, to avoid the stack usage, but the language doesn't guarantee this optimization will take place. – Ben Voigt Oct 24 '15 at 23:37
  • You also may want to look at "Continuation-Passing Style". – Ben Voigt Oct 24 '15 at 23:37
  • Hmmm okay so at least I know the while loop will work and be nice on the stack, but is it the best solution? @BenVoigt thanks for the reference! – wspurgin Oct 24 '15 at 23:41
  • 1
    You want a solution for (the C language **or** the C++ language) or (the C language **and** the C++ language)? There is no `C/C++` language. – James Adkison Oct 24 '15 at 23:41
  • The recursive calls are insan... non-opimal and not used. Just loop around the message-retrieval call. – Martin James Oct 24 '15 at 23:42
  • You are worrying about something that you should not. It does not matter how you loop, while(true), for(;;) even goto. It's just not significant. – Martin James Oct 24 '15 at 23:43
  • You should also look at the most popular event loops: The Win32 event loop (`GetMessage` or `PeekMessage` or `MsgWaitForMultipleObjects`)+(`TranslateMessage`)+(`DispatchMessage`) and the X11 event loop (`XNextEvent`) for pure X11 and ('XtAppNextEvent' or `XtAppPeekEvent`)+('`XtAppProcessEvent` or `XtDispatchEvent`) with X Toolkit. Pretty much all higher level frameworks (.NET `Application.Run` for example) also use these internally in the usual way. – Ben Voigt Oct 24 '15 at 23:49
  • @JamesAdkison sorry C **or** C++ in case there was a solution in one language that wasn't possible in the other. – wspurgin Oct 25 '15 at 00:07
  • @MartinJames like I said, I was *curious* not *worried* ;) – wspurgin Oct 25 '15 at 00:07
  • OK - doesn't matter. Carry on programming:) – Martin James Oct 25 '15 at 00:11

2 Answers2

4

The initial solutions is fundamentally broken. Event loop looks something like this:

while (q != QUITE) {
  q = wait_for_query();
  handle_query(q);
}

It's as simple as this. Which actually agrees with what you described:

They're an easy concept: Wait for an event, handle event, wait for more events.

In initial code, semantically, handling the event, handle_query(), will never be completed until all future events are also completed, recursively, meaning no event will ever be completed. Which is not what you want.

The detailed might get very complicated, e.g how you get the events? does it blocks or not? how events are dispatched? ... etc.

  • 1
    I have a full EventLoop example on the question: [How would you implement a basic event-loop?](https://stackoverflow.com/a/67250018/4934640). – Evandro Coan Apr 25 '21 at 05:57
1

Actually, while won't generate any new stack frames by itself. Stack frames get created when a call instruction is issued.

If you make handle_query return 1 as you mention, then your loop won't grow the stack further than two levels (wait_query+handle_query) for each event it processes:

                                  handle_query           handle_query
                                 /            \         /            \
           wait_for_query_______/              \_______/              \_____...
          /
         /
main_loop

Which looks like the stack structure you were looking for.

pevasquez
  • 862
  • 8
  • 14