<?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>Swift Concurrency &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/swift-concurrency/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>Swift 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>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>
	</channel>
</rss>
