Future trait - Waking the waker without using a thread

Hello,

I am trying to implement the Future trait.

Unfortunately where I am having difficulties is with the waking part of the Waker from the Context.

As of now the best way I see to be sure to know when to wake the waker is to have another thread watching when to wake the waker. But I feel that by doing that the async part is just "lost".

What are the best practices ?

Thank you very much in advance for any help.

Hoping I was clear enough

What needs to be done with the Waker depends entirely on what kind of future you are implementing, and it doesn't necessarily involve a thread. For example:

  • Futures which combine other futures only need to pass the Context down, and do not need to worry about any waking themselves.
  • Channel receiver futures pass the waker to where it can be used by the channel sender.

Tell us what your future does so we can help you with the specific situation.

1 Like

Thank you for your quick response.

Unfortunately, I don't seem to be in either of your two cases.

My future only calls a simple function returning true or false indicating whether the "computing" has completed or not. I have not written the function. It comes from a C lib that I have not written.

Thank you for your time and help.

It’s quite the opposite, in my view. If you didn’t have any wakers, you’d be doing simple polling, not fully efficient asynchronous computations. The latter does generally involve some sort of call-back or waking mechanism to avoid the overhead of “lots and lots of polling all the time” (or the alternative issue of “it takes a while until polling happens and progess is noticed”).

The power of async is that logical threads (aka “tasks”) become data simple structures, and you can support a lot more tasks much cheaper than system threads would. You can have a single thing (e.g. a fixed thread-pool; or even things the OS handles for you) that handles all usages of your API in one place / in a manner most fitting to the specific problem being solved or functionality being offered. It is nonetheless still the case that something has to run in parallel somewhere, and there needs to be some representation of the tasks.

Wakers are the interface to achieve this. You can give your Future some index/identity, and then make a store that keeps track of its latest Waker for each Future. That’s effectively the data-structure representation of the logical threads then.

Now if that’s the whole interface, than that is a simple polling interface, and it’s not ideal for being wrapped in a Rust Future as it doesn’t fit that model. Given you are planning to write the C interface yourself, you should probably reconsider the design. Edit: nevermind, I misread (see below). Still, I’m saying if that really is the whole API for the C library in question; i.e. a purely polling-based API where you need to do polling separately for every single request in question (without any way to bundle them up or something), then a better C API As far as I can tell, it would also improve the situation for all other users of that library API, be it users from C, or from other languages or frameworks – synchronous or asynchronous – as polling APIs would be annoying to work with either way.

Of course, there are cases where polling can be acceptable. E.g. in contexts where you know things aren’t expected to react quickly, so a low-frequency polling (anything that doesn’t come remotely close to showing as any perceivable percantage of CPU usage load for the polling loop alone — for instance, in a rendering loop, doing one poll for each frame to some functionality shouldn’t be problematic) and where the number of API callers that want to do this polling in parallel aren’t too high.

Perhaps this also means – however – that it’s not any use case where usage of async Rust is actually necessary (limited number of callers can probably work fine with system threads)?

Feel free to share more details about the nature of functionality you’re implementing, and the motivation for using async Rust; there may well be legitimate reasons, and determining what’s the cleanest approach or workaround probably depends heavily on the specifics of your situation :wink:

1 Like

Um. Isn't that the exact opposite?

I think it's designed for something like “offloading hardware” that works for indeterminate time and then reports completion by wring something into the “work is completed” slot… not an idea interface but if it's externally imposed…

Woah, I had read their reply at least 3 times, but it looks like my brain has inserted a “yet” there every single time. It clearly says “I have not written”, not “I have not yet written”, that’s my mistake :see_no_evil_monkey:

Thank you for your replies.

So, if I understand well, using a Future / async in my case isn't well suited ? And a simple loop waiting for the function to return true would be better suited ?

As a side fun project, I creating a dbus ffi wrapper in rust. I know there are crates out there, but for the sake of learning and for fun I decided to give it a try. My poll is meant to call dbus_pending_call_get_completed. I am trying to learn about how to implement a Future in a case like this. (maybe not the best suited situation)

Thank you all for your time and help

From the way you’ve described it, if the C function doesn’t offer any way to notify you when it’s done (like a callback, fd readiness, or event handle), then yeah — there’s really no clean way to “wake” the waker without either:

  1. polling the function periodically from another thread, or
  2. offloading the computation to a blocking thread (e.g., with spawn_blocking or similar).

The key thing is: a Waker is just a way for your future to tell the executor, “hey, I made progress — poll me again.”
If nothing can tell you that progress happened, then you can’t trigger the waker at the right time.

So unless the C API gives you a callback or something observable to hook into, you’re right — there’s no fully async solution.
The best you can do is to offload the blocking call to a thread so that you don’t block the async executor.

1 Like

Looking at that API only for a short time, to me it looks like dbus_pending_call_set_notify might help you with wakers.

2 Likes

Thank you.

I didn't even notice this function.