1use lv2_core::extension::ExtensionDescriptor;
116use lv2_core::feature::*;
117use lv2_core::plugin::{Plugin, PluginInstance};
118use std::fmt;
119use std::marker::PhantomData;
120use std::mem;
121use std::mem::ManuallyDrop;
122use std::os::raw::*; use std::ptr;
124use urid::*;
125
126#[derive(PartialEq, Eq, Clone, Copy)]
129pub enum ScheduleError<T> {
130 Unknown(T),
132 NoSpace(T),
134 NoCallback(T),
138}
139
140impl<T> fmt::Debug for ScheduleError<T> {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 match *self {
143 ScheduleError::Unknown(..) => "Unknown(..)".fmt(f),
144 ScheduleError::NoSpace(..) => "NoSpace(..)".fmt(f),
145 ScheduleError::NoCallback(..) => "NoCallback(..)".fmt(f),
146 }
147 }
148}
149
150impl<T> fmt::Display for ScheduleError<T> {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match *self {
153 ScheduleError::Unknown(..) => "unknown error".fmt(f),
154 ScheduleError::NoSpace(..) => "not enough space".fmt(f),
155 ScheduleError::NoCallback(..) => "no callback".fmt(f),
156 }
157 }
158}
159
160#[repr(transparent)]
162pub struct Schedule<'a, P> {
163 internal: &'a lv2_sys::LV2_Worker_Schedule,
164 phantom: PhantomData<*const P>,
165}
166
167unsafe impl<'a, P> UriBound for Schedule<'a, P> {
168 const URI: &'static [u8] = lv2_sys::LV2_WORKER__schedule;
169}
170
171unsafe impl<'a, P> Feature for Schedule<'a, P> {
172 unsafe fn from_feature_ptr(feature: *const c_void, class: ThreadingClass) -> Option<Self> {
173 if class == ThreadingClass::Audio {
174 (feature as *const lv2_sys::LV2_Worker_Schedule)
175 .as_ref()
176 .map(|internal| Self {
177 internal,
178 phantom: PhantomData::<*const P>,
179 })
180 } else {
181 panic!("The Worker Schedule feature is only allowed in the audio threading class");
182 }
183 }
184}
185
186impl<'a, P: Worker> Schedule<'a, P> {
187 pub fn schedule_work(&self, worker_data: P::WorkData) -> Result<(), ScheduleError<P::WorkData>>
208 where
209 P::WorkData: 'static + Send,
210 {
211 let worker_data = ManuallyDrop::new(worker_data);
212 let size = mem::size_of_val(&worker_data) as u32;
213 let ptr = &worker_data as *const _ as *const c_void;
214 let schedule_work = if let Some(schedule_work) = self.internal.schedule_work {
215 schedule_work
216 } else {
217 return Err(ScheduleError::NoCallback(ManuallyDrop::into_inner(
218 worker_data,
219 )));
220 };
221 match unsafe { (schedule_work)(self.internal.handle, size, ptr) } {
222 lv2_sys::LV2_Worker_Status_LV2_WORKER_SUCCESS => Ok(()),
223 lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN => Err(ScheduleError::Unknown(
224 ManuallyDrop::into_inner(worker_data),
225 )),
226 lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_NO_SPACE => Err(ScheduleError::NoSpace(
227 ManuallyDrop::into_inner(worker_data),
228 )),
229 _ => Err(ScheduleError::Unknown(ManuallyDrop::into_inner(
230 worker_data,
231 ))),
232 }
233 }
234}
235
236#[derive(PartialEq, Eq, Clone, Copy)]
239pub enum RespondError<T> {
240 Unknown(T),
242 NoSpace(T),
244 NoCallback(T),
248}
249
250impl<T> fmt::Debug for RespondError<T> {
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 match *self {
253 RespondError::Unknown(..) => "Unknown(..)".fmt(f),
254 RespondError::NoSpace(..) => "NoSpace(..)".fmt(f),
255 RespondError::NoCallback(..) => "NoCallback(..)".fmt(f),
256 }
257 }
258}
259
260impl<T> fmt::Display for RespondError<T> {
261 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
262 match *self {
263 RespondError::Unknown(..) => "unknown error".fmt(f),
264 RespondError::NoSpace(..) => "not enough space".fmt(f),
265 RespondError::NoCallback(..) => "no callback".fmt(f),
266 }
267 }
268}
269
270pub struct ResponseHandler<P: Worker> {
275 response_function: lv2_sys::LV2_Worker_Respond_Function,
277 respond_handle: lv2_sys::LV2_Worker_Respond_Handle,
280 phantom: PhantomData<P>,
281}
282
283impl<P: Worker> ResponseHandler<P> {
284 pub fn respond(
291 &self,
292 response_data: P::ResponseData,
293 ) -> Result<(), RespondError<P::ResponseData>>
294 where
295 P::WorkData: 'static + Send,
296 {
297 let response_data = ManuallyDrop::new(response_data);
298 let size = mem::size_of_val(&response_data) as u32;
299 let ptr = &response_data as *const _ as *const c_void;
300 let response_function = if let Some(response_function) = self.response_function {
301 response_function
302 } else {
303 return Err(RespondError::NoCallback(ManuallyDrop::into_inner(
304 response_data,
305 )));
306 };
307 match unsafe { (response_function)(self.respond_handle, size, ptr) } {
308 lv2_sys::LV2_Worker_Status_LV2_WORKER_SUCCESS => Ok(()),
309 lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN => Err(RespondError::Unknown(
310 ManuallyDrop::into_inner(response_data),
311 )),
312 lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_NO_SPACE => Err(RespondError::NoSpace(
313 ManuallyDrop::into_inner(response_data),
314 )),
315 _ => Err(RespondError::Unknown(ManuallyDrop::into_inner(
316 response_data,
317 ))),
318 }
319 }
320}
321
322#[derive(PartialEq, Eq, Clone, Copy, Debug)]
324pub enum WorkerError {
325 Unknown,
327 NoSpace,
329}
330
331pub trait Worker: Plugin {
340 type WorkData: 'static + Send;
342 type ResponseData: 'static + Send;
344 fn work(
355 response_handler: &ResponseHandler<Self>,
356 data: Self::WorkData,
357 ) -> Result<(), WorkerError>;
358
359 fn work_response(
363 &mut self,
364 _data: Self::ResponseData,
365 _features: &mut Self::AudioFeatures,
366 ) -> Result<(), WorkerError> {
367 Ok(())
368 }
369
370 fn end_run(&mut self, _features: &mut Self::AudioFeatures) -> Result<(), WorkerError> {
375 Ok(())
376 }
377}
378
379pub struct WorkerDescriptor<P: Worker> {
383 plugin: PhantomData<P>,
384}
385
386unsafe impl<P: Worker> UriBound for WorkerDescriptor<P> {
387 const URI: &'static [u8] = lv2_sys::LV2_WORKER__interface;
388}
389
390impl<P: Worker> WorkerDescriptor<P> {
391 unsafe extern "C" fn extern_work(
393 _handle: lv2_sys::LV2_Handle,
394 response_function: lv2_sys::LV2_Worker_Respond_Function,
395 respond_handle: lv2_sys::LV2_Worker_Respond_Handle,
396 size: u32,
397 data: *const c_void,
398 ) -> lv2_sys::LV2_Worker_Status {
399 let response_handler = ResponseHandler {
401 response_function,
402 respond_handle,
403 phantom: PhantomData::<P>,
404 };
405 let worker_data =
407 ptr::read_unaligned(data as *const mem::ManuallyDrop<<P as Worker>::WorkData>);
408 let worker_data = mem::ManuallyDrop::into_inner(worker_data);
409 if size as usize != mem::size_of_val(&worker_data) {
410 return lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN;
411 }
412 match P::work(&response_handler, worker_data) {
413 Ok(()) => lv2_sys::LV2_Worker_Status_LV2_WORKER_SUCCESS,
414 Err(WorkerError::Unknown) => lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN,
415 Err(WorkerError::NoSpace) => lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_NO_SPACE,
416 }
417 }
418
419 unsafe extern "C" fn extern_work_response(
421 handle: lv2_sys::LV2_Handle,
422 size: u32,
423 body: *const c_void,
424 ) -> lv2_sys::LV2_Worker_Status {
425 let plugin_instance =
427 if let Some(plugin_instance) = (handle as *mut PluginInstance<P>).as_mut() {
428 plugin_instance
429 } else {
430 return lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN;
431 };
432 let response_data =
434 ptr::read_unaligned(body as *const mem::ManuallyDrop<<P as Worker>::ResponseData>);
435 let response_data = mem::ManuallyDrop::into_inner(response_data);
436 if size as usize != mem::size_of_val(&response_data) {
437 return lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN;
438 }
439
440 let (instance, features) = plugin_instance.audio_class_handle();
441 match instance.work_response(response_data, features) {
442 Ok(()) => lv2_sys::LV2_Worker_Status_LV2_WORKER_SUCCESS,
443 Err(WorkerError::Unknown) => lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN,
444 Err(WorkerError::NoSpace) => lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_NO_SPACE,
445 }
446 }
447
448 unsafe extern "C" fn extern_end_run(handle: lv2_sys::LV2_Handle) -> lv2_sys::LV2_Worker_Status {
450 if let Some(plugin_instance) = (handle as *mut PluginInstance<P>).as_mut() {
451 let (instance, features) = plugin_instance.audio_class_handle();
452 match instance.end_run(features) {
453 Ok(()) => lv2_sys::LV2_Worker_Status_LV2_WORKER_SUCCESS,
454 Err(WorkerError::Unknown) => lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN,
455 Err(WorkerError::NoSpace) => lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_NO_SPACE,
456 }
457 } else {
458 lv2_sys::LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN
459 }
460 }
461}
462
463impl<P: Worker> ExtensionDescriptor for WorkerDescriptor<P> {
465 type ExtensionInterface = lv2_sys::LV2_Worker_Interface;
466
467 const INTERFACE: &'static lv2_sys::LV2_Worker_Interface = &lv2_sys::LV2_Worker_Interface {
468 work: Some(Self::extern_work),
469 work_response: Some(Self::extern_work_response),
470 end_run: Some(Self::extern_end_run),
471 };
472}
473
474#[cfg(test)]
475mod tests {
476 use super::*;
477 use lv2_core::prelude::*;
478 use lv2_sys::*;
479 use std::fmt;
480 use std::mem;
481 use std::ops;
482 use std::ptr;
483
484 struct HasDrop {
486 drop_count: u32,
487 drop_limit: u32,
488 }
489
490 impl HasDrop {
491 fn new(val: u32) -> Self {
492 Self {
493 drop_count: 0,
494 drop_limit: val,
495 }
496 }
497 }
498
499 impl ops::Drop for HasDrop {
500 fn drop(&mut self) {
501 if self.drop_count >= self.drop_limit {
502 panic!("Dropped more than {} time", self.drop_limit);
503 } else {
504 self.drop_count += 1;
505 }
506 }
507 }
508
509 impl fmt::Display for HasDrop {
510 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
511 write!(f, "HasDrop variable")
512 }
513 }
514
515 #[derive(PortCollection)]
516 struct Ports {}
517
518 struct TestDropWorker;
519
520 unsafe impl<'a> UriBound for TestDropWorker {
522 const URI: &'static [u8] = b"not relevant\0";
523 }
524
525 impl Plugin for TestDropWorker {
526 type Ports = Ports;
527 type InitFeatures = ();
528 type AudioFeatures = ();
529
530 fn new(_plugin_info: &PluginInfo, _features: &mut Self::InitFeatures) -> Option<Self> {
531 Some(Self {})
532 }
533
534 fn run(&mut self, _ports: &mut Ports, _features: &mut Self::InitFeatures, _: u32) {}
535 }
536
537 impl Worker for TestDropWorker {
538 type WorkData = HasDrop;
539 type ResponseData = HasDrop;
540
541 fn work(
542 _response_handler: &ResponseHandler<Self>,
543 _data: HasDrop,
544 ) -> Result<(), WorkerError> {
545 Ok(())
546 }
547
548 fn work_response(
549 &mut self,
550 _data: HasDrop,
551 _features: &mut Self::AudioFeatures,
552 ) -> Result<(), WorkerError> {
553 Ok(())
554 }
555 }
556
557 extern "C" fn extern_schedule(
558 _handle: LV2_Worker_Schedule_Handle,
559 _size: u32,
560 _data: *const c_void,
561 ) -> LV2_Worker_Status {
562 LV2_Worker_Status_LV2_WORKER_SUCCESS
563 }
564
565 extern "C" fn faulty_schedule(
566 _handle: LV2_Worker_Schedule_Handle,
567 _size: u32,
568 _data: *const c_void,
569 ) -> LV2_Worker_Status {
570 LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN
571 }
572
573 extern "C" fn extern_respond(
574 _handle: LV2_Worker_Respond_Handle,
575 _size: u32,
576 _data: *const c_void,
577 ) -> LV2_Worker_Status {
578 LV2_Worker_Status_LV2_WORKER_SUCCESS
579 }
580
581 extern "C" fn faulty_respond(
582 _handle: LV2_Worker_Respond_Handle,
583 _size: u32,
584 _data: *const c_void,
585 ) -> LV2_Worker_Status {
586 LV2_Worker_Status_LV2_WORKER_ERR_UNKNOWN
587 }
588
589 #[test]
590 fn schedule_must_not_drop() {
591 let hd = HasDrop::new(0);
592 let internal = lv2_sys::LV2_Worker_Schedule {
593 handle: ptr::null_mut(),
594 schedule_work: Some(extern_schedule),
595 };
596 let schedule = Schedule {
597 internal: &internal,
598 phantom: PhantomData::<*const TestDropWorker>,
599 };
600 let _ = schedule.schedule_work(hd);
601 }
602
603 #[test]
604 #[should_panic(expected = "Dropped")]
605 fn schedule_must_enable_drop_on_error() {
606 let hd = HasDrop::new(0);
607 let internal = lv2_sys::LV2_Worker_Schedule {
608 handle: ptr::null_mut(),
609 schedule_work: Some(faulty_schedule),
610 };
611 let schedule = Schedule {
612 internal: &internal,
613 phantom: PhantomData::<*const TestDropWorker>,
614 };
615 let _ = schedule.schedule_work(hd);
616 }
617
618 #[test]
619 fn respond_must_not_drop() {
620 let hd = HasDrop::new(0);
621 let respond = ResponseHandler {
622 response_function: Some(extern_respond),
623 respond_handle: ptr::null_mut(),
624 phantom: PhantomData::<TestDropWorker>,
625 };
626 let _ = respond.respond(hd);
627 }
628
629 #[test]
630 #[should_panic(expected = "Dropped")]
631 fn respond_must_enable_drop_on_error() {
632 let hd = HasDrop::new(0);
633 let respond = ResponseHandler {
634 response_function: Some(faulty_respond),
635 respond_handle: ptr::null_mut(),
636 phantom: PhantomData::<TestDropWorker>,
637 };
638 let _ = respond.respond(hd);
639 }
640
641 #[test]
642 #[should_panic(expected = "Dropped")]
643 fn extern_work_should_drop() {
644 let hd = mem::ManuallyDrop::new(HasDrop::new(0));
645 let ptr_hd = &hd as *const _ as *const c_void;
646 let size = mem::size_of_val(&hd) as u32;
647 let mut tdw = TestDropWorker {};
648
649 let ptr_tdw = &mut tdw as *mut _ as *mut c_void;
650 unsafe {
652 WorkerDescriptor::<TestDropWorker>::extern_work(
653 ptr_tdw,
654 Some(extern_respond),
655 ptr::null_mut(),
656 size,
657 ptr_hd,
658 );
659 }
660 }
661
662 #[test]
663 fn extern_work_should_not_drop_twice() {
664 let hd = mem::ManuallyDrop::new(HasDrop::new(1));
665 let ptr_hd = &hd as *const _ as *const c_void;
666 let size = mem::size_of_val(&hd) as u32;
667 let mut tdw = TestDropWorker {};
668
669 let ptr_tdw = &mut tdw as *mut _ as *mut c_void;
670 unsafe {
672 WorkerDescriptor::<TestDropWorker>::extern_work(
673 ptr_tdw,
674 Some(extern_respond),
675 ptr::null_mut(),
676 size,
677 ptr_hd,
678 );
679 }
680 }
681
682 #[test]
683 #[should_panic(expected = "Dropped")]
684 fn extern_work_response_should_drop() {
685 let hd = mem::ManuallyDrop::new(HasDrop::new(0));
686 let ptr_hd = &hd as *const _ as *const c_void;
687 let size = mem::size_of_val(&hd) as u32;
688 let mut tdw = TestDropWorker {};
689
690 let ptr_tdw = &mut tdw as *mut _ as *mut c_void;
691 unsafe {
693 WorkerDescriptor::<TestDropWorker>::extern_work_response(ptr_tdw, size, ptr_hd);
694 }
695 }
696
697 #[test]
698 fn extern_work_response_should_not_drop_twice() {
699 let hd = mem::ManuallyDrop::new(HasDrop::new(1));
700 let ptr_hd = &hd as *const _ as *const c_void;
701 let size = mem::size_of_val(&hd) as u32;
702 let mut tdw = TestDropWorker {};
703
704 let ptr_tdw = &mut tdw as *mut _ as *mut c_void;
705 unsafe {
707 WorkerDescriptor::<TestDropWorker>::extern_work_response(ptr_tdw, size, ptr_hd);
708 }
709 }
710}