Buttplug sex toy control library
at dev 6.5 kB view raw
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}