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

feat: add Event synchronisation object #44

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft

feat: add Event synchronisation object #44

wants to merge 2 commits into from

Conversation

dimlio
Copy link
Contributor

@dimlio dimlio commented May 9, 2023

Description

Add Event synchronisation object that allows multiple tasks to wait for some event to occur.

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Minimal checklist:

  • I have performed a self-review of my own code
  • I have added DocC code-level documentation for any public interfaces exported by the package
  • I have added unit and/or integration tests that prove my fix is effective or that my feature works

@codecov
Copy link

codecov bot commented May 9, 2023

Codecov Report

Merging #44 (7d93f04) into main (0b36be8) will increase coverage by 4.07%.
The diff coverage is 97.17%.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #44      +/-   ##
==========================================
+ Coverage   78.48%   82.55%   +4.07%     
==========================================
  Files          13       14       +1     
  Lines         381      487     +106     
==========================================
+ Hits          299      402     +103     
- Misses         82       85       +3     
Impacted Files Coverage Δ
Sources/ConcurrencyHelpers/Event.swift 94.12% <94.12%> (ø)
...currencyHelpersTests/ConcurrencyHelpersTests.swift 96.03% <100.00%> (+3.07%) ⬆️
Impacted Files Coverage Δ
Sources/ConcurrencyHelpers/Event.swift 94.12% <94.12%> (ø)
...currencyHelpersTests/ConcurrencyHelpersTests.swift 96.03% <100.00%> (+3.07%) ⬆️

Continue to review full report in Codecov by Sentry.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 0b36be8...7d93f04. Read the comment docs.

/**
* Synchronization primitive modelled after Event from WinAPI.
*/
public final class Event {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just asking, why don't do it as actor? Seems it will simplify the logic since it will be guarantee to be race free.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance.
Want to able to check isSignalled as fast as possible.

Copy link
Contributor

@mr-swifter mr-swifter May 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, don’t get me wrong, but seems if actor is not often occupied (which in this use case it will not be) it should take approx. same time. For me it looks like optimisation without clear benefit but with more complex logic.
Or maybe we should add the performance test to see difference and consider proper patter going forward in other places

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, performance numbers are always good to have.
But frankly I cannot imagine anything faster than reading a flag from memory.
And since this code is in general-purpose library I think it makes sense to trade a bit of complexity for performance.

Another reason I don't use actors -- I want methods be available in sync context.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, performance numbers are always good to have.

Yeah, so maybe will try to have some numbers and see? 🙂
Regarding sync context I would agree, sometimes it’s useful

// Can be accessed for read without taking lock
private var signaled: UnsafeAtomic<Bool>

private typealias Continuation = CheckedContinuation<Void, Never>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's generic implementation in helpers I would consider add Success and Error as part of Event generic declaration. Then signal can return some object or throw an error

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think then it will be Future :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it could be, but then we will do the break 🙂

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but maybe it’s okay for now

@hassila
Copy link
Contributor

hassila commented May 9, 2023

Before reviewing, could you please write a little bit about intended concrete use cases in the description please?

lock.lock()

guard !isSignaled else {
lock.unlock()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably, better to use pattern as in library:

defer {
    lock.unlock()
}

Copy link
Contributor Author

@dimlio dimlio May 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not possible, we need to release the lock in the closure passed to withCheckedContinuation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could that closure be executed in other thread?
If so, is it allowed to release lock in other thread?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so.

@dimlio
Copy link
Contributor Author

dimlio commented Aug 31, 2023

Need to properly handle task cancellation while waiting -- throw CancellationError ?

@hassila
Copy link
Contributor

hassila commented Jan 19, 2024

Could I suggest https://github.com/groue/Semaphore instead? Might do a PR with a loop that signals all suspending tasks (the current signal there only signals one pending task, but allows to understand if there are more pending, so its a trivial fix).

@hassila
Copy link
Contributor

hassila commented Jan 19, 2024

(we have it forked for dependencies at https://github.com/ordo-one/Semaphore)

@dukhnyak dukhnyak marked this pull request as draft July 24, 2024 10:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

5 participants