blob: c99497b6edc34daa57a31a88a231ac25d3e5e700 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::cell::Cell;
use std::future::Future;
use std::future::poll_fn;
use std::pin::Pin;
use std::pin::pin;
use std::sync::Arc;
use std::sync::LazyLock;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::task::Context;
use std::task::Poll;
use std::task::Wake;
use std::task::Waker;
struct BenchTask;
// This deliberately uses `Wake` rather than `Waker::noop()` so waiter registration and wake-up
// exercise the reference-counting work performed by runtime task wakers.
#[allow(clippy::manual_noop_waker)]
impl Wake for BenchTask {
fn wake(self: Arc<Self>) {}
}
static BENCH_WAKER: LazyLock<Waker> = LazyLock::new(|| Waker::from(Arc::new(BenchTask)));
#[inline]
pub fn bench_context() -> Context<'static> {
Context::from_waker(&BENCH_WAKER)
}
pub fn poll_ready<F: Future>(future: F, context: &mut Context<'_>) -> F::Output {
let mut future = pin!(future);
poll_pinned_ready(future.as_mut(), context)
}
pub fn poll_pinned_ready<F: Future>(
mut future: Pin<&mut F>,
context: &mut Context<'_>,
) -> F::Output {
match future.as_mut().poll(context) {
Poll::Ready(output) => output,
Poll::Pending => panic!("benchmark future should be ready"),
}
}
pub fn poll_pending<F: Future>(mut future: Pin<&mut F>, context: &mut Context<'_>) {
assert!(future.as_mut().poll(context).is_pending());
}
static NEXT_THREAD_SLOT: AtomicUsize = AtomicUsize::new(0);
thread_local! {
static THREAD_SLOT: usize = NEXT_THREAD_SLOT.fetch_add(1, Ordering::Relaxed);
static THREAD_TICKET: Cell<usize> = const { Cell::new(0) };
}
// Thread-local tickets avoid adding shared-counter contention to the timed loop.
pub fn thread_slot_ticket() -> (usize, usize) {
let slot = THREAD_SLOT.with(|slot| *slot);
let ticket = THREAD_TICKET.with(|ticket| {
let current = ticket.get();
ticket.set(current.wrapping_add(1));
current
});
(slot, ticket)
}
pub async fn wait_until_open(gate: &Cell<bool>) {
poll_fn(|_| {
if gate.get() {
Poll::Ready(())
} else {
Poll::Pending
}
})
.await
}