Migrate 20 crates to monorepo

oneshot-uniffi
minimal-lexical
num_enum
num_enum_derive
num-complex
num-derive
paste
pin-utils
predicates
protobuf-parse
protobuf-support
quote
remain
rustc-demangle-capi
rustix
shared_child
thiserror-impl
threadpool
tikv-jemalloc-sys
tonic

Bug: http://b/339424309
Test: treehugger
Change-Id: I0bd9f3c002026c5ce48eb95ae961088264058960
diff --git a/crates/oneshot-uniffi/tests/assert_mem.rs b/crates/oneshot-uniffi/tests/assert_mem.rs
new file mode 100644
index 0000000..a993ad7
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/assert_mem.rs
@@ -0,0 +1,37 @@
+use oneshot::{Receiver, Sender};
+use std::mem;
+
+/// Just sanity check that both channel endpoints stay the size of a single pointer.
+#[test]
+fn channel_endpoints_single_pointer() {
+    const PTR_SIZE: usize = mem::size_of::<*const ()>();
+
+    assert_eq!(mem::size_of::<Sender<()>>(), PTR_SIZE);
+    assert_eq!(mem::size_of::<Receiver<()>>(), PTR_SIZE);
+
+    assert_eq!(mem::size_of::<Sender<u8>>(), PTR_SIZE);
+    assert_eq!(mem::size_of::<Receiver<u8>>(), PTR_SIZE);
+
+    assert_eq!(mem::size_of::<Sender<[u8; 1024]>>(), PTR_SIZE);
+    assert_eq!(mem::size_of::<Receiver<[u8; 1024]>>(), PTR_SIZE);
+
+    assert_eq!(mem::size_of::<Option<Sender<[u8; 1024]>>>(), PTR_SIZE);
+    assert_eq!(mem::size_of::<Option<Receiver<[u8; 1024]>>>(), PTR_SIZE);
+}
+
+/// Check that the `SendError` stays small. Useful to automatically detect if it is refactored
+/// to become large. We do not want the stack requirement for calling `Sender::send` to grow.
+#[test]
+fn error_sizes() {
+    const PTR_SIZE: usize = mem::size_of::<usize>();
+
+    assert_eq!(mem::size_of::<oneshot::SendError<()>>(), PTR_SIZE);
+    assert_eq!(mem::size_of::<oneshot::SendError<u8>>(), PTR_SIZE);
+    assert_eq!(mem::size_of::<oneshot::SendError<[u8; 1024]>>(), PTR_SIZE);
+
+    // The type returned from `Sender::send` is also just pointer sized
+    assert_eq!(
+        mem::size_of::<Result<(), oneshot::SendError<[u8; 1024]>>>(),
+        PTR_SIZE
+    );
+}
diff --git a/crates/oneshot-uniffi/tests/async.rs b/crates/oneshot-uniffi/tests/async.rs
new file mode 100644
index 0000000..59acbf5
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/async.rs
@@ -0,0 +1,129 @@
+#![cfg(not(soong))]
+#![cfg(all(feature = "async", not(loom)))]
+
+use core::mem;
+use core::time::Duration;
+
+mod helpers;
+use helpers::DropCounter;
+
+#[tokio::test]
+async fn send_before_await_tokio() {
+    let (sender, receiver) = oneshot::channel();
+    assert!(sender.send(19i128).is_ok());
+    assert_eq!(receiver.await, Ok(19i128));
+}
+
+#[async_std::test]
+async fn send_before_await_async_std() {
+    let (sender, receiver) = oneshot::channel();
+    assert!(sender.send(19i128).is_ok());
+    assert_eq!(receiver.await, Ok(19i128));
+}
+
+#[tokio::test]
+async fn await_with_dropped_sender_tokio() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+    mem::drop(sender);
+    receiver.await.unwrap_err();
+}
+
+#[async_std::test]
+async fn await_with_dropped_sender_async_std() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+    mem::drop(sender);
+    receiver.await.unwrap_err();
+}
+
+#[tokio::test]
+async fn await_before_send_tokio() {
+    let (sender, receiver) = oneshot::channel();
+    let (message, counter) = DropCounter::new(79u128);
+    let t = tokio::spawn(async move {
+        tokio::time::sleep(Duration::from_millis(10)).await;
+        sender.send(message)
+    });
+    let returned_message = receiver.await.unwrap();
+    assert_eq!(counter.count(), 0);
+    assert_eq!(*returned_message.value(), 79u128);
+    mem::drop(returned_message);
+    assert_eq!(counter.count(), 1);
+    t.await.unwrap().unwrap();
+}
+
+#[async_std::test]
+async fn await_before_send_async_std() {
+    let (sender, receiver) = oneshot::channel();
+    let (message, counter) = DropCounter::new(79u128);
+    let t = async_std::task::spawn(async move {
+        async_std::task::sleep(Duration::from_millis(10)).await;
+        sender.send(message)
+    });
+    let returned_message = receiver.await.unwrap();
+    assert_eq!(counter.count(), 0);
+    assert_eq!(*returned_message.value(), 79u128);
+    mem::drop(returned_message);
+    assert_eq!(counter.count(), 1);
+    t.await.unwrap();
+}
+
+#[tokio::test]
+async fn await_before_send_then_drop_sender_tokio() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+    let t = tokio::spawn(async {
+        tokio::time::sleep(Duration::from_millis(10)).await;
+        mem::drop(sender);
+    });
+    assert!(receiver.await.is_err());
+    t.await.unwrap();
+}
+
+#[async_std::test]
+async fn await_before_send_then_drop_sender_async_std() {
+    let (sender, receiver) = oneshot::channel::<u128>();
+    let t = async_std::task::spawn(async {
+        async_std::task::sleep(Duration::from_millis(10)).await;
+        mem::drop(sender);
+    });
+    assert!(receiver.await.is_err());
+    t.await;
+}
+
+// Tests that the Receiver handles being used synchronously even after being polled
+#[tokio::test]
+async fn poll_future_and_then_try_recv() {
+    use core::future::Future;
+    use core::pin::Pin;
+    use core::task::{self, Poll};
+
+    struct StupidReceiverFuture(oneshot::Receiver<()>);
+
+    impl Future for StupidReceiverFuture {
+        type Output = Result<(), oneshot::RecvError>;
+
+        fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
+            let poll_result = Future::poll(Pin::new(&mut self.0), cx);
+            self.0.try_recv().expect_err("Should never be a message");
+            poll_result
+        }
+    }
+
+    let (sender, receiver) = oneshot::channel();
+    let t = tokio::spawn(async {
+        tokio::time::sleep(Duration::from_millis(20)).await;
+        mem::drop(sender);
+    });
+    StupidReceiverFuture(receiver).await.unwrap_err();
+    t.await.unwrap();
+}
+
+#[tokio::test]
+async fn poll_receiver_then_drop_it() {
+    let (sender, receiver) = oneshot::channel::<()>();
+    // This will poll the receiver and then give up after 100 ms.
+    tokio::time::timeout(Duration::from_millis(100), receiver)
+        .await
+        .unwrap_err();
+    // Make sure the receiver has been dropped by the runtime.
+    assert!(sender.send(()).is_err());
+}
diff --git a/crates/oneshot-uniffi/tests/future.rs b/crates/oneshot-uniffi/tests/future.rs
new file mode 100644
index 0000000..3895946
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/future.rs
@@ -0,0 +1,65 @@
+#![cfg(feature = "async")]
+
+use core::{future, mem, pin, task};
+
+#[cfg(loom)]
+pub use loom::sync::{Arc, Mutex};
+#[cfg(not(loom))]
+pub use std::sync::{Arc, Mutex};
+
+mod helpers;
+use helpers::maybe_loom_model;
+
+#[test]
+fn multiple_receiver_polls_keeps_only_latest_waker() {
+    #[derive(Default)]
+    struct MockWaker {
+        cloned: usize,
+        dropped: usize,
+    }
+
+    fn clone_mock_waker(waker: *const ()) -> task::RawWaker {
+        let mock_waker = unsafe { Arc::from_raw(waker as *const Mutex<MockWaker>) };
+        mock_waker.lock().unwrap().cloned += 1;
+        let new_waker =
+            task::RawWaker::new(Arc::into_raw(mock_waker.clone()) as *const (), &VTABLE);
+        mem::forget(mock_waker);
+        new_waker
+    }
+
+    fn drop_mock_waker(waker: *const ()) {
+        let mock_waker = unsafe { Arc::from_raw(waker as *const Mutex<MockWaker>) };
+        mock_waker.lock().unwrap().dropped += 1;
+    }
+
+    const VTABLE: task::RawWakerVTable =
+        task::RawWakerVTable::new(clone_mock_waker, |_| (), |_| (), drop_mock_waker);
+
+    maybe_loom_model(|| {
+        let mock_waker1 = Arc::new(Mutex::new(MockWaker::default()));
+        let raw_waker1 =
+            task::RawWaker::new(Arc::into_raw(mock_waker1.clone()) as *const (), &VTABLE);
+        let waker1 = unsafe { task::Waker::from_raw(raw_waker1) };
+        let mut context1 = task::Context::from_waker(&waker1);
+
+        let (_sender, mut receiver) = oneshot::channel::<()>();
+
+        let poll_result = future::Future::poll(pin::Pin::new(&mut receiver), &mut context1);
+        assert_eq!(poll_result, task::Poll::Pending);
+        assert_eq!(mock_waker1.lock().unwrap().cloned, 1);
+        assert_eq!(mock_waker1.lock().unwrap().dropped, 0);
+
+        let mock_waker2 = Arc::new(Mutex::new(MockWaker::default()));
+        let raw_waker2 =
+            task::RawWaker::new(Arc::into_raw(mock_waker2.clone()) as *const (), &VTABLE);
+        let waker2 = unsafe { task::Waker::from_raw(raw_waker2) };
+        let mut context2 = task::Context::from_waker(&waker2);
+
+        let poll_result = future::Future::poll(pin::Pin::new(&mut receiver), &mut context2);
+        assert_eq!(poll_result, task::Poll::Pending);
+        assert_eq!(mock_waker2.lock().unwrap().cloned, 1);
+        assert_eq!(mock_waker2.lock().unwrap().dropped, 0);
+        assert_eq!(mock_waker1.lock().unwrap().cloned, 1);
+        assert_eq!(mock_waker1.lock().unwrap().dropped, 1);
+    });
+}
diff --git a/crates/oneshot-uniffi/tests/helpers/mod.rs b/crates/oneshot-uniffi/tests/helpers/mod.rs
new file mode 100644
index 0000000..1b14539
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/helpers/mod.rs
@@ -0,0 +1,63 @@
+#![allow(dead_code)]
+
+extern crate alloc;
+
+#[cfg(not(loom))]
+use alloc::sync::Arc;
+#[cfg(not(loom))]
+use core::sync::atomic::{AtomicUsize, Ordering::SeqCst};
+#[cfg(loom)]
+use loom::sync::{
+    atomic::{AtomicUsize, Ordering::SeqCst},
+    Arc,
+};
+
+#[cfg(loom)]
+pub mod waker;
+
+pub fn maybe_loom_model(test: impl Fn() + Sync + Send + 'static) {
+    #[cfg(loom)]
+    loom::model(test);
+    #[cfg(not(loom))]
+    test();
+}
+
+pub struct DropCounter<T> {
+    drop_count: Arc<AtomicUsize>,
+    value: Option<T>,
+}
+
+pub struct DropCounterHandle(Arc<AtomicUsize>);
+
+impl<T> DropCounter<T> {
+    pub fn new(value: T) -> (Self, DropCounterHandle) {
+        let drop_count = Arc::new(AtomicUsize::new(0));
+        (
+            Self {
+                drop_count: drop_count.clone(),
+                value: Some(value),
+            },
+            DropCounterHandle(drop_count),
+        )
+    }
+
+    pub fn value(&self) -> &T {
+        self.value.as_ref().unwrap()
+    }
+
+    pub fn into_value(mut self) -> T {
+        self.value.take().unwrap()
+    }
+}
+
+impl DropCounterHandle {
+    pub fn count(&self) -> usize {
+        self.0.load(SeqCst)
+    }
+}
+
+impl<T> Drop for DropCounter<T> {
+    fn drop(&mut self) {
+        self.drop_count.fetch_add(1, SeqCst);
+    }
+}
diff --git a/crates/oneshot-uniffi/tests/helpers/waker.rs b/crates/oneshot-uniffi/tests/helpers/waker.rs
new file mode 100644
index 0000000..2e3f1be
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/helpers/waker.rs
@@ -0,0 +1,64 @@
+//! Creates a Waker that can be observed from tests.
+
+use std::mem::forget;
+use std::sync::atomic::{AtomicU32, Ordering};
+use std::sync::Arc;
+use std::task::{RawWaker, RawWakerVTable, Waker};
+
+#[derive(Default)]
+pub struct WakerHandle {
+    clone_count: AtomicU32,
+    drop_count: AtomicU32,
+    wake_count: AtomicU32,
+}
+
+impl WakerHandle {
+    pub fn clone_count(&self) -> u32 {
+        self.clone_count.load(Ordering::Relaxed)
+    }
+
+    pub fn drop_count(&self) -> u32 {
+        self.drop_count.load(Ordering::Relaxed)
+    }
+
+    pub fn wake_count(&self) -> u32 {
+        self.wake_count.load(Ordering::Relaxed)
+    }
+}
+
+pub fn waker() -> (Waker, Arc<WakerHandle>) {
+    let waker_handle = Arc::new(WakerHandle::default());
+    let waker_handle_ptr = Arc::into_raw(waker_handle.clone());
+    let raw_waker = RawWaker::new(waker_handle_ptr as *const _, waker_vtable());
+    (unsafe { Waker::from_raw(raw_waker) }, waker_handle)
+}
+
+pub(super) fn waker_vtable() -> &'static RawWakerVTable {
+    &RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw)
+}
+
+unsafe fn clone_raw(data: *const ()) -> RawWaker {
+    let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
+    handle.clone_count.fetch_add(1, Ordering::Relaxed);
+    forget(handle.clone());
+    forget(handle);
+    RawWaker::new(data, waker_vtable())
+}
+
+unsafe fn wake_raw(data: *const ()) {
+    let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
+    handle.wake_count.fetch_add(1, Ordering::Relaxed);
+    handle.drop_count.fetch_add(1, Ordering::Relaxed);
+}
+
+unsafe fn wake_by_ref_raw(data: *const ()) {
+    let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
+    handle.wake_count.fetch_add(1, Ordering::Relaxed);
+    forget(handle)
+}
+
+unsafe fn drop_raw(data: *const ()) {
+    let handle: Arc<WakerHandle> = Arc::from_raw(data as *const _);
+    handle.drop_count.fetch_add(1, Ordering::Relaxed);
+    drop(handle)
+}
diff --git a/crates/oneshot-uniffi/tests/loom.rs b/crates/oneshot-uniffi/tests/loom.rs
new file mode 100644
index 0000000..a7625a4
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/loom.rs
@@ -0,0 +1,223 @@
+#![cfg(loom)]
+
+use oneshot::TryRecvError;
+
+use loom::hint;
+use loom::thread;
+#[cfg(feature = "async")]
+use std::future::Future;
+#[cfg(feature = "async")]
+use std::pin::Pin;
+#[cfg(feature = "async")]
+use std::task::{self, Poll};
+#[cfg(feature = "std")]
+use std::time::Duration;
+
+mod helpers;
+
+#[test]
+fn try_recv() {
+    loom::model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+
+        let t = thread::spawn(move || loop {
+            match receiver.try_recv() {
+                Ok(msg) => break msg,
+                Err(TryRecvError::Empty) => hint::spin_loop(),
+                Err(TryRecvError::Disconnected) => panic!("Should not be disconnected"),
+            }
+        });
+
+        assert!(sender.send(19).is_ok());
+        assert_eq!(t.join().unwrap(), 19);
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn send_recv_different_threads() {
+    loom::model(|| {
+        let (sender, receiver) = oneshot::channel();
+        let t2 = thread::spawn(move || {
+            assert_eq!(receiver.recv_timeout(Duration::from_millis(1)), Ok(9));
+        });
+        let t1 = thread::spawn(move || {
+            sender.send(9u128).unwrap();
+        });
+        t1.join().unwrap();
+        t2.join().unwrap();
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn recv_drop_sender_different_threads() {
+    loom::model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        let t2 = thread::spawn(move || {
+            assert!(receiver.recv_timeout(Duration::from_millis(0)).is_err());
+        });
+        let t1 = thread::spawn(move || {
+            drop(sender);
+        });
+        t1.join().unwrap();
+        t2.join().unwrap();
+    })
+}
+
+#[cfg(feature = "async")]
+#[test]
+fn async_recv() {
+    loom::model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        let t1 = thread::spawn(move || {
+            sender.send(987).unwrap();
+        });
+        assert_eq!(loom::future::block_on(receiver), Ok(987));
+        t1.join().unwrap();
+    })
+}
+
+#[cfg(feature = "async")]
+#[test]
+fn send_then_poll() {
+    loom::model(|| {
+        let (sender, mut receiver) = oneshot::channel::<u128>();
+        sender.send(1234).unwrap();
+
+        let (waker, waker_handle) = helpers::waker::waker();
+        let mut context = task::Context::from_waker(&waker);
+
+        assert_eq!(
+            Pin::new(&mut receiver).poll(&mut context),
+            Poll::Ready(Ok(1234))
+        );
+        assert_eq!(waker_handle.clone_count(), 0);
+        assert_eq!(waker_handle.drop_count(), 0);
+        assert_eq!(waker_handle.wake_count(), 0);
+    })
+}
+
+#[cfg(feature = "async")]
+#[test]
+fn poll_then_send() {
+    loom::model(|| {
+        let (sender, mut receiver) = oneshot::channel::<u128>();
+
+        let (waker, waker_handle) = helpers::waker::waker();
+        let mut context = task::Context::from_waker(&waker);
+
+        assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
+        assert_eq!(waker_handle.clone_count(), 1);
+        assert_eq!(waker_handle.drop_count(), 0);
+        assert_eq!(waker_handle.wake_count(), 0);
+
+        sender.send(1234).unwrap();
+        assert_eq!(waker_handle.clone_count(), 1);
+        assert_eq!(waker_handle.drop_count(), 1);
+        assert_eq!(waker_handle.wake_count(), 1);
+
+        assert_eq!(
+            Pin::new(&mut receiver).poll(&mut context),
+            Poll::Ready(Ok(1234))
+        );
+        assert_eq!(waker_handle.clone_count(), 1);
+        assert_eq!(waker_handle.drop_count(), 1);
+        assert_eq!(waker_handle.wake_count(), 1);
+    })
+}
+
+#[cfg(feature = "async")]
+#[test]
+fn poll_with_different_wakers() {
+    loom::model(|| {
+        let (sender, mut receiver) = oneshot::channel::<u128>();
+
+        let (waker1, waker_handle1) = helpers::waker::waker();
+        let mut context1 = task::Context::from_waker(&waker1);
+
+        assert_eq!(Pin::new(&mut receiver).poll(&mut context1), Poll::Pending);
+        assert_eq!(waker_handle1.clone_count(), 1);
+        assert_eq!(waker_handle1.drop_count(), 0);
+        assert_eq!(waker_handle1.wake_count(), 0);
+
+        let (waker2, waker_handle2) = helpers::waker::waker();
+        let mut context2 = task::Context::from_waker(&waker2);
+
+        assert_eq!(Pin::new(&mut receiver).poll(&mut context2), Poll::Pending);
+        assert_eq!(waker_handle1.clone_count(), 1);
+        assert_eq!(waker_handle1.drop_count(), 1);
+        assert_eq!(waker_handle1.wake_count(), 0);
+
+        assert_eq!(waker_handle2.clone_count(), 1);
+        assert_eq!(waker_handle2.drop_count(), 0);
+        assert_eq!(waker_handle2.wake_count(), 0);
+
+        // Sending should cause the waker from the latest poll to be woken up
+        sender.send(1234).unwrap();
+        assert_eq!(waker_handle1.clone_count(), 1);
+        assert_eq!(waker_handle1.drop_count(), 1);
+        assert_eq!(waker_handle1.wake_count(), 0);
+
+        assert_eq!(waker_handle2.clone_count(), 1);
+        assert_eq!(waker_handle2.drop_count(), 1);
+        assert_eq!(waker_handle2.wake_count(), 1);
+    })
+}
+
+#[cfg(feature = "async")]
+#[test]
+fn poll_then_try_recv() {
+    loom::model(|| {
+        let (_sender, mut receiver) = oneshot::channel::<u128>();
+
+        let (waker, waker_handle) = helpers::waker::waker();
+        let mut context = task::Context::from_waker(&waker);
+
+        assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
+        assert_eq!(waker_handle.clone_count(), 1);
+        assert_eq!(waker_handle.drop_count(), 0);
+        assert_eq!(waker_handle.wake_count(), 0);
+
+        assert_eq!(receiver.try_recv(), Err(TryRecvError::Empty));
+
+        assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
+        assert_eq!(waker_handle.clone_count(), 2);
+        assert_eq!(waker_handle.drop_count(), 1);
+        assert_eq!(waker_handle.wake_count(), 0);
+    })
+}
+
+#[cfg(feature = "async")]
+#[test]
+fn poll_then_try_recv_while_sending() {
+    loom::model(|| {
+        let (sender, mut receiver) = oneshot::channel::<u128>();
+
+        let (waker, waker_handle) = helpers::waker::waker();
+        let mut context = task::Context::from_waker(&waker);
+
+        assert_eq!(Pin::new(&mut receiver).poll(&mut context), Poll::Pending);
+        assert_eq!(waker_handle.clone_count(), 1);
+        assert_eq!(waker_handle.drop_count(), 0);
+        assert_eq!(waker_handle.wake_count(), 0);
+
+        let t = thread::spawn(move || {
+            sender.send(1234).unwrap();
+        });
+
+        let msg = loop {
+            match receiver.try_recv() {
+                Ok(msg) => break msg,
+                Err(TryRecvError::Empty) => hint::spin_loop(),
+                Err(TryRecvError::Disconnected) => panic!("Should not be disconnected"),
+            }
+        };
+        assert_eq!(msg, 1234);
+        assert_eq!(waker_handle.clone_count(), 1);
+        assert_eq!(waker_handle.drop_count(), 1);
+        assert_eq!(waker_handle.wake_count(), 1);
+
+        t.join().unwrap();
+    })
+}
diff --git a/crates/oneshot-uniffi/tests/raw.rs b/crates/oneshot-uniffi/tests/raw.rs
new file mode 100644
index 0000000..e38dc45
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/raw.rs
@@ -0,0 +1,46 @@
+#![cfg(not(loom))]
+
+use oneshot::{channel, Receiver, Sender};
+
+#[test]
+fn test_raw_sender() {
+    let (sender, receiver) = channel::<u32>();
+    let raw = sender.into_raw();
+    let recreated = unsafe { Sender::<u32>::from_raw(raw) };
+    recreated
+        .send(100)
+        .unwrap_or_else(|e| panic!("error sending after into_raw/from_raw roundtrip: {e}"));
+    assert_eq!(receiver.try_recv(), Ok(100))
+}
+
+#[test]
+fn test_raw_receiver() {
+    let (sender, receiver) = channel::<u32>();
+    let raw = receiver.into_raw();
+    sender.send(100).unwrap();
+    let recreated = unsafe { Receiver::<u32>::from_raw(raw) };
+    assert_eq!(
+        recreated
+            .try_recv()
+            .unwrap_or_else(|e| panic!("error receiving after into_raw/from_raw roundtrip: {e}")),
+        100
+    )
+}
+
+#[test]
+fn test_raw_sender_and_receiver() {
+    let (sender, receiver) = channel::<u32>();
+    let raw_receiver = receiver.into_raw();
+    let raw_sender = sender.into_raw();
+
+    let recreated_sender = unsafe { Sender::<u32>::from_raw(raw_sender) };
+    recreated_sender.send(100).unwrap();
+
+    let recreated_receiver = unsafe { Receiver::<u32>::from_raw(raw_receiver) };
+    assert_eq!(
+        recreated_receiver
+            .try_recv()
+            .unwrap_or_else(|e| panic!("error receiving after into_raw/from_raw roundtrip: {e}")),
+        100
+    )
+}
diff --git a/crates/oneshot-uniffi/tests/sync.rs b/crates/oneshot-uniffi/tests/sync.rs
new file mode 100644
index 0000000..c6ba081
--- /dev/null
+++ b/crates/oneshot-uniffi/tests/sync.rs
@@ -0,0 +1,343 @@
+use core::mem;
+use oneshot::TryRecvError;
+
+#[cfg(feature = "std")]
+use oneshot::{RecvError, RecvTimeoutError};
+#[cfg(feature = "std")]
+use std::time::{Duration, Instant};
+
+#[cfg(feature = "std")]
+mod thread {
+    #[cfg(loom)]
+    pub use loom::thread::spawn;
+    #[cfg(not(loom))]
+    pub use std::thread::{sleep, spawn};
+
+    #[cfg(loom)]
+    pub fn sleep(_timeout: core::time::Duration) {
+        loom::thread::yield_now()
+    }
+}
+
+mod helpers;
+use helpers::{maybe_loom_model, DropCounter};
+
+#[test]
+fn send_before_try_recv() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        assert!(sender.send(19i128).is_ok());
+
+        assert_eq!(receiver.try_recv(), Ok(19i128));
+        assert_eq!(receiver.try_recv(), Err(TryRecvError::Disconnected));
+        #[cfg(feature = "std")]
+        {
+            assert_eq!(receiver.recv_ref(), Err(RecvError));
+            assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err());
+        }
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn send_before_recv() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<()>();
+        assert!(sender.send(()).is_ok());
+        assert_eq!(receiver.recv(), Ok(()));
+    });
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u8>();
+        assert!(sender.send(19).is_ok());
+        assert_eq!(receiver.recv(), Ok(19));
+    });
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u64>();
+        assert!(sender.send(21).is_ok());
+        assert_eq!(receiver.recv(), Ok(21));
+    });
+    // FIXME: This test does not work with loom. There is something that happens after the
+    // channel object becomes larger than ~500 bytes and that makes an atomic read from the state
+    // result in "signal: 10, SIGBUS: access to undefined memory"
+    #[cfg(not(loom))]
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<[u8; 4096]>();
+        assert!(sender.send([0b10101010; 4096]).is_ok());
+        assert!(receiver.recv().unwrap()[..] == [0b10101010; 4096][..]);
+    });
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn send_before_recv_ref() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        assert!(sender.send(19i128).is_ok());
+
+        assert_eq!(receiver.recv_ref(), Ok(19i128));
+        assert_eq!(receiver.recv_ref(), Err(RecvError));
+        assert_eq!(receiver.try_recv(), Err(TryRecvError::Disconnected));
+        assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err());
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn send_before_recv_timeout() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        assert!(sender.send(19i128).is_ok());
+
+        let start = Instant::now();
+        let timeout = Duration::from_secs(1);
+        assert_eq!(receiver.recv_timeout(timeout), Ok(19i128));
+        assert!(start.elapsed() < Duration::from_millis(100));
+
+        assert!(receiver.recv_timeout(timeout).is_err());
+        assert!(receiver.try_recv().is_err());
+        assert!(receiver.recv().is_err());
+    })
+}
+
+#[test]
+fn send_then_drop_receiver() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        assert!(sender.send(19i128).is_ok());
+        mem::drop(receiver);
+    })
+}
+
+#[test]
+fn send_with_dropped_receiver() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        mem::drop(receiver);
+        let send_error = sender.send(5u128).unwrap_err();
+        assert_eq!(*send_error.as_inner(), 5);
+        assert_eq!(send_error.into_inner(), 5);
+    })
+}
+
+#[test]
+fn try_recv_with_dropped_sender() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        mem::drop(sender);
+        receiver.try_recv().unwrap_err();
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn recv_with_dropped_sender() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        mem::drop(sender);
+        receiver.recv().unwrap_err();
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn recv_before_send() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        let t = thread::spawn(move || {
+            thread::sleep(Duration::from_millis(2));
+            sender.send(9u128).unwrap();
+        });
+        assert_eq!(receiver.recv(), Ok(9));
+        t.join().unwrap();
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn recv_timeout_before_send() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        let t = thread::spawn(move || {
+            thread::sleep(Duration::from_millis(2));
+            sender.send(9u128).unwrap();
+        });
+        assert_eq!(receiver.recv_timeout(Duration::from_secs(1)), Ok(9));
+        t.join().unwrap();
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn recv_before_send_then_drop_sender() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        let t = thread::spawn(move || {
+            thread::sleep(Duration::from_millis(10));
+            mem::drop(sender);
+        });
+        assert!(receiver.recv().is_err());
+        t.join().unwrap();
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn recv_timeout_before_send_then_drop_sender() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        let t = thread::spawn(move || {
+            thread::sleep(Duration::from_millis(10));
+            mem::drop(sender);
+        });
+        assert!(receiver.recv_timeout(Duration::from_secs(1)).is_err());
+        t.join().unwrap();
+    })
+}
+
+#[test]
+fn try_recv() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        assert_eq!(receiver.try_recv(), Err(TryRecvError::Empty));
+        mem::drop(sender)
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn try_recv_then_drop_receiver() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel::<u128>();
+        let t1 = thread::spawn(move || {
+            let _ = sender.send(42);
+        });
+        let t2 = thread::spawn(move || {
+            assert!(matches!(
+                receiver.try_recv(),
+                Ok(42) | Err(TryRecvError::Empty)
+            ));
+            mem::drop(receiver);
+        });
+        t1.join().unwrap();
+        t2.join().unwrap();
+    })
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn recv_deadline_and_timeout_no_time() {
+    maybe_loom_model(|| {
+        let (_sender, receiver) = oneshot::channel::<u128>();
+
+        let start = Instant::now();
+        assert_eq!(
+            receiver.recv_deadline(start),
+            Err(RecvTimeoutError::Timeout)
+        );
+        assert!(start.elapsed() < Duration::from_millis(200));
+
+        let start = Instant::now();
+        assert_eq!(
+            receiver.recv_timeout(Duration::from_millis(0)),
+            Err(RecvTimeoutError::Timeout)
+        );
+        assert!(start.elapsed() < Duration::from_millis(200));
+    })
+}
+
+// This test doesn't give meaningful results when run with oneshot_test_delay and loom
+#[cfg(all(feature = "std", not(all(oneshot_test_delay, loom))))]
+#[test]
+fn recv_deadline_time_should_elapse() {
+    maybe_loom_model(|| {
+        let (_sender, receiver) = oneshot::channel::<u128>();
+
+        let start = Instant::now();
+        #[cfg(not(loom))]
+        let timeout = Duration::from_millis(100);
+        #[cfg(loom)]
+        let timeout = Duration::from_millis(1);
+        assert_eq!(
+            receiver.recv_deadline(start + timeout),
+            Err(RecvTimeoutError::Timeout)
+        );
+        assert!(start.elapsed() > timeout);
+        assert!(start.elapsed() < timeout * 3);
+    })
+}
+
+#[cfg(all(feature = "std", not(all(oneshot_test_delay, loom))))]
+#[test]
+fn recv_timeout_time_should_elapse() {
+    maybe_loom_model(|| {
+        let (_sender, receiver) = oneshot::channel::<u128>();
+
+        let start = Instant::now();
+        #[cfg(not(loom))]
+        let timeout = Duration::from_millis(100);
+        #[cfg(loom)]
+        let timeout = Duration::from_millis(1);
+
+        assert_eq!(
+            receiver.recv_timeout(timeout),
+            Err(RecvTimeoutError::Timeout)
+        );
+        assert!(start.elapsed() > timeout);
+        assert!(start.elapsed() < timeout * 3);
+    })
+}
+
+#[cfg(not(loom))]
+#[test]
+fn non_send_type_can_be_used_on_same_thread() {
+    use std::ptr;
+
+    #[derive(Debug, Eq, PartialEq)]
+    struct NotSend(*mut ());
+
+    let (sender, receiver) = oneshot::channel();
+    sender.send(NotSend(ptr::null_mut())).unwrap();
+    let reply = receiver.try_recv().unwrap();
+    assert_eq!(reply, NotSend(ptr::null_mut()));
+}
+
+#[test]
+fn message_in_channel_dropped_on_receiver_drop() {
+    maybe_loom_model(|| {
+        let (sender, receiver) = oneshot::channel();
+        let (message, counter) = DropCounter::new(());
+        assert_eq!(counter.count(), 0);
+        sender.send(message).unwrap();
+        assert_eq!(counter.count(), 0);
+        mem::drop(receiver);
+        assert_eq!(counter.count(), 1);
+    })
+}
+
+#[test]
+fn send_error_drops_message_correctly() {
+    maybe_loom_model(|| {
+        let (sender, _) = oneshot::channel();
+        let (message, counter) = DropCounter::new(());
+
+        let send_error = sender.send(message).unwrap_err();
+        assert_eq!(counter.count(), 0);
+        mem::drop(send_error);
+        assert_eq!(counter.count(), 1);
+    });
+}
+
+#[test]
+fn send_error_drops_message_correctly_on_into_inner() {
+    maybe_loom_model(|| {
+        let (sender, _) = oneshot::channel();
+        let (message, counter) = DropCounter::new(());
+
+        let send_error = sender.send(message).unwrap_err();
+        assert_eq!(counter.count(), 0);
+        let message = send_error.into_inner();
+        assert_eq!(counter.count(), 0);
+        mem::drop(message);
+        assert_eq!(counter.count(), 1);
+    });
+}