-
Notifications
You must be signed in to change notification settings - Fork 0
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
Refactor: Switch to accepting sync input closures, adjust defaults, add docs #9
Conversation
…g/custom/current_context, tweak defaults to enable concurrency limiting + use spawn_blocking for tokio feature, add more docs
Started integrating with tokio-rustls to demonstrate the API, see: rustls/tokio-rustls#99 |
As we stabilize this API, I wonder if we should have a way to pass configuration options during scheduling of futures. For example, the caller could indicate that suspending the current task was acceptable (allowing the executor to use |
Agreed, I was considering this in #4 but figured it was scope creep for initial impl. I think probably rather than totally arbitrary config, since caller doesn't have too much context on possibilities, we want bimodal or trimodal API pattern. Where there is 'likely to block / for a long time' or 'somewhat like / for a short time'. And then application author could configure additional strategies if they are able to eg I think I probably prefer adding a net new api (ie, Though, there is even further granularity available if we did have config (priority, etc). I could see an argument for bubbling that up, at cost of complexity for library authors? I was thinking about that from the other end (#3) |
Capturing feedback from @rcoh in offline discussion:
|
…te_sync()` to specify likeliness to block, add helper for constructing good default tokio strategy
…all() method, add optional handle to spawn_blocking constructor
@rcoh I think all your feedback has been addressed, thanks for the close read. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whoops never submitted these
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like where we ended up here! left a couple more small thoughts
Capturing offline feedback from rcoh@:
|
Alrighty, pushed up changes that address your feedback @rcoh, thanks!
Re Instead, we are using the |
Issue for allowing usage of executors outside of global strategy: #10 |
Refactor: Switch to accepting sync input closures, add builder, adjust defaults, add docs
Overview
In the previous iteration we were accepting futures and trying to execute them in a way that accommodated blocking. This worked fine for the secondary tokio runtime, but was flawed for the
spawn_blocking
andblock_in_place
executor. Because, in fact they were spawning delegated tasks right back to the worker thread.After thinking about it more, it has become clear to me that the most immediate benefit of this library will be to instead allow libraries to provide sync inputs, not async. It's a convenience to also handle async, but the use cases I'm currently tracking (tls handshaking, high-cardinality metric publication), reduce to sync segments when handled most optimally.
To quote from the new README:
Changes
Shuffle around traits to use
execute_sync
and related executorsAlso did some package reorg.
The core trait now is:
Notably it is async, this is important since we are essentially trying to delegate the blocking segment to another thread, so that we can await its completion in the current thread while sleeping.
I opted to keep
sync
in the naming everywhere at the cost of verbosity. I did this for two reasons:Remove strategies
These are removed:
Secondary tokio runtime
Mostly useful for comingled async/sync, suboptimal threadpool for pure sync.
Block_in_place
This does apply to sync, but it just has so many footguns with how it breaks code running concurrently on the same task and how it can starve out your worker threads. I want to introduce friction to using it by requiring that it be a custom executor, so that people don't blame library authors if it breaks them.
I did include it in a doc example for the custom executor, linked to block in place docs, with ample warnings.
Changes to CustomExecutor
So the custom executor now accepts a sync closure rather than async. This makes it somewhat easier to name.
However, I still opted to keep the wrapping with a oneshot channel to keep the type out of the input/output to avoid generic bounds. Previously this served two purposes - type erasure to avoid generics in static bounds, but also to add cancellability of the future.
Now, the cancellation is no longer relevant. Theoretically we could instead go back to the
Any
usage that reduces allocations a bit and also preserves types for the closure to interact with (sort of, anyway, via downcasting).But, the API becomes much uglier with Any. Also, most use cases are going to need a channel anyway (eg rayon channel). So, I left it as-is for now. Open to alternatives.
I still would ultimately like to add an API that makes types available to the closure (ref #3) but it feels like scope creep until there are people that have a use for it.
Updates to defaults
tokio
feature now defaults toSpawnBlocking
strategy, with concurrency limited to the cpu core count. That concurrency limit is actually pretty important, it takesspawn_blocking
from being a suboptimal threadpool for compute, to a nice lightweight one that lazily spins up threads with configurable idle TTL.We now default to
tokio
feature being on. I feel that this is likely the behavior that most users want. Iftokio
is enabled, the user gets a default that actually is probably a good default, as opposed toCurrentContext
which adds concurrency control but nothing else.I did explicitly add discussion of feature enablement aimed at both library authors + application authors in the readme + module docs.
Doc updates
Took a first stab at drafting module-level docs and a README. Also spruced up docs all over the place.
Notably, added add a
rayon
threadpool custom strategy in an integration test. I also have a module-level doc sample involving Rayon.fixes #8 #7 #4