Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: add serial dispatch queue for storedCalls to avoid data-race #7480

Merged
merged 6 commits into from
Jun 13, 2024

Conversation

amitsingh19975
Copy link
Contributor

Accessing storedCalls from two different threads causes crash since Dictionary is not thread safe that introduces data-race. The serial queue prevents the access to the shared resource by two different threads by queuing the operation and invoking the functions one-by-one.

There is other option to avoid data-race using locks, and top of that, there are two types of locks that can be used here: NSLock and the other one is OSAllocatedUnfairLock, which is faster than the first one.

Accessing `storedCalls` from two different threads causes crash since `Dictionary` is not thread safe that introduces data-race. The serial queue prevents the access to the shared resource by two different threads by queuing the operation and invoking the functions one-by-one.

There is other option to avoid data-race using lock, and there are two types of locks that can be used here `NSLock` and other one `OSAllocatedUnfairLock` that is faster than the first one.
@Steven0351
Copy link
Member

Steven0351 commented May 23, 2024

We have a ConcurrentDictionary type that could be made internal instead of private to use here. I prefer using locks to GCD for synchronization and I think we should keep it consistent across the code base. OSAllocatedUnfairLock is only available for iOS 16 and up

@amitsingh19975
Copy link
Contributor Author

amitsingh19975 commented May 23, 2024

We have a ConcurrentDictionary type that could be made internal instead of private. I prefer using locks to GCD for synchronization. OSAllocatedUnfairLock is only available for iOS 16 and up

I've got an implementation of the unfair lock using os_unfair_lock_lock if you would like I could share that. It could be used below iOS 16.

Here is the gist containing an implementation of the unfair lock. This gist also contains an implementation of UnfairLockValue that could wrap any data structure inside the unfair lock, and it forces concurrent access to the resource.

@Steven0351
Copy link
Member

I get that os_unfair_lock is more performant than NSLock, and I've seen and used the < iOS 16 implementations myself, but I made the conscious decision to not implement it that way in Capacitor because it's a bit of boiler plate that has to be maintained.

Every maintainer on this project has varying levels of familiarity with Swift, so removing the need to juggle pointers and manual memory management, even if it is abstracted in such a way, is better to be avoided in my opinion. I find it doubtful that the impact on performance would be noticeable in a capacitor application.

@amitsingh19975
Copy link
Contributor Author

Originally, I fixed the problem using NSLock, but I thought the Capacitor team wouldn't like that so I went with the dispatch queue. I could make the concurrent dictionary internal and use that if you're ok with that.

@markemer
Copy link
Contributor

If ConcurrentDictionary solves the problem, lets use that. Otherwise NSLock is probably the way to go.

@markemer markemer self-assigned this May 23, 2024
@amitsingh19975
Copy link
Contributor Author

If ConcurrentDictionary solves the problem, lets use that. Otherwise NSLock is probably the way to go.

We've to extend it to have some function for it to be fully compatible with the use case. Otherwise, we could add a single function that accepts a block, and you get full access to the object with a lock.

@Steven0351
Copy link
Member

If ConcurrentDictionary solves the problem, lets use that. Otherwise NSLock is probably the way to go.

We've to extend it to have some function for it to be fully compatible with the use case. Otherwise, we could add a single function that accepts a block, and you get full access to the object with a lock.

Let's just add the single function that accepts the block, its simpler.

@amitsingh19975
Copy link
Contributor Author

If ConcurrentDictionary solves the problem, lets use that. Otherwise NSLock is probably the way to go.

We've to extend it to have some function for it to be fully compatible with the use case. Otherwise, we could add a single function that accepts a block, and you get full access to the object with a lock.

Let's just add the single function that accepts the block, its simpler.

What you plan to call that function? I've a few names from kotlin and other places like run, apply, with or inner.

@Steven0351
Copy link
Member

If ConcurrentDictionary solves the problem, lets use that. Otherwise NSLock is probably the way to go.

We've to extend it to have some function for it to be fully compatible with the use case. Otherwise, we could add a single function that accepts a block, and you get full access to the object with a lock.

Let's just add the single function that accepts the block, its simpler.

What you plan to call that function? I've a few names from kotlin and other places like run, apply, with or inner.

I personally like with or something like withValue or withLock. Since it's not going to be a public API its not a huge deal, but @markemer may have an opinion here

@amitsingh19975
Copy link
Contributor Author

I've replaced the uses of the dispatch queue with the concurrent dictionary.

@markemer markemer merged commit fa13ac1 into ionic-team:main Jun 13, 2024
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants