Skip to content

LocalWaker drops itself when cloning #52629

@Thomasdezeeuw

Description

@Thomasdezeeuw

Running the following code:

#![feature(futures_api)]

use std::ptr::NonNull;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::task::{LocalWaker, Waker, UnsafeWake};

pub fn new_waker() -> LocalWaker {
    let waker = Box::new(MyWaker { count: AtomicUsize::new(0) });
    let ptr: *const dyn UnsafeWake = Box::into_raw(waker);
    let ptr = unsafe { NonNull::new_unchecked(ptr as *mut _) };
    unsafe { LocalWaker::new(ptr) }
}

#[derive(Debug)]
struct MyWaker {
    count: AtomicUsize,
}

unsafe impl UnsafeWake for MyWaker {
    unsafe fn clone_raw(&self) -> Waker {
        println!("clone_raw");
        let ptr: *const UnsafeWake = self;
        let ptr = NonNull::new_unchecked(ptr as *mut _);
        self.count.fetch_add(1, Ordering::SeqCst);
        Waker::new(ptr)
    }

    unsafe fn drop_raw(&self) {
        println!("drop_raw");
        if self.count.fetch_sub(1, Ordering::SeqCst) == 1 {
            // TODO: actually drop self.
        }
    }

    unsafe fn wake(&self) {
        println!("wake");
    }
}

fn main() {
    let local_waker1 = new_waker();
    let local_waker2  = local_waker1.clone();

    local_waker2.wake();

    drop(local_waker1);
    drop(local_waker2);
}

Prints the following:

clone_raw
drop_raw                     # The problem.
wake
drop_raw
drop_raw

It has an extra call to drop_raw.

I think it's because of the Clone implementation of LocalWaker:

unsafe {
        LocalWaker { inner: self.inner.as_ref().clone_raw().inner }
}

As UnsafeWake::clone_raw returns an Waker the LocalWaker code just takes the inner value. However because of this the Waker will be dropped and hence the erroneous extra call to drop_raw.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions