Git Product home page Git Product logo

Comments (6)

alexcrichton avatar alexcrichton commented on June 4, 2024

Thanks for the report! In general we definitely want to make this library as flexible as possible, so if you've got a use case that doesn't quite fit we're very interested to hear and learn about it! The intention is that the foundational abstractions here cut across and are suitable for the entire ecosystem all in one. With that in mind, I'd love to help out to port what you've got to futures!

To answer some of your specific questions:

Functionality to poll a future without worrying about what machinery must be set up for tasks to work.

Yeah the documentation around this isn't great today and the support unfortunately isn't that great either. Currently what you'd want to do is to create a task via Task::new() or Task::new_notify() and then "wed" that to a future. That is, you'd want the future and the task to live together for their entire lifetime. Then you'd do:

let result = task.enter(|| future.poll());

Note that this API is very likely to change in the near future as we fill out this sort of functionality. Does that suffice for you though? (the requirement of pairing a future with something to poll it later on)

Functionality to wait on a future with a timeout, arranged such that the timeout is never late.

There is currently timeout support in the futures-mio crate, which returns futures that resolve to () when the timeout has expired. Would that suffice for this use case? In that sense you can do .select(timeout) to determine which future fired first.

Additionally I've recently added a wait function as well as a wait iterator to convert asynchronous computations to synchronous ones.

I've got some thoughts on how to implement wait_timeout but I think it'd require us to think through a bit more on this story about polling manually. If we had some method to synchronously wait on a future with a timeout, would that suffice for you as well?

An iterator that pulls elements from streams, but only if those elements arrive in a specific time period.

I'd love to add this! I think this definitely ties into the previous point as well. If we've already got a method to wait on a future with a timeout it should be trivial to make a stream that waits on each item with a timeout. On easy way to do this would be to convert the stream to a future and then use the standard timeout mechanism on that.

The ability to spin the Mio event loop once, indicating if the future I passed it completed or not.

Currently we don't expose this directly, but we could perhaps encode this by calling loop.run with a very short timeout (or something like that).

I'm curious, though, could you expand a bit on what you'd like to use this for?

but mio doesn't appear to have game-friendly timing and games are one of the primary applications.

Interesting! I'm not personally super familiar with games, but could you describe some of the requirements they have for timers, and perhaps why the mio timeouts aren't quite appropriate? I'd love to make sure that at the very least there's a crate for various flavors of timeouts, if not directly in futures-mio itself.

from futures-rs.

ahicks92 avatar ahicks92 commented on June 4, 2024

Polling looks okay, so long as I can get the result out. I'll have to look at this more closely. Perhaps a fn Task::poll<F: Future>(f: &F)->Option<Result<F::Item, F::Error>>, assuming I got the signature right? Or maybe an adapter that turns the future into a future that manages its own task, now that it's going through TLS and that's therefore a valid implementation?

A wait_timeout is exactly what I'm asking for. I think this could be manually constructed by using select and a future that times out on the Mio event loop, but obviously this isn't general and will rely on the Mio timeout resolution.

I'll get to the use case for my project in a minute. I'm not the best to talk about games because I'm blind, and the only reason it matters much is that my as-of-yet incomplete library is for networking in such a manner as to be friendly to games and other things that might need P2P. Any game I did can deal with timing that's a bit off. It would also be unplayable by anyone sighted, but that's an entirely unrelated discussion. A typical game loop looks like the following pseudocode, as I learned it:

fps = 60
while True:
    start = time()
    do_game()
    end = time()
    delta = end-start
    needed = (1/fps)-delta
    wait for delta seconds

Your goal is to go no slower or faster than fps if at all possible, though many games also introduce other stuff so that they can deal with hiccups. If Mio guaranteed that callbacks would never be off by more than a fixed amount (assuming the event loop isn't blocked) then you can use the Mio loop to time out a future and be all right: the loop can check to see how much time is left before it needs to go around again. But Mio doesn't actually say how precise timeouts are or if they can be early or late or what.

This is actually reasonable behavior. Being precise about timeouts means turning on high resolution timing and keeping the processor from idling, which is indeed inefficient. But sometimes you need something that's guaranteed to be right on or at least not late. In addition to being a potential problem with Mio, it's also a potential problem with any implementation of wait_timeout we come up with.

As for my use case, my project is a library called Fastnet (I am amazing at naming). It will be networking like Enet or Raknet, when finished. My public API is here. The idea is that I will eventually write higher-level pieces on top of it. For integrating into typical game loops, wait_timeout is helpful. But if I had that then I could kill the ugly Handler trait, use a Channel per connection for incoming messages, and avoid having to write adapter modules.

Internally, this is all one big, ugly state machine. I'd like to rewrite that part as well. I don't think anything in regards to this crate stops me from doing so, though I'm not 100% sure how to reorganize it yet. What I envision is adapting a UDP socket into a stream of incoming packets, then filtering the stream to extract packets related to each connection into other streams.

It's also a learning project, so I can do whatever I want with it.

from futures-rs.

alexcrichton avatar alexcrichton commented on June 4, 2024

Some extra pieces about the poll story are likely to be fleshed out with #95, a WIP PR. I'll get back to you once we've figure out how to merge that!

The resolution of timeouts kinda varies here and there, but it sounds like for a game you'd basically want the resolution of the timeout to be on the order of the FPS rate, and otherwise any more precision isn't necessarily needed. The timer wheel data structure used here should in theory be appropriate for that, and then you could just use the timeout_at function to schedule timeouts at instants in time and avoid accumulated drift.

I would naively say that an epoll-based event loop would be suitable for a game, but having never done it myself I wouldn't necessarily be confident in saying that! Hopefully futures could definitely help simplify the state machine and also work with all sorts of I/O necessary, but I guess that depends on the particular game itself.

from futures-rs.

ahicks92 avatar ahicks92 commented on June 4, 2024

I'm not the ideal person to talk about game timing for high-resolution high-fps games with hundreds of thousands of triangles, so some of this is going to need us to wait and see. I think some people want to use 120 FPS these days, though, which means timeout resolutions of at least 8 MS.

A few specific problems:

  1. The timer wheel schedules you in the next slot if you're late. I can't quite follow this code, but I take this to mean that you can sometimes be off as much as 1.99*resolution by scheduling exactly one tick's worth of time into the future just after a tick happens. If I am understanding it properly, this means that the timer wheel can introduce some jitter. I do not know for sure how much is acceptable.
  2. The needed resolution is not actually 1/fps. Games have to factor in how much time they spent computing and interacting with the network and the like in order to maintain a steady frame rate. Since modern games try to push hardware to the limit, I think that being just under 1/fps is reasonable. But even if you use half the time computing, you're still looking at needing a timer that's precise enough to handle half a frame.

using the timer wheel with the current settings, however, is definitely not sufficient. You could only get 10 FPS.

Some games and frameworks (i.e. XNA from Microsoft, now abandoned last I checked) deal with these problems by allowing the timeout to be jittery for the update step, then separating the rendering step out into something that runs faster. This could possibly be on a different thread as well. If this pattern is how everyone in the Rust ecosystem is doing it and intends to do it in the future, then jitter probably isn't such a big deal.

One thing we might add is an iterator which pulls from streams instantly. That is, as soon as the stream hasn't got something for us, we stop and return None. If timeouts can't be made high resolution, this might be the next best thing for people that need it. Spin waiting could be implemented around it, if that's really required.

from futures-rs.

alexcrichton avatar alexcrichton commented on June 4, 2024

The timer wheel does have tunable parameters in terms of the size of each bucket and size of each tick, so it could perhaps be adapted to a game's needs to be more suitable, but in general I think that it should definitely all be possible to do one way or another w/ streams and futures.

from futures-rs.

alexcrichton avatar alexcrichton commented on June 4, 2024

Ok, with the recent overhaul to the executor story, I think we're well set here. The issue title at least is certainly taken care of through the task::spawn function, coupled with poll_future. That way applications have complete control about how a future is polled and when blocking actually happens.

If there's more to do here, though, please just let me know!

from futures-rs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.