-
Notifications
You must be signed in to change notification settings - Fork 628
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
add mapped_futures #2751
base: master
Are you sure you want to change the base?
add mapped_futures #2751
Conversation
I'm also considering further additions for more complex data structure support, such as:
|
Thanks for the PR! It looks like a lot of the code added here is a copy from FutureUnordered. Is it possible to replace some of them with re-exports or wrappers for the types and functions used in FutureUnordered? |
This was my first approach, but it didn't work out. This PR required several small internal changes to FuturesUnordered, such that I don't think it would be possible to implement this as a simple wrapper. However, it may be possible to do so by making changes within the actual FuturesUnordered module that would make it easier to reuse. I'll look into that. |
Yeah, I guess making mapped_futures a submodule of the futures_unordered module or creating a new module for the code used in both can remove a lot of duplication. |
I've implemented a new module futures_keyed which contains most of the code that was in futures_unordered. futures_unordered and mapped_futures now consume the API of futures_keyed. There is more work to do, and there are some things about this implementation that are not ideal Work remaining:
Things that aren't ideal
Please let me know of any problems with this implementation; if you find none then I will proceed with implementing more consumers of futures_keyed |
Thanks.
For now, I would like to add a minimum (mapped_futures and mapped_streams; other ones are implemented on top of them, right?).
Yeah, futures_unordered_internal would be better. |
@taiki-e |
Also, the crate I published evidently has unsound code that causes this bug in tokio: StoicDeveloper/mapped_futures#4 EDIT: This issue has been resolved. |
I found unsoundness by running this PR's tests through miri. The unsoundness comes from the potential for a dropped task to still have a pointer in the ReadyToRunQueue, which will get dereferenced during polling. Solutions may include:
The Miri output can be found in the MappedFutures crate's issue: StoicDeveloper/mapped_futures#5 EDIT: The real cause was that I wasn't calling |
This probably needs to be rebased after the breaking changes in 0.3.31. I'd really like to see this get merged into futures-rs, as I think it addresses a big gap in functionality, but there hasn't been a comment from an owner in over a year now. |
Adds the functionality asked for in this issue: #2329. I wanted this feature, searched for it, found only people wishing that the feature existed, and so decided to write it myself. A previous attempt that wrapped FuturesUnordered didn't work; I wanted to avoid anything
unsafe
which I haven't used before. Having worked with it now though, I can see that unsafe rust is really just C with some guardrails.The motivation is to have a HashMap that can run futures. Adds the MappedFutures struct, which supports inserting,
get_mut
ing, cancelling, removing, and replacing futures by key, and running them in an identical style as FuturesUnordered.Internally it uses a HashSet and adds a hashable wrapper around the Task struct from FuturesUnordered.
In terms of ergonomics, the API is perhaps a bit confused, including both the
insert(key, fut) -> bool
andreplace(key, fut) -> Option<Fut>
methods which are more similar to the methods of HashSet than HashMap. The source of this confusion is that some methods can only work if the Future is Unpin; ie. anything that would return an owned future or future reference.Some justifications:
(key, fut_output)
to be similar to a LinkedHashMap, which in some ways this library is similar to (but withnext().await
in place ofpop_front()
. I think it will be typical for a consumer to want to know the key of whichever future has completed, though of course any future could be mapped as such prior to insertion.Arc<Task>
which is placed in a HashSet to get around this requirement. Its possible that a different solution would have worked (like pointers to and from a Key wrapper and Task), but this method seemed the most expedient.Potential improvements:
I did already publish these changes in a crate which I will yank if this gets merged. https://crates.io/crates/mapped_futures