async-await 구현: Rust로 만든 간단한 런타임

Rust의 런타임이 어떻게 작동하는지 이해하기 위해 최소한의 외부 의존성 없이 간단한 런타임을 구현했습니다. 이 런타임은 복잡한 스케줄링 알고리즘 없이 단일 전역 작업 큐를 사용합니다.

다음 코드는 멀티 프로듀서, 멀티 컨슈머(MPMC) 부분만 crossbeam 크레이트를 사용하고 나머지는 직접 작성했습니다.

예제 코드:

fn main() {
    let runtime = SimpleRuntime::new();

    for i in 1..=20 {
        runtime.spawn(async move {
            let result = DummyIO::new(Duration::from_secs(i)).await;
            println!("{:?}: {:?}", thread::current().id(), result);
        });
    }

    runtime.run(4); // 4개의 스레드 사용
}

DummyIO 구조체도 매우 단순합니다:

pub struct DummyIO {
    done: Arc<AtomicBool>,
    delay: Duration,
}

impl Future for DummyIO {
    type Output = Duration;

    fn poll(
        self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Self::Output> {
        if self.done.load(Ordering::Acquire) {
            return Poll::Ready(self.delay);
        }

        let done = self.done.clone();
        let waker = cx.waker().clone();
        let delay = self.delay;

        thread::spawn(move || {
            thread::sleep(delay);

            done.store(true, Ordering::Release);
            waker.wake();
        });

        Poll::Pending
    }
}

데이터 구조

다음과 같은 데이터 구조를 사용하였습니다(Tokio의 설계를 참고):

struct Task {
    raw: RawTask,
}

unsafe impl Send for Task {}
unsafe impl Sync for Task {}

struct RawTask {
    ptr: NonNull<Header>, // Cell<T> 포인터, 여기서 T는 Future
}

struct Header {
    state: Mutex<State>,
    vtable: &'static Vtable,
    sender: crossbeam::channel::Sender<Task>,
}

#[derive(Default)]
struct State {
    running: bool,
    notified: bool,
    completed: bool,
}

#[repr(C)]
struct Cell<T: Future> {
    header: Header,
    future: T,
    output: Option<T::Output>,
}

struct Vtable {
    poll_task: unsafe fn(NonNull<Header>),
    clone_task: unsafe fn(NonNull<Header>) -> NonNull<Header>,
    drop_task: unsafe fn(NonNull<Header>),
}

주요 사항:

  • RawTaskptr은 실제로 NonNull<Cell<T: Future>>을 가리킵니다.
  • Cell<T: Future>에는 #[repr(C)]가 적용되어 있으며, 이에 대한 설명은 주석에 있습니다.
  • vtable의 설계는 Waker의 vtable을 참고하여 타입 정보를 저장하고 나중에 원래 타입으로 복원할 수 있도록 합니다.

GitHub에서 전체 코드를 확인하세요.

즐겁게 코딩하세요!

태그: Rust async-await 런타임 concurrency Futures

7월 24일 17:58에 게시됨