<?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>Task &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/task/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Wed, 21 Aug 2024 22:39:52 +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>Task &#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>Calling Swift Concurrency async code synchronously in Swift</title>
		<link>https://wadetregaskis.com/calling-swift-concurrency-async-code-synchronously-in-swift/</link>
					<comments>https://wadetregaskis.com/calling-swift-concurrency-async-code-synchronously-in-swift/#comments</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Wed, 21 Aug 2024 00:09:00 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[Sad]]></category>
		<category><![CDATA[Snafu]]></category>
		<category><![CDATA[spherical chicken in a vacuum]]></category>
		<category><![CDATA[Swift]]></category>
		<category><![CDATA[Swift Concurrency]]></category>
		<category><![CDATA[Task]]></category>
		<category><![CDATA[withoutActuallyEscaping]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=8351</guid>

					<description><![CDATA[Sometimes you just need to shove a round peg into a square hole. Sometimes that genuinely is the best option (or perhaps more accurately: the least bad option). I find my hand is often forced by APIs I don&#8217;t control (most often Apple&#8217;s APIs). e.g. data source or delegate callbacks that are synchronous and require&#8230; <a class="read-more-link" href="https://wadetregaskis.com/calling-swift-concurrency-async-code-synchronously-in-swift/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p>Sometimes you just need to shove a round peg into a square hole.  Sometimes that genuinely is the best option (or perhaps more accurately: the least bad option).</p>



<p>I find my hand is often forced by APIs I don&#8217;t control (most often Apple&#8217;s APIs).  e.g. data source or delegate callbacks that are synchronous<sup data-fn="fca8ab2f-af18-4e50-8bde-9aa3e28a1927" class="fn"><a href="#fca8ab2f-af18-4e50-8bde-9aa3e28a1927" id="fca8ab2f-af18-4e50-8bde-9aa3e28a1927-link">1</a></sup> and require you to return a value, but in order to obtain that value you have to run async code (perhaps because yet again that&#8217;s all you&#8217;re given by 3rd parties, or because that code makes sense to be async and is used happily as such in other places and you don&#8217;t want to have to duplicate it in perpetuity just to have a sync version).</p>



<p>If that asynchronosity is achieved through e.g. <a href="https://developer.apple.com/documentation/DISPATCH" data-wpel-link="external" target="_blank" rel="external noopener">GCD</a> or <a href="https://developer.apple.com/documentation/foundation/nsrunloop" data-wpel-link="external" target="_blank" rel="external noopener">NSRunLoop</a> or <a href="https://developer.apple.com/documentation/foundation/process" data-wpel-link="external" target="_blank" rel="external noopener">NSProcess</a> or <a href="https://developer.apple.com/documentation/foundation/nstask" data-wpel-link="external" target="_blank" rel="external noopener">NSTask</a> or <a href="https://developer.apple.com/documentation/foundation/nsthread" data-wpel-link="external" target="_blank" rel="external noopener">NSThread</a> or <a href="https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/pthread.3.html" data-wpel-link="external" target="_blank" rel="external noopener">pthreads</a>, it&#8217;s easy.  There are numerous ways to synchronously wait on their tasks.  In contrast, <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/" data-wpel-link="external" target="_blank" rel="external noopener">Swift Concurrency</a> <em>really</em> doesn&#8217;t want you to do this.  The language and standard library take an adamant idealogical position on this &#8211; one which is unfortunately impractical; a <a href="https://en.wikipedia.org/wiki/Spherical_cow" data-wpel-link="external" target="_blank" rel="external noopener">spherical chicken in a vacuum</a><sup data-fn="3f7186da-728e-41f5-b12b-6b7ca7456625" class="fn"><a href="#3f7186da-728e-41f5-b12b-6b7ca7456625" id="3f7186da-728e-41f5-b12b-6b7ca7456625-link">2</a></sup>.</p>



<p>Nonetheless, despite Swift&#8217;s best efforts to prevent me, I believe I&#8217;ve come up with a way to do this.  It appears to work reliably, given fairly extensive testing.  Nonetheless, I do not make any promises.  Use at your own risk.</p>



<p>If you know of a better way, please do let me know (e.g. in the comments below).</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)"><span role="button" tabindex="0" data-code="import Dispatch

extension Task {
    /// Executes the given async closure synchronously, waiting for it to finish before returning.
    ///
    /// **Warning**: Do not call this from a thread used by Swift Concurrency (e.g. an actor, including global actors like MainActor) if the closure - or anything it calls transitively via `await` - might be bound to that same isolation context.  Doing so may result in deadlock.
    static func sync(_ code: sending () async throws(Failure) -&gt; Success) throws(Failure) -&gt; Success { // 1
        let semaphore = DispatchSemaphore(value: 0)

        nonisolated(unsafe) var result: Result&lt;Success, Failure&gt;? = nil // 2

        withoutActuallyEscaping(code) { // 3
            nonisolated(unsafe) let sendableCode = $0 // 4

            let coreTask = Task&lt;Void, Never&gt;.detached(priority: .userInitiated) { @Sendable () async -&gt; Void in // 5
                do {
                    result = .success(try await sendableCode())
                } catch {
                    result = .failure(error as! Failure)
                }
            }

            Task&lt;Void, Never&gt;.detached(priority: .userInitiated) { // 6
                await coreTask.value
                semaphore.signal()
            }

            semaphore.wait()
        }

        return try result!.get() // 7
    }
}" style="color:#000000;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki light-plus" style="background-color: #FFFFFF" tabindex="0"><code><span class="line"><span style="color: #AF00DB">import</span><span style="color: #000000"> </span><span style="color: #267F99">Dispatch</span></span>
<span class="line"></span>
<span class="line"><span style="color: #0000FF">extension</span><span style="color: #000000"> </span><span style="color: #267F99">Task</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #008000">/// Executes the given async closure synchronously, waiting for it to finish before returning.</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #008000">///</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #008000">/// **Warning**: Do not call this from a thread used by Swift Concurrency (e.g. an actor, including global actors like MainActor) if the closure - or anything it calls transitively via `await` - might be bound to that same isolation context.  Doing so may result in deadlock.</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">static</span><span style="color: #000000"> </span><span style="color: #0000FF">func</span><span style="color: #000000"> </span><span style="color: #795E26">sync</span><span style="color: #000000">(</span><span style="color: #795E26">_</span><span style="color: #000000"> </span><span style="color: #001080">code</span><span style="color: #000000">: sending () </span><span style="color: #AF00DB">async</span><span style="color: #000000"> </span><span style="color: #AF00DB">throws</span><span style="color: #000000">(Failure) -&gt; Success) </span><span style="color: #AF00DB">throws</span><span style="color: #000000">(Failure) -&gt; Success { </span><span style="color: #008000">// 1</span></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #0000FF">let</span><span style="color: #000000"> semaphore = </span><span style="color: #795E26">DispatchSemaphore</span><span style="color: #000000">(</span><span style="color: #795E26">value</span><span style="color: #000000">: </span><span style="color: #098658">0</span><span style="color: #000000">)</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #795E26">nonisolated</span><span style="color: #000000">(unsafe) </span><span style="color: #0000FF">var</span><span style="color: #000000"> result: Result&lt;Success, Failure&gt;? = </span><span style="color: #0000FF">nil</span><span style="color: #000000"> </span><span style="color: #008000">// 2</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #795E26">withoutActuallyEscaping</span><span style="color: #000000">(code) { </span><span style="color: #008000">// 3</span></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #795E26">nonisolated</span><span style="color: #000000">(unsafe) </span><span style="color: #0000FF">let</span><span style="color: #000000"> sendableCode = </span><span style="color: #0000FF">$0</span><span style="color: #000000"> </span><span style="color: #008000">// 4</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">            </span><span style="color: #0000FF">let</span><span style="color: #000000"> coreTask = Task&lt;</span><span style="color: #267F99">Void</span><span style="color: #000000">, Never&gt;.</span><span style="color: #795E26">detached</span><span style="color: #000000">(</span><span style="color: #795E26">priority</span><span style="color: #000000">: .</span><span style="color: #001080">userInitiated</span><span style="color: #000000">) { </span><span style="color: #0000FF">@Sendable</span><span style="color: #000000"> () async -&gt; </span><span style="color: #267F99">Void</span><span style="color: #000000"> </span><span style="color: #AF00DB">in</span><span style="color: #000000"> </span><span style="color: #008000">// 5</span></span>
<span class="line"><span style="color: #000000">                </span><span style="color: #AF00DB">do</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">                    result = .</span><span style="color: #795E26">success</span><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"> </span><span style="color: #795E26">sendableCode</span><span style="color: #000000">())</span></span>
<span class="line"><span style="color: #000000">                } </span><span style="color: #AF00DB">catch</span><span style="color: #000000"> {</span></span>
<span class="line"><span style="color: #000000">                    result = .</span><span style="color: #795E26">failure</span><span style="color: #000000">(error as! Failure)</span></span>
<span class="line"><span style="color: #000000">                }</span></span>
<span class="line"><span style="color: #000000">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">            Task&lt;</span><span style="color: #267F99">Void</span><span style="color: #000000">, Never&gt;.</span><span style="color: #795E26">detached</span><span style="color: #000000">(</span><span style="color: #795E26">priority</span><span style="color: #000000">: .</span><span style="color: #001080">userInitiated</span><span style="color: #000000">) { </span><span style="color: #008000">// 6</span></span>
<span class="line"><span style="color: #000000">                </span><span style="color: #AF00DB">await</span><span style="color: #000000"> coreTask.</span><span style="color: #001080">value</span></span>
<span class="line"><span style="color: #000000">                semaphore.</span><span style="color: #795E26">signal</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">            }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">            semaphore.</span><span style="color: #795E26">wait</span><span style="color: #000000">()</span></span>
<span class="line"><span style="color: #000000">        }</span></span>
<span class="line"></span>
<span class="line"><span style="color: #000000">        </span><span style="color: #AF00DB">return</span><span style="color: #000000"> </span><span style="color: #AF00DB">try</span><span style="color: #000000"> result!.</span><span style="color: #795E26">get</span><span style="color: #000000">() </span><span style="color: #008000">// 7</span></span>
<span class="line"><span style="color: #000000">    }</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p>Elaborating on some of the odder or less than self-explanatory aspects of this:</p>



<ol class="wp-block-list">
<li>The closure parameter <em>must</em> be <code><a href="https://github.com/swiftlang/swift-evolution/blob/main/proposals/0430-transferring-parameters-and-results.md" data-wpel-link="external" target="_blank" rel="external noopener">sending</a></code> otherwise this deadlocks if e.g. you call it from the main thread (even if the closure, and all its transitive async calls, are not isolated to the main thread).  I don&#8217;t understand why this happens &#8211; it&#8217;s <em>possibly</em> explicable and working as intended, but <a href="https://github.com/swiftlang/swift/issues/75866" data-wpel-link="external" target="_blank" rel="external noopener">I wonder if it&#8217;s simply a bug</a>.  Nobody has been able to explain why it happens.<br><br>Note: in the initial version of this post I accidentally omitted this essential keyword.  I apologise for the error, and hope it didn&#8217;t cause grief for anyone.</li>



<li>Since there&#8217;s no sync way to retrieve the result of a <code><a href="https://developer.apple.com/documentation/swift/task" data-wpel-link="external" target="_blank" rel="external noopener">Task</a></code>, the result has to be passed out through a side-channel. The <code><a href="https://www.swift.org/blog/swift-5.10-released/#unsafe-opt-outs" data-wpel-link="external" target="_blank" rel="external noopener">nonisolated(unsafe)</a></code> is to silence the Swift 6 compiler&#8217;s erroneous error diagnostics about concurrent mutation of shared state.</li>



<li><code>Task</code> constructors only accept <a href="https://docs.swift.org/swift-book/documentation/the-swift-programming-language/closures/#Escaping-Closures" data-wpel-link="external" target="_blank" rel="external noopener">escaping closures</a>, even though as they&#8217;re used here the closure never <em>actually</em> escapes. Fortunately the <code><a href="https://developer.apple.com/documentation/swift/withoutactuallyescaping(_:do:)" data-wpel-link="external" target="_blank" rel="external noopener">withoutActuallyEscaping</a></code> escape hatch is available.</li>



<li><code>code</code> isn&#8217;t <code><a href="https://developer.apple.com/documentation/swift/sendable#Sendable-Functions-and-Closures" data-wpel-link="external" target="_blank" rel="external noopener">@Sendable</a></code> &#8211; since it doesn&#8217;t <em>actually</em> have to be sent in the sense of executing concurrently &#8211; so trying to use it in the <code>Task</code> closure below, which is <code>@Sendable</code>, results in an erroneous compiler error (&#8220;<code>Capture of 'code' with non-sendable type '() async throws(Failure) -&gt; Success' in a @Sendable closure</code>&#8220;). Assigning to a variable lets us apply <code>nonisolated(unsafe)</code> to disable the incorrect compiler diagnostic.</li>



<li>Several key aspects happen on this line:
<ul class="wp-block-list">
<li>It&#8217;s important to use a detached task, in case we&#8217;re already running in an isolated context (e.g. the MainActor) as we&#8217;re going to block the current thread waiting on the task to finish, via the semaphore.</li>



<li>The task logically needs to be run at the current task&#8217;s priority (or higher) in order to ensure it does actually run (re. priority inversion problems), although I&#8217;m not sure that technically matters here since we&#8217;re blocking in a non-await way anyway. One could use <code>Task.<a href="https://developer.apple.com/documentation/swift/task/currentpriority" data-wpel-link="external" target="_blank" rel="external noopener">currentPriority</a></code> here, but I&#8217;ve chosen to hard-code the highest priority (<code><a href="https://developer.apple.com/documentation/swift/taskpriority/userinitiated" data-wpel-link="external" target="_blank" rel="external noopener">userInitiated</a></code>) because it&#8217;s not great to block (in a non-await manner) on async code; although async code isn&#8217;t <em>necessarily</em> slow, I feel it&#8217;s wise to eliminate task prioritisation as a variable.</li>



<li>This closure must be explicitly marked as <code>@Sendable</code> as by default the compiler mistakenly infers it to be <em>not</em> <code>@Sendable</code>, even though all closure arguments to <code>Task</code> initialisers have to be <code>@Sendable</code>.  The compiler diagnostics in this case are frustratingly obtuse and misleading (although the sad saving grace is that this is a relatively common problem, so once you hit it enough times you start to develop a spidey sense for it).</li>
</ul>
</li>



<li>This otherwise pointless second <code>Task</code> is critical to prevent <code>withoutActuallyEscaping</code> from crashing.<br><br><code>withoutActuallyEscaping</code> basically relies on reference-counting &#8211; it records the retain count of its primary argument going in (<code>code</code> in this case) and compares that to the retain count going out &#8211; if they disagree, it crashes. There&#8217;s no way to disable or directly work around this<sup data-fn="ed8f4dd0-bc9b-4237-bf6f-fe6dad9cc344" class="fn"><a href="#ed8f4dd0-bc9b-4237-bf6f-fe6dad9cc344" id="ed8f4dd0-bc9b-4237-bf6f-fe6dad9cc344-link">3</a></sup>.<br><br>That&#8217;s a problem here because if we just signal the semaphore in the first task, right before exiting the task, we have a race &#8211; maybe the task <em>will</em> actually exit before the signal is acted on (<code>semaphore.<a href="https://developer.apple.com/documentation/dispatch/dispatchsemaphore/2016071-wait" data-wpel-link="external" target="_blank" rel="external noopener">wait</a>()</code> returns and allows execution to exit the <code>withoutActuallyEscaping</code> block), but maybe it won&#8217;t. Since the task is retaining the closure, it <em>must</em> exit before we wake up from the semaphore and exit the <code>withoutActuallyEscaping</code> block, otherwise crash.<br><br>The only way I found to <em>ensure</em> the problematic task has fully exited &#8211; after hours of experimenting, covering numerous methods &#8211; is to wait for it in a <em>second</em> task. Surprisingly, the second task &#8211; despite having a strong reference to the first task &#8211; seemingly doesn&#8217;t prevent the first task from being cleaned up. This makes me suspicious, but despite extensive testing I&#8217;m unable to get <code>withoutActuallyEscaping</code> to crash when using this workaround.</li>



<li>There&#8217;s no practical way to avoid this forced unwrap, even though it&#8217;s impossible for it to fail unless something goes <em>very</em> wrong with Swift&#8217;s built-ins (like <code>withoutActuallyEscaping</code> and <code>Task</code>).<br><br>If you don&#8217;t wish to use typed throws, you could unwrap it more gently and throw an error of your own type if it&#8217;s nil, but it&#8217;s extra work and a loss of type safety for something that realistically cannot happen.</li>
</ol>


<ol class="wp-block-footnotes"><li id="fca8ab2f-af18-4e50-8bde-9aa3e28a1927">Specifically meaning &#8220;not run through Swift Concurrency, as async functions / closures&#8221;.  Lots of APIs will execute the callback on the main thread, which is the most difficult case, but even those that execute on a user-specified GCD queue aren&#8217;t helpful here &#8211; at least, <a href="https://github.com/swiftlang/swift/issues/74626" data-wpel-link="external" target="_blank" rel="external noopener">not until <code>assumeIsolated</code> actually works</a>. <a href="#fca8ab2f-af18-4e50-8bde-9aa3e28a1927-link" aria-label="Jump to footnote reference 1">↩︎</a></li><li id="3f7186da-728e-41f5-b12b-6b7ca7456625">Incidentally, Wikipedia seems to think the canonical version of the joke is about spherical cows, but I&#8217;ve only ever heard it about chickens.  Indeed <a href="https://www.science.org/doi/10.1126/science.182.4119.1296.c" data-wpel-link="external" target="_blank" rel="external noopener">the very first known instance of the joke</a> used chickens, and all the pop-culture uses of it that I could find use chickens (most notably <a href="https://www.youtube.com/watch?v=Id0Ppz4OBKE" data-wpel-link="external" target="_blank" rel="external noopener">the ninth episode of The Big Bang Theory</a>). <a href="#3f7186da-728e-41f5-b12b-6b7ca7456625-link" aria-label="Jump to footnote reference 2">↩︎</a></li><li id="ed8f4dd0-bc9b-4237-bf6f-fe6dad9cc344">There&#8217;s no <code>unsafeWithoutActuallyEscaping</code> that forgoes the runtime checking, nor any environment variables or similar that influence it; the check is <em>always</em> included and cannot be disabled at runtime.  Even when it&#8217;s unnecessary or &#8211; as in this case &#8211; outright erroneous.<br><br>Nor is there a way to replicate <code>withoutActuallyEscaping</code>&#8216;s core functionality of merely adding <code>@escaping</code> to the closure&#8217;s signature (e.g. via <code>unsafeBitCast</code> or similar) because it&#8217;s a special interaction with the compiler&#8217;s escape checker which is evaluated purely at compile-time (whether a closure is escaping or not is not actually encoded into the output binary nor memory representation of closures &#8211; the only time escapingness ever leaks into the binary is when you use <code>withoutActuallyEscaping</code> and the compiler inserts the special runtime assertion). <a href="#ed8f4dd0-bc9b-4237-bf6f-fe6dad9cc344-link" aria-label="Jump to footnote reference 3">↩︎</a></li></ol>]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/calling-swift-concurrency-async-code-synchronously-in-swift/feed/</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">8351</post-id>	</item>
		<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>
