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 cfg_if::cfg_if;
9 use core::{
10     arch::wasm32,
11     sync::atomic::{AtomicI32, Ordering},
12 };
13 use instant::Instant;
14 use std::time::Duration;
15 use std::{convert::TryFrom, thread};
16 
17 cfg_if! {
18     if #[cfg(all(
19         target_arch = "wasm32",
20         target_os = "unknown",
21         target_vendor = "unknown"
22     ))] {
23         // This function serves as a polyfill for `Instant::checked_duration_since`, which is
24         // currently not implemented for wasm32-unknown-unknown.
25         // TODO: Remove this shim once it
26         fn checked_duration_since_now(other: Instant) -> Option<Duration> {
27             let now = Instant::now();
28 
29             if other < now {
30                 None
31             } else {
32                 Some(other.duration_since(now))
33             }
34         }
35     } else {
36         // If we are not targeting wasm32, we can use the native `checked_duration_since`.
37         fn checked_duration_since_now(timeout: Instant) -> Option<Duration> {
38             timeout.checked_duration_since(Instant::now())
39         }
40     }
41 }
42 
43 // Helper type for putting a thread to sleep until some other thread wakes it up
44 pub struct ThreadParker {
45     parked: AtomicI32,
46 }
47 
48 const UNPARKED: i32 = 0;
49 const PARKED: i32 = 1;
50 
51 impl super::ThreadParkerT for ThreadParker {
52     type UnparkHandle = UnparkHandle;
53 
54     const IS_CHEAP_TO_CONSTRUCT: bool = true;
55 
56     #[inline]
new() -> ThreadParker57     fn new() -> ThreadParker {
58         ThreadParker {
59             parked: AtomicI32::new(UNPARKED),
60         }
61     }
62 
63     #[inline]
prepare_park(&self)64     unsafe fn prepare_park(&self) {
65         self.parked.store(PARKED, Ordering::Relaxed);
66     }
67 
68     #[inline]
timed_out(&self) -> bool69     unsafe fn timed_out(&self) -> bool {
70         self.parked.load(Ordering::Relaxed) == PARKED
71     }
72 
73     #[inline]
park(&self)74     unsafe fn park(&self) {
75         while self.parked.load(Ordering::Acquire) == PARKED {
76             let r = wasm32::memory_atomic_wait32(self.ptr(), PARKED, -1);
77             // we should have either woken up (0) or got a not-equal due to a
78             // race (1). We should never time out (2)
79             debug_assert!(r == 0 || r == 1);
80         }
81     }
82 
83     #[inline]
park_until(&self, timeout: Instant) -> bool84     unsafe fn park_until(&self, timeout: Instant) -> bool {
85         while self.parked.load(Ordering::Acquire) == PARKED {
86             if let Some(left) = checked_duration_since_now(timeout) {
87                 let nanos_left = i64::try_from(left.as_nanos()).unwrap_or(i64::max_value());
88                 let r = wasm32::memory_atomic_wait32(self.ptr(), PARKED, nanos_left);
89                 debug_assert!(r == 0 || r == 1 || r == 2);
90             } else {
91                 return false;
92             }
93         }
94         true
95     }
96 
97     #[inline]
unpark_lock(&self) -> UnparkHandle98     unsafe fn unpark_lock(&self) -> UnparkHandle {
99         // We don't need to lock anything, just clear the state
100         self.parked.store(UNPARKED, Ordering::Release);
101         UnparkHandle(self.ptr())
102     }
103 }
104 
105 impl ThreadParker {
106     #[inline]
ptr(&self) -> *mut i32107     fn ptr(&self) -> *mut i32 {
108         &self.parked as *const AtomicI32 as *mut i32
109     }
110 }
111 
112 pub struct UnparkHandle(*mut i32);
113 
114 impl super::UnparkHandleT for UnparkHandle {
115     #[inline]
unpark(self)116     unsafe fn unpark(self) {
117         let num_notified = wasm32::memory_atomic_notify(self.0 as *mut i32, 1);
118         debug_assert!(num_notified == 0 || num_notified == 1);
119     }
120 }
121 
122 #[inline]
thread_yield()123 pub fn thread_yield() {
124     thread::yield_now();
125 }
126