Change:
<!-- Thank you for creating a tracking issue! Tracking issues are for tracking a feature from implementation to stabilization. Make sure to include the relevant RFC for the feature if it has one. If the new feature is small, it may be fine to skip the RFC process. In that case, you can use use in your initial implementation PR. The reviewer will ask you to open a tracking issue if they agree your feature can be without an RFC. --Feature gate: This is a tracking issue for getters of data and vtable pointers for , and the method to get from . <!-- Include a short description of the feature. --<!-- For most library features, it'd be useful to include a summarized version of the public API. (E.g. just the public function signatures without their doc comments or implementation.) --<!-- For larger features, more steps might be involved. If the feature is changed later, please add those PRs here as well. --- [x] Implementation: [x] Final comment period (FCP) [x] <!-- Once the feature has gone through a few release cycles and there are no unresolved questions left, the feature might be ready for stabilization. If this feature didn't go through the RFC process, a final comment period (FCP) is always needed before stabilization. This works as follows: A library API team member can kick off the stabilization process, at which point the rfcbot will ask all the team members to verify they agree with stabilization. Once enough members agree and there are no concerns, the final comment period begins: this issue will be marked as such and will be listed in the next This Week in Rust newsletter. If no blocking concerns are raised in that period of 10 days, a stabilzation PR can be opened by anyone. --<!-- Include any open questions that need to be answered before the feature can be stabilised. If multiple (unrelated) big questions come up, it can be a good idea to open a separate issue for each, to make it easier to keep track of the discussions. It's useful to link any relevant discussions and conclusions (whether on GitHub, Zulip, or the internals forum) here. --- Do we also need for ?
Maybe we could stabilize this? Note sure what I could do next.
I am concerned about , it's not clear to me how it is intended to be used. It takes by reference and returns a , which you can then decompose into a vtable and data pointer. However I don't see how you can then re-construct a reference to the original : doing so with will create a second which will call the function in the vtable when dropped. This can lead to a double-drop once the original is dropped as well.
It returns , not , thus it's a getter rather than an ownership transfer. Its purpose is to allow getting or checking the underlying data. Reconstructing a from the result pointers is indeed incorrect, and unnecessary due to the existence of . If we do want reconstruction, I'd consider adding another , which also consists with . But I think it's less useful, because we can only access and get a . We don't own Waker without explicitly cloning it somewhere.
Then I'm afraid that I don't understand how is intended to be used. What can you actually do with the returned vtable and data? I can understand passing it through FFI, but then you eventually have to pass it back to Rust code to use it as a .
It's effectively for . It allows validating Waker VTables, ad-hoc optimization, or retrieving data from known Wakers, where the async runtime only supports Wakers from itself. I found an example in, In case of passing through FFI, we need do --[^1] without ownership transfer. The last step can be done via reconstructing and using the reference to inner. If we want to transfer the ownership via, to say , we need an extra* during every . Note that the - pattern above can be found in, [^1]: Since the VTable representation is repr(Rust). Here we actually need to create a new repr(C) VTable for forwarding method calls, rather than to pass the pointer through FFI directly. This is doable currently.
I feel that a better story is needed for getting a from a . Perhaps something like this? Also I do think that we should add for consistency with .
Also this API is more like a helper, which is able to be implemented outside std ( cannot, due to ). Should it also be a part of ?
I think it should be inlcuded as the counterpart to , otherwise it isn't clear how the returned by can be safely used. This is important for the doc examples for these functions. For tokio, it seems to only be used . It should be easy to refactor that to use .
Noticing that is , it should be safe to from to I think? Then the you suggested can be replaced by,
Didn't realize does this already! Thanks
Because has private fields, the is an implementation detail and not an API guarantee that users can rely on. It isn't shown in rustdoc for this reason. The problem with is that it effectively turns this into a public guarantee: it might be something we can commit to but it would have a higher barrier for acceptance.
To add another use case is storing the waker atomically. You can split the data and vtable into two and store those atomically separately (with some careful programming) or in a single (on 64 bit archs at least). wote that exists for this.
I think it might be natural to think that a could do more than just . Like how a might be a specific kind of error with some fields we would care about. I can imagine as situation where you might want to check if the current is a in order to extract a field from it, eg a TaskID. (I am exploring an idea at the moment to hack in a go like typemap into through this mechanism.) In order to do this, I could verify that . (in a similar respect to and , it would be interesting to have a chain, especially with the recent push to structured concurrency with many nested sub-executors, but that is for a separate issue and another time)
Hi, now that is out, I'm eagerly awaiting accessors for the contents of the vtable and the date pointer, so that they may be passed over the ffi frontier in an ffi safe manner without having to wrap the waker in a stable trait object at every poll, which is what stabby is currently forced to do to safely support futures being passed across the ffi frontier. Note that since the vtable isn't repr(c), and the functions use the default calling convention, access to the vtable but not the functions in contains is not sufficient: I'd need accessors to at least one of the wakes, clone and drop. As a second point, is there a good reason why the function pointers use the (unstable) rust calling convention? This means the function pointers couldn't be passed across ffi even if they were accessible... Anything I could do to help with the process?
Hi again, I've started a new RFC () which, while orthogonal code-wise, has the same end-purpose of allowing the safe passage of wakers across the FFI. A key issue that don't address is that the vtable is composed of function pointers, which can't safely cross the FFI bounday. My proposed solution involves modifying the current layout of , which would make accessing the vtable's inner function pointers fallible. I just wanted this stated somewhere to avoid stabilizing accessors that would prevent the vtable from being made truly FFI-safe without breaks in the accessors API. For example, an eventual would prevent proposing layouts for the vtable that may not contain said function pointer. Should such accessors be proposed, I strongly suggest considering making them of the form
We ended up doing the exact same thing. These getters allow us to provide executor-specific extensions on , by verifying that a specific context is indeed one of our contexts (kind of RTTI). Most async-runtimes have global executors that you use to spawn tasks. But with you can actually implement features like which would use the passed to access state of the calling future and allow spawning another future on the same executor / scheduling-class / etc. You can also use it to modify scheduling priorities / properties of the calling future, and much more. Without this you need a lookup-tree to get your internal state from a public , even though the data is already stored in the context.
One way to construct a value within a function and return its reference is to have its memory location passed in. It's a little gross, but for example: EDIT: fixed the code
Would it make sense to have the API look like this? Or a slightly different take:
My issue with this feature is that it's not really usable with . Could we add the following implementations? (for the symmetry with the already existing implementation) Should it be moved in another feature?
What's left to move forward with this? It would enable extension experimentation outside of std, which could help accelerate async runtime interop.
(NOT A CONTRIBUTION) This would be a useful API & I'd like to see it stabilized. I don't think any of the discussion on this issue should be blocking on stabilizing the APIs already under this feature. My motivation is for a tightly bound executor/reactor: I want to check in the reactor if a registered waker has the vtable for "my" executor, so that I can avoid making the virtual wake call and directly re-enqueue the task. I don't need to pass the Waker through FFI, I don't need to get a waker back from a RawWaker, these are not necessary features for this API to be useful. I don't think the conversion from &RawWaker to &Waker is needed for this API to be useful, but I also find it extremely hard to imagine Waker ever gaining any other fields. We Context specifically to have a place to add fields that wasn't Waker. I think T-libs should guarantee the layout of Waker.
fcp merge I propose that we stabilize the following 3 accessors, which have been available since Rust 1.60-nightly (2 years). The use case is described succinctly and convincingly in The workaround involves assuming implementation details of the standard library's data structures and compiler's struct layout. By making stable accessors available, this would be: The importance of this use case is reinforced by , and I would be open to considering making public fields on , instead of accessors. This is in fact what is shown in the . As far as I can tell, is what precipitated the change to private fields under the possibility that adding a 3rd field would turn out to be useful, which seems obsolete as a rationale. Or, move the accessors to and phase out the use of . I tried looking into why and are separate types and failed to find a good reason. My guess is this pre-dates the separation of from , and might no longer be well motivated now that serves as our extension point going forward (for things like ) and will remain permanently just a as articulated in
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [ ] [ ] Concerns: Alternative 1: public RawWaker fields () Alternative 2: phase out RawWaker () Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
concern Alternative 1: public RawWaker fields concern Alternative 2: phase out RawWaker
:+1: for going with alternative 1. (Please feel free to check my box for an FCP that proposes alternative 1.)
fcp cancel We talked about this in today's libs-api meeting and think we're on board with giving public and fields, rather than a and accessor method Let me go ahead and FCP that (Alternative 1 from above).
proposal cancelled.
fcp merge I propose stabilizing the following counterproposal: and deleting these 2 unstable accessors currently tracked by this tracking issue: was originally stabilized with private fields related to a plan to maybe add a 3rd field (). An field was envisioned to support downcasting Waker based on the type of object pointed to by . Instead, modern implementations such as embassy have done downcasting based on the value of , not the type of the object pointed by . This is just as good and no separate is needed. See for links to the relevant code.
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [ ] [x] [x] [ ] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
Is there a downside to using public accessors? It seems like the more conservative route. And are there compelling use cases specifically for exposing the fields? (I don't have any use case in mind for adding another field to . I am only coming at this from the angle of preserving for the future unless there's a good reason not to.)
labels +I-async-nominated Nominating this for WG-async for our review. We should have a look at this.
Should be marked if all the field become public so it can be extended in the future if needed?
Do we not plan on ever making RawWaker a dyn-star?
(NOT A CONTRIBUTION) I don't think it matters. However, I think with a pretty high probability there is real code in the world that transmutes a Waker into its component parts. I don't know this first hand, it just seems very plausible. Such code is unsound, because the order of the fields is currently unspecified. But I think since there's also no reason to modify this, libs should commit Waker/RawWaker to their present representation (adding repr(transparent) and repr(C) where appropriate) so that that code is sound if it exists. Reasonable people can differ and prefer leaving options open. But if you're going to do that, stabilizing the accessors seems critical to give people a way to examine the vtable without unsound code.
.
Carl Lerche on the tokio discord server:
labels +I-libs-api-nominated We discussed this in the WG-async call, and while we don't have any specifically strong concerns with any of the proposed options, in our discussions, we did have a preference for adding the accessors to instead, and we would like to hear the analysis for why it might be better to make these fields public. What's the advantage of that? We weren't able to find that in the public discussion. Nominating so that T-libs-api can hopefully help to catch us up here.
Capturing discussion from today's meeting: We're fine with seeing accessors rather than public fields, which certainly does feel like it leaves open more possibilities in the future. We're fine with seeing those accessors to , in addition to or instead of , assuming that meets people's needs for these accessors. If we add the accessors to , we may also want to add an unsafe that accepts the two values, which would make less necessary.
Concrete proposal from the meeting: Add the two accessors directly to . Add an unsafe constructing it from those two values. Don't bother adding .
:bell: This is now entering its final comment period, as per the . :bell:
fcp cancel
proposal cancelled.
fcp merge This is Alternative 2 from By adding accessors ( and ) and constructor () on , the type becomes vestigial. There should never be any remaining reason to use .
Team member has proposed to merge this. The next step is review by the rest of the tagged team members: [x] [x] [x] [x] [ ] No concerns currently listed. Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See for info about what commands tagged team members can give me.
:bell: This is now entering its final comment period, as per the . :bell:
It is still necessary for constructing a , it takes a parameter
yeah, deprecating feels a little off to me. I think we can still make use of it if we ever ship local wakers.
:+1: Thank you, good catch. I guess we will not be deprecating.
The final comment period, with a disposition to merge, as per the , is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon.
I... realize that this stabilization process has gone on for a while, so totally understand if you don't want to bother with re-doing it, but: Given that is clearly still useful, and may even be used in , I believe that Alternative 2 , stabilizing the fields of , feels like the more coherent solution. (A very hypothetical example: if the fields ever become public, if I end up manually calling the function, I have to construct a to be able to access the fields of the returned from that function, which seems backwards). I'd also like to add, if you don't want to commit to a specific number of fields in , you could consider adding .
It looks like there was a vote to merge, but this issue isn't a PR and it remains open. What's next here?
As I have already , this feature is not really usable with . Could it be possible to add the following implementation? It would allow to match the vtable returned by .
Does not work?
Yes it works, but it requires to already have an allocated , which makes the thing unusable. The goal is to compare the vtable of a random waker to know if I can cast its data pointer to my own waker implementation. I don't want to rely for this comparison on a lazily allocated waker to get its vtable (especially as this lazy waker may requires some parameters random to be initialized). In short, this is unusable. That's too bad, because is a convenient implementation shortcut, but I have to rewrite it myself to use this feature.
If I understand correctly, the FCP has happened and ended with a disposition to merge. Is there some reason why the FCP checkbox in the first comment is still unticked? In terms of the stabilization PR, is that just waiting for someone to do it, or is there some missing prerequisite?
This is just waiting for someone to submit a stabilization PR.
Submitted a stabilization PR:

Discussion:
RawWaker { data, vtable } } <del> /// Gets the `data` pointer used to create this `RawWaker`. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] pub fn data(&self) -> *const () { self.data } /// Gets the `vtable` pointer used to create this `RawWaker`. #[inline] #[must_use] #[unstable(feature = "waker_getters", issue = "96992")] pub fn vtable(&self) -> &'static RawWakerVTable { self.vtable } </del> #[unstable(feature = "noop_waker", issue = "98286")] const NOOP: RawWaker = { const VTABLE: RawWakerVTable = RawWakerVTable::new(

a_data == b_data && ptr::eq(a_vtable, b_vtable) } <ins> /// Creates a new `Waker` from the provided `data` pointer and `vtable`. /// /// The `data` pointer can be used to store arbitrary data as required /// by the executor. This could be e.g. a type-erased pointer to an `Arc` /// that is associated with the task. /// The value of this pointer will get passed to all functions that are part /// of the `vtable` as the first parameter. /// /// It is important to consider that the `data` pointer must point to a /// thread safe type such as an `Arc`. /// /// The `vtable` customizes the behavior of a `Waker`. For each operation /// on the `Waker`, the associated function in the `vtable` will be called. /// /// # Safety /// /// The behavior of the returned `Waker` is undefined if the contract defined /// in [`RawWakerVTable`]'s documentation is not upheld. /// /// (Authors wishing to avoid unsafe code may implement the [`Wake`] trait instead, at the /// cost of a required heap allocation.) /// /// [`Wake`]: ../../alloc/task/trait.Wake.html #[inline] #[must_use] #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] pub const unsafe fn new(data: *const (), vtable: &'static RawWakerVTable) -> Self { Waker { waker: RawWaker { data, vtable } } } </ins> /// Creates a new `Waker` from [`RawWaker`]. /// /// # Safety

WAKER } <del> /// Gets a reference to the underlying [`RawWaker`]. </del> <ins> /// Gets the `data` pointer used to create this `Waker`. </ins> #[inline] #[must_use] <del> #[unstable(feature = "waker_getters", issue = "96992")] pub fn as_raw(&self) -> &RawWaker { &self.waker </del> <ins> #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] pub fn data(&self) -> *const () { self.waker.data } /// Gets the `vtable` pointer used to create this `Waker`. #[inline] #[must_use] #[stable(feature = "waker_getters", since = "CURRENT_RUSTC_VERSION")] pub fn vtable(&self) -> &'static RawWakerVTable { self.waker.vtable </ins> } }

a_data == b_data && ptr::eq(a_vtable, b_vtable) } <ins> /// Creates a new `LocalWaker` from the provided `data` pointer and `vtable`. /// /// The `data` pointer can be used to store arbitrary data as required /// by the executor. This could be e.g. a type-erased pointer to an `Arc` /// that is associated with the task. /// The value of this pointer will get passed to all functions that are part /// of the `vtable` as the first parameter. /// /// The `vtable` customizes the behavior of a `LocalWaker`. For each /// operation on the `LocalWaker`, the associated function in the `vtable` /// will be called. /// /// # Safety /// /// The behavior of the returned `Waker` is undefined if the contract defined /// in [`RawWakerVTable`]'s documentation is not upheld. /// #[inline] #[must_use] #[unstable(feature = "local_waker", issue = "118959")] pub const unsafe fn new(data: *const (), vtable: &'static RawWakerVTable) -> Self { LocalWaker { waker: RawWaker { data, vtable } } } </ins> /// Creates a new `LocalWaker` from [`RawWaker`]. /// /// The behavior of the returned `LocalWaker` is undefined if the contract defined

WAKER } <del> /// Gets a reference to the underlying [`RawWaker`]. </del> <ins> /// Gets the `data` pointer used to create this `LocalWaker`. </ins> #[inline] #[must_use] <del> #[unstable(feature = "waker_getters", issue = "96992")] pub fn as_raw(&self) -> &RawWaker { &self.waker </del> <ins> #[unstable(feature = "local_waker", issue = "118959")] pub fn data(&self) -> *const () { self.waker.data } /// Gets the `vtable` pointer used to create this `LocalWaker`. #[inline] #[must_use] #[unstable(feature = "local_waker", issue = "118959")] pub fn vtable(&self) -> &'static RawWakerVTable { self.waker.vtable </ins> } } #[unstable(feature = "local_waker", issue = "118959")]

#![feature(unsize)] #![feature(unsized_tuple_coercion)] #![feature(unwrap_infallible)] <del> #![feature(waker_getters)] </del> // tidy-alphabetical-end #![allow(internal_features)] #![deny(fuzzy_provenance_casts)]

#[test] fn test_waker_getters() { let raw_waker = RawWaker::new(ptr::without_provenance_mut(42usize), &WAKER_VTABLE); <del> assert_eq!(raw_waker.data() as usize, 42); assert!(ptr::eq(raw_waker.vtable(), &WAKER_VTABLE)); </del> let waker = unsafe { Waker::from_raw(raw_waker) }; <ins> assert_eq!(waker.data() as usize, 42); assert!(ptr::eq(waker.vtable(), &WAKER_VTABLE)); </ins> let waker2 = waker.clone(); <del> let raw_waker2 = waker2.as_raw(); assert_eq!(raw_waker2.data() as usize, 43); assert!(ptr::eq(raw_waker2.vtable(), &WAKER_VTABLE)); </del> <ins> assert_eq!(waker2.data() as usize, 43); assert!(ptr::eq(waker2.vtable(), &WAKER_VTABLE)); </ins> } static WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(