Make Class Threadsafe | raywenderlich.com


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/9461083-ios-concurrency-with-gcd-and-operations/lessons/18

If the simulator is running in Dark Mode, the text “I think I did it right.” will appear in the View as white text on a white background (and so the entire screen will be white)

This had me puzzled for a bit until I figure out what was causing this. I turned off dark mode using the simulator Settings app (iOS 14.4) and the view matched the video. Sharing here in the event that others encounter this.

1 Like

Im unsure of what’s happening here, but when I try to turn the Concurrency app project with the Thread sanitizer the app crashes and I get the below error. Same issue if I block out the Thread.sleep. Same issue when trying to run the TSanExample project. Fails every time when running Thread Sanitizer. Any idea?

Xcode Version 13.2.1 (13C100)
Swift 5.3
iOS Deployment Target: iOS 13

**dyld: Symbol not found: _vfork**

**Referenced from: /Users/txpilot/Library/Developer/CoreSimulator/Devices/D9E06D3C-E4AB-4284-87D1-87C5793C3CB0/data/Containers/Bundle/Application/D247CCCE-17BC-47BE-8ED3-E09A990C6775/Concurrency.app/Frameworks/libclang_rt.tsan_iossim_dynamic.dylib**

**Expected in: /usr/lib/libSystem.B.dylib**

dyld: launch, loading dependent libraries

DYLD_SHARED_CACHE_DIR=/Users/txpilot/Library/Developer/CoreSimulator/Caches/dyld/21D62/com.apple.CoreSimulator.SimRuntime.iOS-13-0.17A577

DYLD_ROOT_PATH=/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 13.0.simruntime/Contents/Resources/RuntimeRoot

DYLD_LIBRARY_PATH=/Users/txpilot/Library/Developer/Xcode/DerivedData/Concurrency-adttbumlydbblwezitzjelxdunog/Build/Products/Debug-iphonesimulator:/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 13.0.simruntime/Contents/Resources/RuntimeRoot/usr/lib/system/introspection

DYLD_INSERT_LIBRARIES=@executable_path/Frameworks/libclang_rt.tsan_iossim_dynamic.dylib:/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 13.0.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libBacktraceRecording.dylib:/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 13.0.simruntime/Contents/Resources/RuntimeRoot/usr/lib/libMainThreadChecker.dylib:/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 13.0.simruntime/Contents/Resou

**(lldb)**

Regarding the ThreadSafe playground question: After adding the barrier to the isolationQueue so that each task will complete and update super.name and allowing it to complete before running the next task…isn’t this the same as running the task on a serial queue asynchronously? Why go through all the trouble of setting up the barrier when you just want the for-loop to run serially? Am I missing the bigger picture here?

hi Kenneth!, isolationQueue is a concurrent queue so read operations run concurrently but a write operation runs alone.

I get a different error when I run Concurrency, but it doesn’t crash

Concurrency(55091,0x100aa8580) malloc: nano zone abandoned due to inability to preallocate reserved vm space.

Ahh…ok. So kind of like the bank ATM scenario, read concurrently, but write serially. I get it now. Thanks.

And as far as the crash. I will have to do some more research, as the research I’ve done so far this has happened before to others when using Thread Sanitizer…but no fix was recommended in what I’ve seen so far.

Thank you for the quick reply!

When running multiple sync task on a concurent queue, the OS will decide the best approach and will create multiple threads even though it’s sync, it won’t run all the sync task on one thread.
So my question is, why run the read name sync and not async? Why does it matter?
I did understood that the barrier will wait for all threads(both sync and async threads) to finish all executing tasks before locking all of them during the execution of the write task, and return to executing tasks normally afterwards.

hi Lucian, dispatch barriers are a special situation where you definitely need a concurrent queue. In this case, the sync reading might be just the standard pattern from Apple, to synchronise access to shared state, because dispatch queues are serial by default.

But consider, if you dispatched the read task asynchronously: the task could be suspended. How would the dispatch barrier know whether it had completed or not?

I’m working on the course for Swift concurrency now: much more fun, much less code!

After trying the code out I understood why the sync was used, it’s related to the implementation, since name is a computed property and it has to return immediately it has to use a synchronise task.
Even though in my opinions is not the best approach, in case you have a large volume of reads because each sync task on a concurent queue can create a new Thread which can lead to Thread explosion. But this doesn’t stop you from using an async read of a value using something like this:

func safeName(completion: @escaping (String) -> Void) {
    isolationQueue.async {
        completion(super.name)
    }
}
1 Like