<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:media="http://search.yahoo.com/mrss/"
>

<channel>
	<title>Structured Concurrency &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/structured-concurrency/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Mon, 01 Jan 2024 22:46:06 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://wadetregaskis.com/wp-content/uploads/2016/03/Stitch-512x512-1-256x256.png</url>
	<title>Structured Concurrency &#8211; Wade Tregaskis</title>
	<link>https://wadetregaskis.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">226351702</site>	<item>
		<title>Performing a delayed and/or repeating operation in a Swift Actor</title>
		<link>https://wadetregaskis.com/performing-a-delayed-and-or-repeating-operation-in-a-swift-actor/</link>
					<comments>https://wadetregaskis.com/performing-a-delayed-and-or-repeating-operation-in-a-swift-actor/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Sat, 30 Jul 2022 19:50:07 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Howto]]></category>
		<category><![CDATA[Actor]]></category>
		<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[Dispatch]]></category>
		<category><![CDATA[DispatchQueue]]></category>
		<category><![CDATA[Executor]]></category>
		<category><![CDATA[Grand Central Dispatch]]></category>
		<category><![CDATA[RunLoop]]></category>
		<category><![CDATA[Structured Concurrency]]></category>
		<category><![CDATA[Swift]]></category>
		<category><![CDATA[Task]]></category>
		<category><![CDATA[Thread]]></category>
		<category><![CDATA[Timer]]></category>
		<guid isPermaLink="false">https://blog.wadetregaskis.com/?p=5155</guid>

					<description><![CDATA[Say you want to perform some operation after a delay, and/or at regular intervals, inside a Swift actor &#8211; maybe your actor represents a weather station and you want to periodically fetch the latest weather information, automatically. How do you do that? You could punt the problem to some other code, outside the actor &#8211;&#8230; <a class="read-more-link" href="https://wadetregaskis.com/performing-a-delayed-and-or-repeating-operation-in-a-swift-actor/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>Say you want to perform some operation after a delay, and/or at regular intervals, inside a Swift actor &#8211; maybe your actor represents a weather station and you want to periodically fetch the latest weather information, automatically.  How do you do that?</p>



<p>You <em>could</em> punt the problem to some other code, outside the actor &#8211; i.e. make something <em>else</em> periodically call some method on the actor.  Sometimes this is a suitable approach, but sometimes you just want your actor to be self-contained and not require external aid.  And if the work takes time anyway (e.g. network activity) what&#8217;s the point of having some external entity calling in and having to wait on your actor &#8211; easier if your actor just notifies the external entity whenever new data is available.</p>



<h2 class="wp-block-heading">Timer ❌</h2>



<p>You might think to just use <a href="https://developer.apple.com/documentation/foundation/timer" data-type="URL" data-id="https://developer.apple.com/documentation/foundation/timer" data-wpel-link="external" target="_blank" rel="external noopener"><code>Timer</code></a> (formerly <code>NSTimer</code>) &#8211; and most of the internet would strongly encourage you to do so, based on the search results you&#8217;ll find for this topic.</p>



<p>Except it doesn&#8217;t work.</p>



<p>Timer relies on <a href="https://developer.apple.com/documentation/foundation/runloop/" data-type="URL" data-id="https://developer.apple.com/documentation/foundation/runloop/" target="_blank" rel="noreferrer noopener external" data-wpel-link="external"><code>RunLoop</code></a> (<code>NSRunLoop</code>) to actually schedule itself and be executed.  <code>RunLoop</code>s are <em>normally</em> created automatically in most cases and kind of just work magically in the background.</p>



<p>Actors don&#8217;t have runloops.  More accurately &#8211; the magical, mutually-exclusive context in which an actor runs is not a <code>RunLoop</code> and does not interact with <code>RunLoop</code>s.  Actors use a relatively new <a href="https://developer.apple.com/documentation/swift/executor/" target="_blank" rel="noreferrer noopener external" data-wpel-link="external"><code>Executor</code></a> class family, but at least for now (2022) those are basically useless &#8211; literally the only method they have is to add a task to be executed as soon as possible. They provide no way to schedule a task after a delay.</p>



<p>Confusingly, if you access <a href="https://developer.apple.com/documentation/foundation/runloop/1412291-current" data-type="URL" data-id="https://developer.apple.com/documentation/foundation/runloop/1412291-current" target="_blank" rel="noreferrer noopener external" data-wpel-link="external"><code>RunLoop.current</code></a> from an actor context, it <em>will</em> return some non-nil value, but it&#8217;s useless because nothing ever <em>runs</em> that particular <code>RunLoop</code>.  And how would you run it yourself &#8211; calling its <a href="https://developer.apple.com/documentation/foundation/runloop/1412430-run" data-type="URL" data-id="https://developer.apple.com/documentation/foundation/runloop/1412430-run" target="_blank" rel="noreferrer noopener external" data-wpel-link="external">run</a> method from within the actor&#8217;s context would block the actor indefinitely and cause deadlock.</p>



<p>You could use the <a rel="noreferrer noopener external" href="https://developer.apple.com/documentation/foundation/runloop/1418388-main/" data-type="URL" data-id="https://developer.apple.com/documentation/foundation/runloop/1418388-main/" target="_blank" data-wpel-link="external">main</a> runloop that most Swift apps have by default, but you shouldn&#8217;t &#8211; that main runloop is intended for user interaction <em>only</em>.  You should never put random background tasks on it, as they can interfere with your UI and make your app stutter.</p>



<p>You could spawn your own <a href="https://developer.apple.com/documentation/foundation/thread" data-type="URL" data-id="https://developer.apple.com/documentation/foundation/thread" target="_blank" rel="noreferrer noopener external" data-wpel-link="external"><code>Thread</code></a> to actually run a <code>RunLoop</code>, but it&#8217;s unsafe &#8211; you cannot interact with any runloop except the <em>current</em> runloop &#8211; i.e. you have to be in a runloop in order to touch it &#8211; and you can&#8217;t get <em>into</em> such a runloop in an easy way from an actor context.  Seemingly obvious and innocuous methods like calling <code>RunLoop.current</code> from actor context are dangerous because they&#8217;re not guaranteed to return the <em>same</em> runloop each time.  Instead, you have to explicitly bridge to the runloop via some bespoke message system that you have to pre-install into that runloop.  Ick.</p>



<p>It&#8217;s also very inefficient, if you don&#8217;t actually need to use a whole CPU core most of the time &#8211; you&#8217;ll need a dedicated thread for every actor instance, and threads aren&#8217;t completely free to create &amp; maintain &#8211; plus there&#8217;s a limit to how many you can have alive at one time.</p>



<h2 class="wp-block-heading">Dispatch ✔️</h2>



<p>A better approach is to use <a href="https://developer.apple.com/documentation/dispatch" data-type="URL" data-id="https://developer.apple.com/documentation/dispatch" target="_blank" rel="noreferrer noopener external" data-wpel-link="external">Dispatch</a> (formerly Grand Central Dispatch) &#8211; specifically the convenient methods on <a href="https://developer.apple.com/documentation/dispatch/dispatchqueue" data-type="URL" data-id="https://developer.apple.com/documentation/dispatch/dispatchqueue" target="_blank" rel="noreferrer noopener external" data-wpel-link="external"><code>DispatchQueue</code></a> that allow you to set up delayed and/or scheduled tasks.  There&#8217;s the various async… methods for doing something once after a certain time period, and also schedule… methods which also support recurring, cancellable timers.  Granted they aren&#8217;t tied to the actor&#8217;s context &#8211; you still have to bridge back into the actor via <a href="https://developer.apple.com/documentation/swift/task/" data-type="URL" data-id="https://developer.apple.com/documentation/swift/task/" target="_blank" rel="noreferrer noopener external" data-wpel-link="external"><code>Task</code></a> or similar &#8211; but at least they actually work.</p>



<p>e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">let</span><span style="color: #000000"> timer = DispatchQueue</span></span>
<span class="line"><span style="color: #000000">    .</span><span style="color: #795E26">global</span><span style="color: #000000">(</span><span style="color: #795E26">qos</span><span style="color: #000000">: .</span><span style="color: #001080">utility</span><span style="color: #000000">)</span></span>
<span class="line"><span style="color: #000000">    .</span><span style="color: #795E26">schedule</span><span style="color: #000000">(</span><span style="color: #795E26">after</span><span style="color: #000000">: DispatchQueue.</span><span style="color: #795E26">SchedulerTimeType</span><span style="color: #000000">(.</span><span style="color: #795E26">now</span><span style="color: #000000">()),</span></span>
<span class="line"><span style="color: #000000">              </span><span style="color: #795E26">interval</span><span style="color: #000000">: .</span><span style="color: #795E26">seconds</span><span style="color: #000000">(refreshInterval),</span></span>
<span class="line"><span style="color: #000000">              </span><span style="color: #795E26">tolerance</span><span style="color: #000000">: .</span><span style="color: #795E26">seconds</span><span style="color: #000000">(refreshInterval / </span><span style="color: #098658">5</span><span style="color: #000000">)) { [</span><span style="color: #0000FF">weak</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">] </span><span style="color: #AF00DB">in</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">guard</span><span style="color: #000000"> </span><span style="color: #0000FF">let</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000"> </span><span style="color: #AF00DB">else</span><span style="color: #000000"> { </span><span style="color: #AF00DB">return</span><span style="color: #000000"> }</span></span>
<span class="line"><span style="color: #000000">    Task { </span><span style="color: #AF00DB">await</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #795E26">getLatestMeasurement</span><span style="color: #000000">() } </span><span style="color: #008000">// Trampoline back into the actor context.</span></span>
<span class="line"><span style="color: #000000">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #008000">// Keep &quot;timer&quot; around as e.g. a member variable if you want to be able to cancel it.</span></span></code></pre></div>



<p>If you use the schedule… methods you may need to import Combine, as the <code>Cancellable</code> type that they return is actually defined in Combine.  But you don&#8217;t have to actually use Combine otherwise.</p>



<h2 class="wp-block-heading">Structured Concurrency (<code>Task</code>) ✔️</h2>



<p>An alternative approach is to just use Swift Concurrency primitives.  This is arguably &#8220;purer&#8221; &#8211; no need to pull in the Dispatch &amp; Combine libraries &#8211; but requires a bit more manual labour if you need to support cancellation.</p>



<p>Basically you can create a new <code>Task</code> which just sits in a loop running the desired operation endlessly (or until cancelled), manually sleeping between executions.</p>



<p>e.g.:</p>



<div class="wp-block-kevinbatdorf-code-block-pro padding-disabled" data-code-block-pro-font-family="" style="font-size:.875rem;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #001080">timer</span><span style="color: #000000"> = </span><span style="color: #795E26">Task</span><span style="color: #000000">(</span><span style="color: #795E26">priority</span><span style="color: #000000">: .</span><span style="color: #001080">utility</span><span style="color: #000000">) { [</span><span style="color: #0000FF">weak</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">] </span><span style="color: #AF00DB">in</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">while</span><span style="color: #000000"> !Task.</span><span style="color: #001080">isCancelled</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">          </span><span style="color: #AF00DB">guard</span><span style="color: #000000"> </span><span style="color: #0000FF">let</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000"> </span><span style="color: #AF00DB">else</span><span style="color: #000000"> { </span><span style="color: #AF00DB">return</span><span style="color: #000000"> }</span></span>
<span class="line"><span style="color: #000000">          </span><span style="color: #AF00DB">await</span><span style="color: #000000"> </span><span style="color: #0000FF">self</span><span style="color: #000000">.</span><span style="color: #795E26">getLatestMeasurement</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">          </span><span style="color: #AF00DB">try</span><span style="color: #000000">? </span><span style="color: #AF00DB">await</span><span style="color: #000000"> Task.</span><span style="color: #795E26">sleep</span><span style="color: #000000">(</span><span style="color: #795E26">nanoseconds</span><span style="color: #000000">: </span><span style="color: #267F99">UInt64</span><span style="color: #000000">(refreshInterval * </span><span style="color: #098658">1_000_000_000</span><span style="color: #000000">))</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>It doesn&#8217;t look too bad, but keep in mind this loses some important functionality vs the Dispatch approach &#8211; there&#8217;s no way to specify a tolerance to the timer, so this is not as good for energy efficiency (in cases where you don&#8217;t need <em>exact</em> timing, which is most cases).</p>



<p>There is <a href="https://developer.apple.com/documentation/swift/task/sleep(until:tolerance:clock:)" data-type="URL" data-id="https://developer.apple.com/documentation/swift/task/sleep(until:tolerance:clock:)" target="_blank" rel="noreferrer noopener external" data-wpel-link="external">a new variant of the <code>Task.sleep</code> method</a> potentially coming in iOS 16, macOS 13, etc which does allow you to specify a tolerance, but that of course requires a minimum deployment target of essentially 2023 or later.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/performing-a-delayed-and-or-repeating-operation-in-a-swift-actor/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">5155</post-id>	</item>
	</channel>
</rss>
