<?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>Concurrency &#8211; Wade Tregaskis</title>
	<atom:link href="https://wadetregaskis.com/tags/concurrency/feed/" rel="self" type="application/rss+xml" />
	<link>https://wadetregaskis.com</link>
	<description></description>
	<lastBuildDate>Tue, 23 Jan 2024 23:50:11 +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>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>NSImage is dangerous</title>
		<link>https://wadetregaskis.com/nsimage-is-dangerous/</link>
					<comments>https://wadetregaskis.com/nsimage-is-dangerous/#comments</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Tue, 23 Jan 2024 21:53:57 +0000</pubDate>
				<category><![CDATA[Coding]]></category>
		<category><![CDATA[AppKit]]></category>
		<category><![CDATA[Apple]]></category>
		<category><![CDATA[Broken by design]]></category>
		<category><![CDATA[Concurrency]]></category>
		<category><![CDATA[NSBitmapImageRep]]></category>
		<category><![CDATA[NSImage]]></category>
		<category><![CDATA[Sad]]></category>
		<category><![CDATA[SwiftUI]]></category>
		<category><![CDATA[Undocumented]]></category>
		<category><![CDATA[unsafe]]></category>
		<guid isPermaLink="false">https://wadetregaskis.com/?p=7501</guid>

					<description><![CDATA[NSImage is formally documented as largely not thread-safe: The following classes and functions are generally not thread-safe. In most cases, you can use these classes from any thread as long as you use them from only one thread at a time. Check the class documentation for additional details. Apple&#8217;s Threading Programming Guide &#62; Appendix A:&#8230; <a class="read-more-link" href="https://wadetregaskis.com/nsimage-is-dangerous/" data-wpel-link="internal">Read more</a>]]></description>
										<content:encoded><![CDATA[
<p><code><a href="https://developer.apple.com/documentation/appkit/nsimage" data-wpel-link="external" target="_blank" rel="external noopener">NSImage</a></code> is <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123351-BBCFIIEB" data-wpel-link="external" target="_blank" rel="external noopener">formally documented</a> as largely <em>not</em> thread-safe:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>The following classes and functions are generally not thread-safe. In most cases, you can use these classes from any thread as long as you use them from only one thread at a time. Check the class documentation for additional details.</p>
<cite>Apple&#8217;s <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/Introduction/Introduction.html#//apple_ref/doc/uid/10000057i-CH1-SW1" data-wpel-link="external" target="_blank" rel="external noopener">Threading Programming Guide</a> &gt; <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-SW1" data-wpel-link="external" target="_blank" rel="external noopener">Appendix A: Thread Safety Summary</a>, subsection <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123351-BBCFIIEB" data-wpel-link="external" target="_blank" rel="external noopener">Application Kit Framework Thread Safety</a></cite></blockquote>



<p>What &#8220;in most cases&#8221; means is left to the reader&#8217;s imagination.  Apple adds a little addendum for <code>NSImage</code> specifically:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>One thread can create an <code><a href="https://developer.apple.com/documentation/appkit/nsimage" data-wpel-link="external" target="_blank" rel="external noopener">NSImage</a></code> object, draw to the image buffer, and pass it off to the main thread for drawing. The underlying image cache is shared among all threads. For more information about images and how caching works, see <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003290" data-wpel-link="external" target="_blank" rel="external noopener">Cocoa Drawing Guide</a>. </p>
<cite><a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-126728" data-wpel-link="external" target="_blank" rel="external noopener">NSImage Restrictions</a></cite></blockquote>



<p>For a start, it&#8217;s talking only about <em>creating an NSImage from scratch</em>, not loading it from serialised form (e.g. a file, a pasteboard, etc).  It doesn&#8217;t even deign to mention those other, much more common cases.</p>



<p>And even for that one mentioned use case, what does it mean, exactly?  What &#8220;image cache&#8221; is it referring to?</p>



<p>I don&#8217;t have authoritative answers.  The documentation is so infuriatingly vague that Apple could do basically anything to the implementation, between macOS updates, and claim to have broken no promises.</p>



<p>What I do have is some empirical data and the results of some reverse engineering (shout out to <a href="https://www.hopperapp.com" data-wpel-link="external" target="_blank" rel="external noopener">Hopper</a>), from macOS 14.2 Sonoma.</p>



<h2 class="wp-block-heading">NSImage 101</h2>



<p><code>NSImage</code>s can represent a wide range of imagery.  Most uses of them are probably for bitmap data (i.e. what you find in common image formats like <a href="https://en.wikipedia.org/wiki/WebP" data-wpel-link="external" target="_blank" rel="external noopener">WebP</a> &amp; <a href="https://en.wikipedia.org/wiki/AVIF" data-wpel-link="external" target="_blank" rel="external noopener">AVIF</a>), but <code>NSImage</code> also supports &#8216;raw&#8217; images (e.g. <a href="https://www.nikonusa.com/learn-and-explore/c/products-and-innovation/nikon-electronic-format-nef" data-wpel-link="external" target="_blank" rel="external noopener">Nikon NEF</a>) as well as vector data (e.g. <a href="https://en.wikipedia.org/wiki/SVG" data-wpel-link="external" target="_blank" rel="external noopener">SVG</a> &amp; <a href="https://en.wikipedia.org/wiki/PDF" data-wpel-link="external" target="_blank" rel="external noopener">PDF</a>).  It also has a plug-in mechanism of sorts, so the supported image formats can be extended dynamically at runtime.</p>



<p>You can fetch the full list of supported types from <a href="https://developer.apple.com/documentation/appkit/nsimage/1519988-imagetypes" data-wpel-link="external" target="_blank" rel="external noopener">NSImage.imageTypes</a> &#8211; although it doesn&#8217;t distinguish between those it can read vs those it can write.  Fortunately, <code>sips --formats</code> in Terminal gives you the same list with additional metadata.  On my machine that list happens to be:</p>



<pre class="wp-block-preformatted">Supported Formats:
-------------------------------------------
com.adobe.pdf                pdf   Writable
com.adobe.photoshop-image    psd   Writable
com.adobe.raw-image          dng   
com.apple.atx                --    Writable
com.apple.icns               icns  Writable
com.apple.pict               pict  
com.canon.cr2-raw-image      cr2   
com.canon.cr3-raw-image      cr3   
com.canon.crw-raw-image      crw   
com.canon.tif-raw-image      tif   
com.compuserve.gif           gif   Writable
com.dxo.raw-image            dxo   
com.epson.raw-image          erf   
com.fuji.raw-image           raf   
com.hasselblad.3fr-raw-image 3fr   
com.hasselblad.fff-raw-image fff   
com.ilm.openexr-image        exr   Writable
com.kodak.raw-image          dcr   
com.konicaminolta.raw-image  mrw   
com.leafamerica.raw-image    mos   
com.leica.raw-image          raw   
com.leica.rwl-raw-image      rwl   
com.microsoft.bmp            bmp   Writable
com.microsoft.cur            --    
com.microsoft.dds            dds   Writable
com.microsoft.ico            ico   Writable
com.nikon.nrw-raw-image      nrw   
com.nikon.raw-image          nef   
com.olympus.or-raw-image     orf   
com.olympus.raw-image        orf   
com.olympus.sr-raw-image     orf   
com.panasonic.raw-image      raw   
com.panasonic.rw2-raw-image  rw2   
com.pentax.raw-image         pef   
com.phaseone.raw-image       iiq   
com.samsung.raw-image        srw   
com.sgi.sgi-image            sgi   
com.sony.arw-raw-image       arw   
com.sony.raw-image           srf   
com.sony.sr2-raw-image       sr2   
com.truevision.tga-image     tga   Writable
org.khronos.astc             astc  Writable
org.khronos.ktx              ktx   Writable
org.khronos.ktx2             --    Writable
org.webmproject.webp         webp  
public.avci                  avci  
public.avif                  avif  
public.avis                  --    
public.heic                  heic  Writable
public.heics                 heics Writable
public.heif                  heif  
public.jpeg                  jpeg  Writable
public.jpeg-2000             jp2   Writable
public.jpeg-xl               jxl   
public.mpo-image             mpo   
public.pbm                   pbm   Writable
public.png                   png   Writable
public.pvr                   pvr   Writable
public.radiance              pic   
public.svg-image             svg   
public.tiff                  tiff  Writable</pre>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>Sidenote:  notice how it supports modern formats like WebP, AVIF, and JPEG-XL, but <em>only</em> for reading, not writing. 😕</p>



<p>It used to be that <code>NSImage</code> was pretty much a one-stop-shop for typical app image I/O needs, but Apple have for some reason crippled it over the years.  I feel like this is part of a larger trend of Apple providing increasingly less functionality, more complexity, and less coherence in their frameworks.</p>
</div></div>



<p>An <code>NSImage</code> can have multiple &#8216;<a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Images/Images.html#//apple_ref/doc/uid/TP40003290-CH208-SW9" data-wpel-link="external" target="_blank" rel="external noopener">representations</a>&#8216;.  These are essentially just different versions of the image.  One representation might be the original vector form (e.g. an <code><a href="https://developer.apple.com/documentation/appkit/nspdfimagerep" data-wpel-link="external" target="_blank" rel="external noopener">NSPDFImageRep</a></code>).  Another might be a rasterisation of that at a certain resolution.  Yet another might be a rasterisation at a different resolution.</p>



<p>It gets more complicated, however, because some representations are merely proxies for <em>other</em> frameworks&#8217; representations, like <a href="https://developer.apple.com/documentation/coregraphics/cgimage/" data-wpel-link="external" target="_blank" rel="external noopener"><code>CGImage</code></a>, <a href="https://developer.apple.com/documentation/coreimage/ciimage/" data-wpel-link="external" target="_blank" rel="external noopener"><code>CIImage</code></a> or somesuch.</p>



<p>Nonetheless, for the simple case of a static bitmap image read from a file, generally <code>NSImage</code> produces just one representation, which is the full bitmap (as an <code><a href="https://developer.apple.com/documentation/appkit/nsbitmapimagerep" data-wpel-link="external" target="_blank" rel="external noopener">NSBitmapImageRep</a></code>).  That&#8217;s the only case I&#8217;m going to discuss here, for simplicity&#8217;s sake (though likely the lessons apply to the other cases too).</p>



<h2 class="wp-block-heading">Accessing / using an NSImage</h2>



<p>Generally to use an <code>NSImage</code> you need a bitmap representation.  e.g. to actually draw it to the screen.</p>



<p>Unless you manually create an <code>NSImage</code> from an in-memory bitmap, the bitmap representation is not loaded initially, but rather only when first needed.</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>☝️ You can usually obtain the <code>NSBitmapImageRep</code> (via e.g. <code><a href="https://developer.apple.com/documentation/appkit/nsimage/1519961-bestrepresentation" data-wpel-link="external" target="_blank" rel="external noopener">bestRepresentation(for: .infinity, context: nil, hints: nil)</a></code>) even before it&#8217;s actually been loaded &#8211; initially it&#8217;s largely just a shell, that knows its metadata (e.g. dimensions and colour space) but not yet its actual imagery.</p>
</div></div>



<p>Ultimately the load is triggered when something asks for the raw bytes of the bitmap (either you, directly in your code, or indirectly when you e.g. ask AppKit to draw the image).</p>



<p>Fetching the raw bytes of a bitmap from <code>NSBitmapImageRep</code> is not a trivial exercise.  I&#8217;m going to gloss over the complexities (like planar data formats) and just talk about the common case of single-plane (&#8220;interleaved channels&#8221;) bitmaps.</p>



<p>For those, you can access the raw bytes via the <a href="https://developer.apple.com/documentation/appkit/nsbitmapimagerep/1395421-bitmapdata" data-wpel-link="external" target="_blank" rel="external noopener"><code>bitmapData</code></a> property.</p>



<p>Nominally, <code>bitmapData</code> just returns a <code>uint8_t*</code>.</p>



<p>In fact, <code>bitmapData</code> calls a <em>lot</em> of internal methods in a complicated fashion, with many possible code paths.  What exactly it does depends on the underlying source of data, but in a nutshell it checks if the desired data has already been created / loaded (it&#8217;s cached in an instance variable) and if not it loads it, e.g. by calling <code><a href="https://developer.apple.com/documentation/imageio/1465011-cgimagesourcecreateimageatindex" data-wpel-link="external" target="_blank" rel="external noopener">CGImageSourceCreateImageAtIndex</a></code>.</p>



<p>That&#8217;s what makes <code>NSImage</code> dangerous to use from multiple threads simultaneously.</p>



<figure class="wp-block-pullquote"><blockquote><p>There is no mutual exclusion protecting any of these code paths.</p></blockquote></figure>



<p>Reading the <em>cached</em> bitmap data is essentially read-only (just some retain/autorelease traffic) so it&#8217;s safe to call from multiple threads concurrently (as long as something ensures the <code>NSBitmapImageRep</code> is kept alive the whole time <em>and not mutated</em>)</p>



<p>But loading or modifying it is not.  As such, if you call <code>bitmapData</code> from multiple threads concurrently, and you don&#8217;t know for sure that it&#8217;s already fully loaded, you get a <a href="https://www.avanderlee.com/swift/race-condition-vs-data-race/" data-wpel-link="external" target="_blank" rel="external noopener">data race</a> (also known as a &#8220;WTF does my app crash randomly?!&#8221; condition).</p>



<p>The consequences of that race vary.  Maybe you &#8220;win&#8221; the race &#8211; one thread happens to run virtually to completion of <code>bitmapData</code> first, storing the fresh backing data into the caching instance member, and then all the other threads run and just return that same value &#8211; the ideal situation as everything works as intended.</p>



<p>Maybe you &#8220;lose&#8221; the race: every concurrent thread checks simultaneously and sees there&#8217;s no cached value, so they all &#8211; in parallel and redundant to each other &#8211; load the bitmap data and store it into the cache.  They each return the one they created, even though ultimately only one thread wins &#8211; the <em>last</em> one to write into the cache &#8211; and the duplicate bitmap data that all the earlier threads created is deallocated.  Even though pointers to them have been returned to you.  And you might be in the middle of using them.  Causing you to crash with a memory protection fault (or worse, read from some random other memory allocation that happened to be placed at the same address afterwards, reading essentially garbage).</p>



<p>Hypocritically, this is because Apple don&#8217;t follow <a href="https://developer.apple.com/documentation/swift/calling-functions-with-pointer-parameters#Pass-a-Constant-Pointer-as-a-Parameter" data-wpel-link="external" target="_blank" rel="external noopener">their own rules on escaping pointers</a>:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>The pointer you pass to the function is only guaranteed to be valid for the duration of the function call. Do not persist the pointer and access it after the function has returned.</p>
<cite>Apple&#8217;s <a href="https://developer.apple.com/documentation/swift/swift-standard-library" data-wpel-link="external" target="_blank" rel="external noopener">Swift Standard Library</a> &gt; <a href="https://developer.apple.com/documentation/swift/manual-memory-management" data-wpel-link="external" target="_blank" rel="external noopener">Manual Memory Management</a> &gt; <a href="https://developer.apple.com/documentation/swift/calling-functions-with-pointer-parameters" data-wpel-link="external" target="_blank" rel="external noopener">Calling Functions With Pointer Parameters</a>, subsection <a href="https://developer.apple.com/documentation/swift/calling-functions-with-pointer-parameters#Pass-a-Constant-Pointer-as-a-Parameter" data-wpel-link="external" target="_blank" rel="external noopener">Pass a Constant Pointer as a Parameter</a>.</cite></blockquote>



<p>The source code for the relevant <code>NSBitmapImageRep</code> methods is essentially:</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:4;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: #000000">- (</span><span style="color: #0000FF">uint8_t</span><span style="color: #000000">*)bitmapData {</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #0000FF">uint8_t</span><span style="color: #000000"> **result = </span><span style="color: #0000FF">nil</span><span style="color: #000000">;</span></span>
<span class="line"><span style="color: #000000">    [</span><span style="color: #0000FF">self</span><span style="color: #000000"> </span><span style="color: #795E26">getBitmapDataPlanes:</span><span style="color: #000000">&amp;result];</span></span>
<span class="line"><span style="color: #000000">    </span><span style="color: #AF00DB">return</span><span style="color: #000000"> *result;</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: #0000FF">void</span><span style="color: #000000">)getBitmapDataPlanes:(</span><span style="color: #0000FF">uint8_t</span><span style="color: #000000">***)output {</span></span>
<span class="line"><span style="color: #000000">    [</span><span style="color: #0000FF">self</span><span style="color: #000000"> </span><span style="color: #795E26">_performBlockUsingBackingMutableData:</span><span style="color: #000000">^</span><span style="color: #795E26">void</span><span style="color: #000000"> (</span><span style="color: #0000FF">uint8_t</span><span style="color: #000000">* </span><span style="color: #001080">dataPlanes</span><span style="color: #000000">[</span><span style="color: #098658">5</span><span style="color: #000000">]) {</span></span>
<span class="line"><span style="color: #000000">        *output = dataPlanes;</span></span>
<span class="line"><span style="color: #000000">    }];</span></span>
<span class="line"><span style="color: #000000">}</span></span></code></pre></div>



<p class="has-text-align-center has-x-large-font-size">🤦‍♂️</p>



<figure class="wp-block-pullquote"><blockquote><p>The <code>bitmapData</code> property and <code>getBitmapDataPlanes</code> methods are fundamentally unsafe.</p></blockquote></figure>



<p>Unfortunately, while <code>_performBlockUsingBackingMutableData</code> and its many similar siblings <em>can</em> be made safe, they are (a) all private and (b) not currently enforcing the necessary mutual exclusion.  This could be corrected by Apple in future (although I wouldn&#8217;t hold your breath).</p>



<div class="wp-block-group"><div class="wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained">
<p>Nominally the <code><a href="https://developer.apple.com/documentation/appkit/nsbitmapimagerep/1395583-colorat" data-wpel-link="external" target="_blank" rel="external noopener">colorAt(x:y:)</a></code> method could also be safe, but it currently lacks the same underlying mutual exclusion &#8211; and even if it didn&#8217;t, the performance when using it is atrocious due to the Objective-C method call overhead.  Even utilising <a href="https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeSpeed/Articles/CriticalCode.html#//apple_ref/doc/uid/20001871-98344" data-wpel-link="external" target="_blank" rel="external noopener">IMP caching</a>, the performance is still terrible compared to directly accessing the bitmap byte buffer.</p>
</div></div>



<h2 class="wp-block-heading">Why does it matter that NSImage is not thread-safe?</h2>



<p>The crux of the problem is that you often have no good choice about it, because <code>NSImage</code> is a <a href="https://forums.swift.org/t/what-does-currency-type-mean/41065" data-wpel-link="external" target="_blank" rel="external noopener">currency type</a> used widely throughout Apple&#8217;s own frameworks, and you often have no control over what thread it&#8217;s created or used on.</p>



<p>For example, even using the very latest Apple APIs such as SwiftUI&#8217;s <code><a href="https://developer.apple.com/documentation/swiftui/view/dropdestination(for:action:istargeted:)" data-wpel-link="external" target="_blank" rel="external noopener">dropDestination(for:action:isTargeted:)</a></code>, you cannot control which thread the <code>NSImage</code>s are created on (it <em>appears</em> to be the main thread, although that API provides no guarantees).</p>



<p>Similarly you have no control over where those images are used if you pass them to 3rd party code &#8211; including Apple&#8217;s.  e.g. <code><a href="https://developer.apple.com/documentation/swiftui/image/init(nsimage:)" data-wpel-link="external" target="_blank" rel="external noopener">Image(nsImage:)</a></code>; <em>possibly</em> that only uses them on the main thread, but it <em>might</em> be pre-rendered the image a separate thread for better performance (to avoid blocking the main thread and causing the app to hang).  In fact it <em>should</em>, in principle.</p>



<p>Loading an image &#8211; actually reading it from a file or URL and decompressing it into a raw bitmap suitable for drawing to the screen &#8211; should never be done on the main thread, because it can take a long time.  A 1 GiB TIFF takes nearly 30 seconds on my iMac Pro, for example (and TIFF uses very lightweight compression, so it&#8217;s a relatively fast-to-read format).  Anything involving the network could take an unbounded amount of time.  Even small files &#8211; like a 20 MiB NEF &#8211; can take seconds to render because they are non-trivial to decode and/or decompress.</p>



<p>So you&#8217;re screwed on multiple levels:  not only can you generally not guarantee what thread <code>NSImage</code> is born on nor used on, you <em>can&#8217;t</em> use it exclusively on the main thread because that will cause a terrible user experience.</p>



<h2 class="wp-block-heading">What can you do?</h2>



<p>In simple terms, the best you can realistically do is try ensure that all modifications to the <code>NSImage</code> (including implicit ones, such as loading a bitmap representation upon first use) happen exclusively in one thread [at a time].  For example, in my experiments, putting a lock around otherwise concurrent calls to <code>bitmapData</code> is enough to prevent any data races.  Although I am mystified as to how I can still let the main thread draw the image concurrently without any apparent problems. 🤔</p>



<p>If you want to play it <em>really</em> safe, you have to create &amp; pre-load each <code>NSImage</code> on a single thread (<em>not</em> the main thread), before ever sending it to another isolation domain.  That means manually reimplementing things like drag and drop of images (because you can no longer work directly with <code>NSImage</code> with any drag &amp; drop APIs, you have to instead use only &#8211; and <em>all</em> &#8211; the things that <em>could potentially be</em> images, like URLs or data blobs, and then translate those to &amp; from <code>NSImage</code>s manually).</p>



<p>Complicating all this is that <code>NSImage</code> is unclear about how <a href="https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CocoaDrawingGuide/Images/Images.html#//apple_ref/doc/uid/TP40003290-CH208-SW11" data-wpel-link="external" target="_blank" rel="external noopener">its caching</a> works.  For starters, does this caching apply to the representations or is it a hidden, orthogonal system?  Is it thread-safe?  Etc.</p>



<p>You can supposedly modify the caching behaviour via the <code><a href="https://developer.apple.com/documentation/appkit/nsimage/1519850-cachemode" data-wpel-link="external" target="_blank" rel="external noopener">cacheMode</a></code> property, but in my experience there is no apparent effect no matter what it is set it to (not on the image&#8217;s representations nor on render performance in the GUI).</p>



<p>It&#8217;s a shame that <code>NSImage</code> has been so neglected, and has so many glaring problems.  Over the years Apple have seemingly tried to replace it, introducing new image types <a href="https://developer.apple.com/documentation/coreimage/ciimage/" data-wpel-link="external" target="_blank" rel="external noopener">over</a> and <a href="https://developer.apple.com/documentation/uikit/uiimage/" data-wpel-link="external" target="_blank" rel="external noopener">over</a> again, but all that&#8217;s done is <a href="https://xkcd.com/927/" data-wpel-link="external" target="_blank" rel="external noopener">made everything more complicated</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://wadetregaskis.com/nsimage-is-dangerous/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			<media:content url="https://wadetregaskis.com/wp-content/uploads/2024/01/NSImage-thread-unsafety-caught-by-Address-Sanitizer-MallocScribble.webp" medium="image" />
<post-id xmlns="com-wordpress:feed-additions:1">7501</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>
