Buttplug sex toy control library
at master 3.7 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 8use buttplug_core::util::{async_manager, sleep}; 9use futures::Future; 10use std::{ 11 sync::{ 12 Arc, 13 atomic::{AtomicBool, Ordering}, 14 }, 15 time::Duration, 16}; 17use tokio::{ 18 select, 19 sync::{Notify, mpsc}, 20}; 21 22pub enum PingMessage { 23 Ping, 24 StartTimer, 25 StopTimer, 26 End, 27} 28 29async fn ping_timer( 30 max_ping_time: u32, 31 mut ping_msg_receiver: mpsc::Receiver<PingMessage>, 32 notifier: Arc<Notify>, 33 pinged_out_status: Arc<AtomicBool>, 34) { 35 let mut started = false; 36 let mut pinged = false; 37 loop { 38 select! { 39 _ = sleep(Duration::from_millis(max_ping_time.into())) => { 40 if started { 41 if !pinged { 42 notifier.notify_waiters(); 43 pinged_out_status.store(true, Ordering::Relaxed); 44 return; 45 } 46 pinged = false; 47 } 48 } 49 msg = ping_msg_receiver.recv() => { 50 if msg.is_none() { 51 return; 52 } 53 match msg.expect("Already checked") { 54 PingMessage::StartTimer => started = true, 55 PingMessage::StopTimer => started = false, 56 PingMessage::Ping => pinged = true, 57 PingMessage::End => break, 58 } 59 } 60 }; 61 } 62} 63 64pub struct PingTimer { 65 max_ping_time: u32, 66 ping_msg_sender: mpsc::Sender<PingMessage>, 67 ping_timeout_notifier: Arc<Notify>, 68 pinged_out: Arc<AtomicBool>, 69} 70 71impl Drop for PingTimer { 72 fn drop(&mut self) { 73 // This cannot block, otherwise it will throw in WASM contexts on 74 // destruction. We must use send(), not blocking_send(). 75 let sender = self.ping_msg_sender.clone(); 76 async_manager::spawn(async move { 77 if sender.send(PingMessage::End).await.is_err() { 78 debug!("Receiver does not exist, assuming ping timer event loop already dead."); 79 } 80 }); 81 } 82} 83 84impl PingTimer { 85 pub fn new(max_ping_time: u32) -> Self { 86 let ping_timeout_notifier = Arc::new(Notify::new()); 87 let (sender, receiver) = mpsc::channel(256); 88 let pinged_out = Arc::new(AtomicBool::new(false)); 89 if max_ping_time > 0 { 90 let fut = ping_timer( 91 max_ping_time, 92 receiver, 93 ping_timeout_notifier.clone(), 94 pinged_out.clone(), 95 ); 96 async_manager::spawn(fut); 97 } 98 Self { 99 max_ping_time, 100 ping_msg_sender: sender, 101 ping_timeout_notifier, 102 pinged_out, 103 } 104 } 105 106 pub fn ping_timeout_waiter(&self) -> impl Future<Output = ()> + use<> { 107 let notify = self.ping_timeout_notifier.clone(); 108 async move { 109 notify.notified().await; 110 } 111 } 112 113 fn send_ping_msg(&self, msg: PingMessage) -> impl Future<Output = ()> + use<> { 114 let ping_msg_sender = self.ping_msg_sender.clone(); 115 let max_ping_time = self.max_ping_time; 116 async move { 117 if max_ping_time == 0 { 118 return; 119 } 120 if ping_msg_sender.send(msg).await.is_err() { 121 error!("Cannot ping, no event loop available."); 122 } 123 } 124 } 125 126 pub fn start_ping_timer(&self) -> impl Future<Output = ()> + use<> { 127 // If we're starting the timer, clear our status. 128 self.pinged_out.store(false, Ordering::Relaxed); 129 self.send_ping_msg(PingMessage::StartTimer) 130 } 131 132 pub fn stop_ping_timer(&self) -> impl Future<Output = ()> + use<> { 133 self.send_ping_msg(PingMessage::StopTimer) 134 } 135 136 pub fn update_ping_time(&self) -> impl Future<Output = ()> + use<> { 137 self.send_ping_msg(PingMessage::Ping) 138 } 139 140 pub fn pinged_out(&self) -> bool { 141 self.pinged_out.load(Ordering::Relaxed) 142 } 143}