1 // Copyright 2016 Amanieu d'Antras
2 //
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
7 
8 use core::{
9     arch::wasm32,
10     sync::atomic::{AtomicI32, Ordering},
11 };
12 use std::time::{Duration, Instant};
13 use std::{convert::TryFrom, thread};
14 
15 // Helper type for putting a thread to sleep until some other thread wakes it up
16 pub struct ThreadParker {
17     parked: AtomicI32,
18 }
19 
20 const UNPARKED: i32 = 0;
21 const PARKED: i32 = 1;
22 
23 impl super::ThreadParkerT for ThreadParker {
24     type UnparkHandle = UnparkHandle;
25 
26     const IS_CHEAP_TO_CONSTRUCT: bool = true;
27 
28     #[inline]
new() -> ThreadParker29     fn new() -> ThreadParker {
30         ThreadParker {
31             parked: AtomicI32::new(UNPARKED),
32         }
33     }
34 
35     #[inline]
prepare_park(&self)36     unsafe fn prepare_park(&self) {
37         self.parked.store(PARKED, Ordering::Relaxed);
38     }
39 
40     #[inline]
timed_out(&self) -> bool41     unsafe fn timed_out(&self) -> bool {
42         self.parked.load(Ordering::Relaxed) == PARKED
43     }
44 
45     #[inline]
park(&self)46     unsafe fn park(&self) {
47         while self.parked.load(Ordering::Acquire) == PARKED {
48             let r = wasm32::memory_atomic_wait32(self.ptr(), PARKED, -1);
49             // we should have either woken up (0) or got a not-equal due to a
50             // race (1). We should never time out (2)
51             debug_assert!(r == 0 || r == 1);
52         }
53     }
54 
55     #[inline]
park_until(&self, timeout: Instant) -> bool56     unsafe fn park_until(&self, timeout: Instant) -> bool {
57         while self.parked.load(Ordering::Acquire) == PARKED {
58             if let Some(left) = timeout.checked_duration_since(Instant::now()) {
59                 let nanos_left = i64::try_from(left.as_nanos()).unwrap_or(i64::max_value());
60                 let r = wasm32::memory_atomic_wait32(self.ptr(), PARKED, nanos_left);
61                 debug_assert!(r == 0 || r == 1 || r == 2);
62             } else {
63                 return false;
64             }
65         }
66         true
67     }
68 
69     #[inline]
unpark_lock(&self) -> UnparkHandle70     unsafe fn unpark_lock(&self) -> UnparkHandle {
71         // We don't need to lock anything, just clear the state
72         self.parked.store(UNPARKED, Ordering::Release);
73         UnparkHandle(self.ptr())
74     }
75 }
76 
77 impl ThreadParker {
78     #[inline]
ptr(&self) -> *mut i3279     fn ptr(&self) -> *mut i32 {
80         &self.parked as *const AtomicI32 as *mut i32
81     }
82 }
83 
84 pub struct UnparkHandle(*mut i32);
85 
86 impl super::UnparkHandleT for UnparkHandle {
87     #[inline]
unpark(self)88     unsafe fn unpark(self) {
89         let num_notified = wasm32::memory_atomic_notify(self.0 as *mut i32, 1);
90         debug_assert!(num_notified == 0 || num_notified == 1);
91     }
92 }
93 
94 #[inline]
thread_yield()95 pub fn thread_yield() {
96     thread::yield_now();
97 }
98