// Copyright 2016 Amanieu d'Antras // // Licensed under the Apache License, Version 2.0, or the MIT license , at your option. This file may not be // copied, modified, or distributed except according to those terms. use cfg_if::cfg_if; use core::{ arch::wasm32, sync::atomic::{AtomicI32, Ordering}, }; use instant::Instant; use std::time::Duration; use std::{convert::TryFrom, thread}; cfg_if! { if #[cfg(all( target_arch = "wasm32", target_os = "unknown", target_vendor = "unknown" ))] { // This function serves as a polyfill for `Instant::checked_duration_since`, which is // currently not implemented for wasm32-unknown-unknown. // TODO: Remove this shim once it fn checked_duration_since_now(other: Instant) -> Option { let now = Instant::now(); if other < now { None } else { Some(other.duration_since(now)) } } } else { // If we are not targeting wasm32, we can use the native `checked_duration_since`. fn checked_duration_since_now(timeout: Instant) -> Option { timeout.checked_duration_since(Instant::now()) } } } // Helper type for putting a thread to sleep until some other thread wakes it up pub struct ThreadParker { parked: AtomicI32, } const UNPARKED: i32 = 0; const PARKED: i32 = 1; impl super::ThreadParkerT for ThreadParker { type UnparkHandle = UnparkHandle; const IS_CHEAP_TO_CONSTRUCT: bool = true; #[inline] fn new() -> ThreadParker { ThreadParker { parked: AtomicI32::new(UNPARKED), } } #[inline] unsafe fn prepare_park(&self) { self.parked.store(PARKED, Ordering::Relaxed); } #[inline] unsafe fn timed_out(&self) -> bool { self.parked.load(Ordering::Relaxed) == PARKED } #[inline] unsafe fn park(&self) { while self.parked.load(Ordering::Acquire) == PARKED { let r = wasm32::memory_atomic_wait32(self.ptr(), PARKED, -1); // we should have either woken up (0) or got a not-equal due to a // race (1). We should never time out (2) debug_assert!(r == 0 || r == 1); } } #[inline] unsafe fn park_until(&self, timeout: Instant) -> bool { while self.parked.load(Ordering::Acquire) == PARKED { if let Some(left) = checked_duration_since_now(timeout) { let nanos_left = i64::try_from(left.as_nanos()).unwrap_or(i64::max_value()); let r = wasm32::memory_atomic_wait32(self.ptr(), PARKED, nanos_left); debug_assert!(r == 0 || r == 1 || r == 2); } else { return false; } } true } #[inline] unsafe fn unpark_lock(&self) -> UnparkHandle { // We don't need to lock anything, just clear the state self.parked.store(UNPARKED, Ordering::Release); UnparkHandle(self.ptr()) } } impl ThreadParker { #[inline] fn ptr(&self) -> *mut i32 { &self.parked as *const AtomicI32 as *mut i32 } } pub struct UnparkHandle(*mut i32); impl super::UnparkHandleT for UnparkHandle { #[inline] unsafe fn unpark(self) { let num_notified = wasm32::memory_atomic_notify(self.0 as *mut i32, 1); debug_assert!(num_notified == 0 || num_notified == 1); } } #[inline] pub fn thread_yield() { thread::yield_now(); }