Buttplug sex toy control library
1// Buttplug Rust Source Code File - See https://buttplug.io for more info.
2//
3// Copyright 2016-2024 Nonpolynomial Labs LLC. All rights reserved.
4//
5// Licensed under the BSD 3-Clause license. See LICENSE file in the project root
6// for full license information.
7
8//! Buttplug futures utilities. Mostly used for building message futures in the
9//! client, used to wait on responses from the server.
10
11use core::pin::Pin;
12use futures::{
13 future::Future,
14 task::{Context, Poll, Waker},
15};
16use std::sync::{Arc, Mutex, MutexGuard};
17
18/// Struct used for facilitating resolving futures across contexts.
19///
20/// Since ButtplugFuture is [Pinned][Pin], we can't just go passing it around
21/// tasks or threads. This struct is therefore used to get replies from other
22/// contexts while letting the future stay pinned. It holds the reply to the
23/// future, as well as a [Waker] for waking up the future when the reply is set.
24#[derive(Debug, Clone)]
25pub struct ButtplugFutureState<T> {
26 reply: Option<T>,
27 waker: Option<Waker>,
28}
29
30// For some reason, deriving default above doesn't work, but doing an explicit
31// derive here does work.
32impl<T> Default for ButtplugFutureState<T> {
33 fn default() -> Self {
34 ButtplugFutureState::<T> {
35 reply: None,
36 waker: None,
37 }
38 }
39}
40
41impl<T> ButtplugFutureState<T> {
42 /// Sets the response for the future, firing the waker.
43 ///
44 /// When a response is received from whatever we're waiting on, this function
45 /// takes the response, updates the state struct, and calls [Waker::wake] so
46 /// that the corresponding future can finish.
47 ///
48 /// # Panics
49 ///
50 /// If the reply is set twice, the library will panic. We have no way of
51 /// resolving two replies to the same future, so this is considered a
52 /// catastrophic error.
53 pub fn set_reply(&mut self, reply: T) {
54 if self.reply.is_some() {
55 panic!("set_reply_msg called multiple times on the same future.");
56 }
57
58 self.reply = Some(reply);
59
60 if self.waker.is_some() {
61 self.waker.take().expect("Already checked validity").wake();
62 }
63 }
64}
65
66/// Shared [ButtplugFutureState] type.
67///
68/// [ButtplugFutureState] is made to be shared across tasks, and we'll never
69/// know if those tasks are running on single or multithreaded executors.
70///
71/// # Panics and notes on setting replies
72///
73/// The lock for a [ButtplugFutureState] should only ever be taken when the
74/// reply is being set (which the `set_reply` method does internally), and there
75/// should never be a point where the reply is set twice (See the panic
76/// documentation for [ButtplugFutureState]). In order to make sure we never
77/// block, we always lock using try_lock with .expect(). If try_lock fails, this
78/// means we're already in a double reply situation, and therefore we'll panic
79/// on the .expect(). Any panic from this should be considered a library error
80/// and reported as a bug.
81#[derive(Debug)]
82pub struct ButtplugFutureStateShared<T> {
83 /// The internal state of the future. When `set_reply` is run, we fill this in
84 /// with the value we want the related future to resolve with.
85 state: Arc<Mutex<ButtplugFutureState<T>>>,
86}
87
88impl<T> ButtplugFutureStateShared<T> {
89 pub fn new(state: ButtplugFutureState<T>) -> Self {
90 Self {
91 state: Arc::new(Mutex::new(state)),
92 }
93 }
94
95 /// Locks and returns a [MutexGuard].
96 ///
97 /// See [ButtplugFutureStateShared] struct documentation for more info on
98 /// locking.
99 ///
100 /// # Visibility
101 ///
102 /// The only thing that needs to read the reply from a future is our poll
103 /// method, in this module. Everything else should just be setting replies,
104 /// and can use set_reply accordingly.
105 pub(super) fn lock(&self) -> MutexGuard<'_, ButtplugFutureState<T>> {
106 self
107 .state
108 .lock()
109 .expect("There should never be lock contention for a buttplug future.")
110 }
111
112 /// Locks immediately and sets the reply for the internal waker, or panics if
113 /// lock is held.
114 ///
115 /// See [ButtplugFutureStateShared] struct documentation for more info on
116 /// locking.
117 pub fn set_reply(&self, reply: T) {
118 self.lock().set_reply(reply);
119 }
120}
121
122impl<T> Default for ButtplugFutureStateShared<T> {
123 fn default() -> Self {
124 Self {
125 state: Arc::new(Mutex::new(ButtplugFutureState::<T>::default())),
126 }
127 }
128}
129
130impl<T> Clone for ButtplugFutureStateShared<T> {
131 fn clone(&self) -> Self {
132 Self {
133 state: self.state.clone(),
134 }
135 }
136}
137
138/// [Future] implementation for long operations in Buttplug.
139///
140/// This is a convenience struct, used for handling indeterminately long
141/// operations, like Buttplug's request/reply communications between the client
142/// and server. It allows us to say what type we expect back, then hold a waker
143/// that we can pass around as needed.
144#[derive(Debug)]
145pub struct ButtplugFuture<T> {
146 /// State that holds the waker for the future, and the reply (once set).
147 ///
148 /// ## Notes
149 ///
150 /// This needs to be an [Arc]<[Mutex]<T>> in order to make it mutable under
151 /// pinning when dealing with being a future. There is a chance we could do
152 /// this as a [Pin::get_unchecked_mut] borrow, which would be way faster, but
153 /// that's dicey and hasn't been proven as needed for speed yet.
154 waker_state: ButtplugFutureStateShared<T>,
155}
156
157// TODO Should we implement drop on this?
158//
159// It'd be nice if the future would yell if its dropping and the waker didn't
160// fire? Otherwise it seems like we could have quiet deadlocks.
161
162impl<T> Default for ButtplugFuture<T> {
163 fn default() -> Self {
164 ButtplugFuture::<T> {
165 waker_state: ButtplugFutureStateShared::<T>::default(),
166 }
167 }
168}
169
170impl<T> ButtplugFuture<T> {
171 /// Returns a clone of the state, used for moving the state across contexts
172 /// (tasks/threads/etc...).
173 pub fn get_state_clone(&self) -> ButtplugFutureStateShared<T> {
174 self.waker_state.clone()
175 }
176}
177
178impl<T> Future for ButtplugFuture<T> {
179 type Output = T;
180
181 /// Wakes up when the Output type reply has been set in the
182 /// [ButtplugFutureStateShared].
183 fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
184 // This is the only place lock_now_or_panic should be called, since we're
185 // reading the value.
186 let mut waker_state = self.waker_state.lock();
187 if waker_state.reply.is_some() {
188 let msg = waker_state.reply.take().expect("Already checked validity");
189 Poll::Ready(msg)
190 } else {
191 waker_state.waker = Some(cx.waker().clone());
192 Poll::Pending
193 }
194 }
195}