Simultaneous accesses to 0x4c0a3f0f8, but modification requires exclusive access error on Xcode 9 beta 4 using Swift 4

Hi, I've a project that uses both Objective-C and Swift code. When a user logs in, it calls a set of apis for user preference, I have a DataCoordinator.swift class which schedules the API operation and I make this calls from UserDetailViewController.m class to load user preferences, so the main user dashboard loads only when all the api calls are complete and this class also help prevent multiple api calls. This use to work fine before I migrated my code to Swift 4 using Xcode 9 beta 4. Now when I login it crashes by giving me this error in my DataCoordinator class. Below is a sample of my DataCoordinator and UserDetailViewController class.

DataCoordinator.swift
import UIKit 
@objcMembers 

class DataCoordinator: NSObject {
  
    fileprivate var user = myDataStore.sharedInstance().user
    fileprivate var preferenceFetchOperations = [FetchOperation]()
    fileprivate func scheduleFetchOperation(_ operation:FetchOperation, inFetchOperations operations:inout [FetchOperation]) {
        guard  operations.index(of: operation) == nil else { return }
        operations.append(operation)
    }
    fileprivate func completeFetchOperation(_ fetchOperation:FetchOperation, withError error:Error?, andCompletionHandler handler:@escaping FetchCompletionHandler) {
        func removeOperation(_ operation:FetchOperation, fromOperations operations:inout [FetchOperation]) {
            if operations.count > 0 {
                operations.remove(at: operations.index(of: fetchOperation)!)              
              handler(error)
            }
        }
        if preferenceFetchOperations.contains(fetchOperation) {
            removeOperation(fetchOperation, fromOperations: &preferenceFetchOperations)
        }
    }
    fileprivate func schedulePreferencesFetchOperation(_ serviceName:String, fetch:@escaping FetchOperationBlock){
        let operation = FetchOperation(name: serviceName, fetch: fetch);
        scheduleFetchOperation(operation, inFetchOperations: &preferenceFetchOperations)
    }
    fileprivate func runOperationsIn(_ fetchOperations:inout [FetchOperation]) {
        for  var operation in fetchOperations {
            guard operation.isActivated == false else { continue }
            operation.isActivated = true
            operation.execute()
        }
    }
  
    typealias FetchCompletionHandler = (_ error:Error?)->Void
    var numberOfPreferencesFetchCalls:Int {
        get { return preferenceFetchOperations.count }
    }
  
    func fetchPreferences(_ completionHandler:@escaping FetchCompletionHandler) -> Void {
        defer {
            runOperationsIn(&preferenceFetchOperations)
        }
        schedulePreferencesFetchOperation("com.fetchPreferences.type1") {[unowned self] (operation:FetchOperation) in
            WebServiceManager.getType1Detail(for: user) {[unowned self] (error) in
                self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
            }
        }
        schedulePreferencesFetchOperation("com.fetchPreferences.type2") {[unowned self] (operation:FetchOperation) in
            WebServiceManager.getType2Detail(for: user) {[unowned self] (error) in
                self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
            }
        }
        schedulePreferencesFetchOperation("com.fetchPreferences.type3") {[unowned self] (operation:FetchOperation) in
            WebServiceManager.getType3Detail(for: user) {[unowned self] (error) in
                self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
            }
        }
        schedulePreferencesFetchOperation("com.fetchPreferences.type4") {[unowned self] (operation:FetchOperation) in
            WebServiceManager.getType4Detail(for: user) {[unowned self] (error) in
                self.completeFetchOperation(operation,  withError: error, andCompletionHandler: completionHandler)
            }
        }
    }
}
//Fetch Operation Struct

private typealias FetchOperationBlock = (_ operation:FetchOperation)->Void
private struct FetchOperation:Hashable {
    fileprivate var runToken = 0
    fileprivate let fetchBlock:FetchOperationBlock
    let name:String!
    var isActivated:Bool {
        get {
            return runToken == 0 ? false : true
        }
        mutating set {
            if runToken == 0 && newValue == true {
                runToken = 1
            }
        }
    }
    fileprivate var hashValue: Int {
        get {
            return name.hashValue
        }
    }
    func execute() -> Void {
        fetchBlock(self)
    }
    init (name:String, fetch:@escaping FetchOperationBlock) {
        self.name = name
        self.fetchBlock = fetch
    }
}
private func ==(lhs: FetchOperation, rhs: FetchOperation) -> Bool {
    return lhs.hashValue == rhs.hashValue
}



This is how I use it on my UserDetailViewController class



- (void)viewDidLoad {
    [super viewDidLoad];
__weak UserDetailViewController *weakSelf = self;
[self.dataCoordinator fetchPreferences:^(NSError * _Nullable error) {
                if (error == nil) {
                    [weakSelf didFetchPrefrences];
                }
                else {
                    //handles error
                }
            }];
}
//Completion response
- (void)didFetchPrefrences {
    //When all api calls completes this will be true
    if (self.dataCoordinator.numberOfPreferencesFetchCalls == 0) {
        / /Load details here
     }
}



Can anyone tell me, how should I proceed on this and modify my DataCoordinator.swift class to support multiple api request without causing this error on Swift 4?

Can you show where the error occurs exactly ?

Have you a backtrace of the crash ?

Hi, it seem to crash inside the FetchCompletionHandler inside numberOfPreferencesFetchCalls. Below is the backtrace of the crash



`DataCoordinator.completeFetchOperation(_:withError:andCompletionHandler:) + 604 (0x10d4e8b3c).
Current access (a read) started at:
0    libswiftCore.dylib                 0x0000000114a0a0e0 swift_beginAccess + 505
1    eCube Rounder                      0x000000010d4e8300 DataCoordinator.preferenceFetchOperations.getter + 72
2    eCube Rounder                      0x000000010d4ea140 DataCoordinator.numberOfPreferencesFetchCalls.getter + 17
3    eCube Rounder                      0x000000010d4ea100 @objc DataCoordinator.numberOfPreferencesFetchCalls.getter + 36
4    eCube Rounder                      0x000000010d431220 -[UserDetailViewController didFetchPrefrences] + 70
5    eCube Rounder                      0x000000010d4171a0 __41-[UserDetailViewController viewDidAppear:]_block_invoke_2 + 90
6    eCube Rounder                      0x000000010d4eed80 thunk for @callee_unowned @convention(block) (@unowned NSError?) -> () + 82
7    eCube Rounder                      0x000000010d4eee30 partial apply for thunk for @callee_unowned @convention(block) (@unowned NSError?) -> () + 58
8    eCube Rounder                      0x000000010d4e8fd0 removeOperation #1 (_:fromOperations:) in DataCoordinator.completeFetchOperation(_:withError:andCompletionHandler:) + 1105
9    eCube Rounder                      0x000000010d4e88e0 DataCoordinator.completeFetchOperation(_:withError:andCompletionHandler:) + 840
10   eCube Rounder                      0x000000010d4eb5b0 closure #1 in closure #1 in closure #1 in DataCoordinator.fetchPreferences(_:) + 417
11   eCube Rounder                      0x000000010d4fcd80 partial apply for closure #1 in closure #1 in closure #1 in DataCoordinator.fetchPreferences(_:) + 274
12   eCube Rounder                      0x000000010d4eb7c0 thunk for @callee_owned (@owned Error?) -> () + 103
13   eCube Rounder                      0x000000010d3cf760 __54+[WebServiceManager mainQueueCompletion:withError:]_block_invoke + 36
14   libdispatch.dylib                  0x000000011555d303 _dispatch_call_block_and_release + 12
15   libdispatch.dylib                  0x000000011555e34c _dispatch_client_callout + 8
16   libdispatch.dylib                  0x000000011556924f _dispatch_main_queue_callback_4CF + 628
17   CoreFoundation                     0x0000000113a97b00 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
18   CoreFoundation                     0x0000000113a5b9d0 __CFRunLoopRun + 2402
19   CoreFoundation                     0x0000000113a5b5c0 CFRunLoopRunSpecific + 409
20   GraphicsServices                   0x00000001180099a4 GSEventRunModal + 62
21   UIKit                              0x00000001106408af UIApplicationMain + 159
22   eCube Rounder                      0x000000010d4ba810 main + 115
23   libdyld.dylib                      0x00000001155db3c4 start + 1

To start, you need to read up on Swift 4 exclusive access model. At a minimum you should take a look at SE-0176 Enforce Exclusive Access to Memory, and if you’re feeling adventurous you can dive into the Ownership Manifesto.

Now, with reference to the backtrace you posted, there’s two important frames here:

  • Frame 9 — Here the outer function,

    completeFetchOperation(…)
    , has called an inner function,
    removeOperation(…)
    and passed it an
    inout
    reference to the
    preferenceFetchOperations
    array. The inner function then calls
    handler
    .
  • Frame 2 — Here the computed property

    numberOfPreferencesFetchCalls
    is accessing the
    preferenceFetchOperations
    array.

So,

removeOperation(…)
is given read/write access to
preferenceFetchOperations
(via its
inout
parameter) and it holds that access for the entire duration of its execution, including its call out to
handler
. That call out eventually calls
numberOfPreferencesFetchCalls
, which tries to get read-only access to the
preferenceFetchOperations
, and you trap because you’re not allowed to get a read-only access while someone has read/write access.

I think you can fix this by removing the

fromOperations
parameter from
completeFetchOperation(…)
, and have that function access
preferenceFetchOperations
directly. That will prevent it from holding its read/write access to
preferenceFetchOperations
while calling
handler
.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Hi Eskimo,


I'm having the same issue here, and I read the "Enforce Exclusive Access to Memory" document.

In the article, they refer to a "@exclusive (uncheck) " keyword, but the compiler doesn't seems to find such a declaration. Is this part missing in swift4?


Thanks.

I don’t think that got added; note that the Disabling dynamic enforcement section starts with “We could add an attribute which allows dynamic enforcement to be downgraded” (my emphasis). However, if want a definitive answer you should ask over on the swift-users mailing list.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
Simultaneous accesses to 0x4c0a3f0f8, but modification requires exclusive access error on Xcode 9 beta 4 using Swift 4
 
 
Q