<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Event-Loop on Roberto Selbach</title><link>https://rselbach.com/tags/event-loop/</link><description>Recent content in Event-Loop on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Fri, 02 Mar 2007 20:16:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/event-loop/index.xml" rel="self" type="application/rss+xml"/><item><title>One Event Loop, One Owner</title><link>https://rselbach.com/one-event-loop-one-owner/</link><pubDate>Fri, 02 Mar 2007 20:16:00 +0000</pubDate><guid>https://rselbach.com/one-event-loop-one-owner/</guid><description>&lt;p&gt;A worker emitted a progress signal today, the label updated twice, and then the application hung while closing. The visible update tempted me to declare the cross-thread code correct. The shutdown was less charitable.&lt;/p&gt;
&lt;p&gt;Qt gives each &lt;code&gt;QObject&lt;/code&gt; thread affinity. Queued events for that object are processed by the event loop in its owning thread. A signal connected across threads normally becomes a queued call, so the receiver&amp;rsquo;s slot runs where the receiver lives. This permits a worker to report results without directly touching GUI objects.&lt;/p&gt;
&lt;p&gt;The safe shape is simple:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;connect(worker, SIGNAL(progress(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; dialog, SLOT(setProgress(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Qt&lt;span style="color:#f92672"&gt;::&lt;/span&gt;QueuedConnection);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The explicit connection type documents the boundary. The worker emits an integer; the dialog owns the label and updates it on the GUI thread.&lt;/p&gt;
&lt;p&gt;My hang came from destruction. The dialog waited for the worker while handling its close event, and the worker was waiting for a queued reply from the dialog&amp;rsquo;s event loop. The GUI event loop could not deliver the reply because it was blocked waiting. Both sides were admirably patient.&lt;/p&gt;
&lt;p&gt;I removed the synchronous callback. Closing now requests cancellation, disables the interface, and lets normal event processing continue until the worker announces completion. Only then is it destroyed. Data passed in signals is copied as needed, so custom value types must be registered if they cross a queued connection.&lt;/p&gt;
&lt;p&gt;I prefer message-like queued signals between worker and GUI code. They make ownership visible and avoid shared widget state. The caveat is that queued delivery is asynchronous: the sender cannot assume the slot has run, event loops must remain active, and ordering guarantees do not replace a proper state machine.&lt;/p&gt;
&lt;p&gt;Nested event loops are another temptation. Opening a modal operation or manually calling event processing can keep paints moving, but it also permits unrelated events to re-enter code whose invariants are half changed. I use that technique only with a very clear boundary, never as a general cure for long work.&lt;/p&gt;
&lt;p&gt;The practical test now closes the window at several stages of the job. Starting and finishing are easy; cancellation and teardown reveal who actually owns what. If a design works only while the user behaves, the user is not the unreliable component.&lt;/p&gt;</description></item><item><title>The Timer That Froze the Window</title><link>https://rselbach.com/the-timer-that-froze-the-window/</link><pubDate>Thu, 12 Jan 2006 21:14:00 +0000</pubDate><guid>https://rselbach.com/the-timer-that-froze-the-window/</guid><description>&lt;p&gt;I found today&amp;rsquo;s problem by dragging a window. The repaint stopped, the title bar sulked, and a progress label remained at 12 percent while the application did several seconds of perfectly respectable work.&lt;/p&gt;
&lt;p&gt;The work was running from a &lt;code&gt;QTimer&lt;/code&gt; slot. I had assumed that using a timer made it asynchronous. It does not. A timer merely arranges for a callback to be delivered by the event loop; the callback still runs on that loop&amp;rsquo;s thread. If the slot computes for five seconds, paint events wait five seconds. So do mouse events and, more importantly, the user&amp;rsquo;s patience.&lt;/p&gt;
&lt;p&gt;The quick diagnostic was wonderfully primitive:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Scanner&lt;span style="color:#f92672"&gt;::&lt;/span&gt;scanNext()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qDebug() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;enter scanNext&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scanEverything();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; qDebug() &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;leave scanNext&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The pause sat between those messages. Splitting the operation into bounded pieces fixed the immediate problem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Scanner&lt;span style="color:#f92672"&gt;::&lt;/span&gt;scanNext()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;50&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; hasMore(); &lt;span style="color:#f92672"&gt;++&lt;/span&gt;i)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scanOne();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (hasMore())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; QTimer&lt;span style="color:#f92672"&gt;::&lt;/span&gt;singleShot(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;, SLOT(scanNext()));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A zero-duration timer does not promise instant execution. It puts more work back into event processing, allowing pending paints and input to run between batches. The batch size is deliberately boring: small enough to keep the interface alive, large enough not to drown in scheduling overhead.&lt;/p&gt;
&lt;p&gt;I prefer this incremental approach when the job naturally divides into independent records. It keeps all GUI objects on their owning thread and makes cancellation straightforward. The caveat is that a single expensive record still blocks everything. In that case the computation belongs on a worker thread, with results sent back rather than widgets touched directly.&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;QCoreApplication::processEvents()&lt;/code&gt; inside the loop is tempting, but it permits other callbacks to enter while the current operation is unfinished. That can expose partially updated state or let the user start the same action twice. Returning to the event loop between explicit batches gives me a cleaner boundary: each batch leaves the object consistent before yielding.&lt;/p&gt;
&lt;p&gt;That boundary also gives cancellation a predictable checkpoint between records, rather than during an arbitrary mutation.&lt;/p&gt;
&lt;p&gt;Qt 4&amp;rsquo;s event loop is not magic concurrency dust. This is disappointing only until one remembers that magic concurrency dust would probably deadlock before breakfast.&lt;/p&gt;</description></item></channel></rss>