lv2_worker/
lib.rs

1//! Work scheduling library that allows real-time capable LV2 plugins to execute non-real-time actions.
2//!
3//! This crate allows plugins to schedule work that must be performed in another thread.
4//! Plugins can use this interface to safely perform work that is not real-time safe, and receive
5//! the result in the run context. A typical use case is a sampler reading and caching data from
6//! disk. You can look at the
7//! [LV2 Worker Specification](https://lv2plug.in/ns/ext/worker/worker.html) for more details.
8//!
9//! # Example
10//!```
11//!use std::any::Any;
12//!use lv2_core::feature::*;
13//!use lv2_core::prelude::*;
14//!use urid::*;
15//!use lv2_worker::*;
16//!
17//!#[derive(PortCollection)]
18//!struct Ports {}
19//!
20//!/// Requested features
21//!#[derive(FeatureCollection)]
22//!struct AudioFeatures<'a> {
23//!    ///host feature allowing to schedule some work
24//!    schedule: Schedule<'a, EgWorker>,
25//!}
26//!
27//!//custom datatype
28//!struct WorkMessage {
29//!    cycle: usize,
30//!    task: usize,
31//!}
32//!
33//!/// A plugin that do some work in another thread
34//!struct EgWorker {
35//!    // The schedule handler needs to know the plugin type in order to access the `WorkData` type.
36//!    cycle: usize,
37//!    end_cycle: usize,
38//!}
39//!
40//!/// URI identifier
41//!unsafe impl UriBound for EgWorker {
42//!    const URI: &'static [u8] = b"urn:rust-lv2-more-examples:eg-worker-rs\0";
43//!}
44//!
45//!impl Plugin for EgWorker {
46//!    type Ports = Ports;
47//!    type InitFeatures = ();
48//!    type AudioFeatures = AudioFeatures<'static>;
49//!
50//!    fn new(_plugin_info: &PluginInfo, _features: &mut Self::InitFeatures) -> Option<Self> {
51//!        Some(Self {
52//!            cycle: 0,
53//!            end_cycle: 1,
54//!        })
55//!    }
56//!
57//!    fn run(&mut self, _ports: &mut Ports, features: &mut Self::AudioFeatures, _: u32) {
58//!        self.cycle += 1;
59//!        let cycle = self.cycle;
60//!        println!("cycle {} started", cycle);
61//!        for task in 0..10 {
62//!            let work = WorkMessage { cycle, task };
63//!            // schedule some work, passing some data and check for error
64//!            if let Err(e) = features.schedule.schedule_work(work) {
65//!                eprintln!("Can't schedule work: {}", e);
66//!            }
67//!        }
68//!    }
69//!
70//!    fn extension_data(uri: &Uri) -> Option<&'static dyn Any> {
71//!        match_extensions![uri, WorkerDescriptor<Self>]
72//!    }
73//!}
74//!
75//!/// Implementing the extension.
76//!impl Worker for EgWorker {
77//!    // data type sent by the schedule handler and received by the `work` method.
78//!    type WorkData = WorkMessage;
79//!    // data type sent by the response handler and received by the `work_response` method.
80//!    type ResponseData = String;
81//!    fn work(
82//!        //response handler need to know the plugin type.
83//!        response_handler: &ResponseHandler<Self>,
84//!        data: Self::WorkData,
85//!    ) -> Result<(), WorkerError> {
86//!        println!("work received: cycle {}, task {}", data.cycle, data.task);
87//!        if data.task >= 5 {
88//!            if let Err(e) = response_handler.respond(format!(
89//!                "response to cycle {}, task {}",
90//!                data.cycle, data.task
91//!            )) {
92//!                eprintln!("Can't respond: {}", e);
93//!            }
94//!        };
95//!        Ok(())
96//!    }
97//!
98//!    fn work_response(
99//!        &mut self,
100//!        data: Self::ResponseData,
101//!        _features: &mut Self::AudioFeatures,
102//!    ) -> Result<(), WorkerError> {
103//!        println!("work_response received: {}", data);
104//!        Ok(())
105//!    }
106//!
107//!    fn end_run(&mut self, _features: &mut Self::AudioFeatures) -> Result<(), WorkerError> {
108//!        println!("cycle {} ended", self.end_cycle);
109//!        self.end_cycle += 1;
110//!        Ok(())
111//!    }
112//!}
113//!```
114
115use 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::*; //get all common c_type
123use std::ptr;
124use urid::*;
125
126/// Errors potentially generated by the
127/// [`Schedule::schedule_work`](struct.Schedule.html#method.schedule_work) method
128#[derive(PartialEq, Eq, Clone, Copy)]
129pub enum ScheduleError<T> {
130    /// Unknown or general error
131    Unknown(T),
132    /// Failure due to a lack of space
133    NoSpace(T),
134    /// No `schedule_work` callback was provided by the host
135    ///
136    /// This can only happen with faulty host
137    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/// Host feature providing data to build a ScheduleHandler.
161#[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    /// Request the host to call the worker thread.
188    ///
189    /// If this method fails, the data is considered as untransmitted and is returned to the caller.
190    ///
191    /// This method should be called from `run()` context to request that the host call the `work()`
192    /// method in a non-realtime context with the given arguments.
193    ///
194    /// This function is always safe to call from `run()`, but it is not guaranteed that the worker
195    /// is actually called from a different thread. In particular, when free-wheeling (e.g. for
196    /// offline rendering), the worker may be executed immediately. This allows single-threaded
197    /// processing with sample accuracy and avoids timing problems when `run()` is executing much
198    /// faster or slower than real-time.
199    ///
200    /// Plugins SHOULD be written in such a way that if the worker runs immediately, and responses
201    /// from the worker are delivered immediately, the effect of the work takes place immediately
202    /// with sample accuracy.
203    ///
204    /// **Notes about the passed data:** The buffer used to pass data is managed by the host. That
205    /// mean the size is unknown and may be limited. So if you need to pass huge amount of data,
206    /// it's preferable to use another way, for example a sync::mpsc channel.
207    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/// Errors potentially generated by the
237/// [`ResponseHandler::respond`](struct.ResponseHandler.html#method.respond) method
238#[derive(PartialEq, Eq, Clone, Copy)]
239pub enum RespondError<T> {
240    /// Unknown or general error
241    Unknown(T),
242    /// Failure due to a lack of space
243    NoSpace(T),
244    /// No response callback was provided by the host
245    ///
246    /// This can only happen with faulty host
247    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
270/// Handler available inside the worker function to send a response to the `run()` context.
271///
272/// The `ResponseHandler` needs to know the `Worker` trait implementor as a generic parameter since the
273/// data, which is send to `work_response`, must be of the `ResponseData` associated type.
274pub struct ResponseHandler<P: Worker> {
275    /// function provided by the host to send response to `run()`
276    response_function: lv2_sys::LV2_Worker_Respond_Function,
277    /// Response handler provided by the host, must be passed to the host provided
278    /// response_function.
279    respond_handle: lv2_sys::LV2_Worker_Respond_Handle,
280    phantom: PhantomData<P>,
281}
282
283impl<P: Worker> ResponseHandler<P> {
284    /// Send a response to the `run` context.
285    ///
286    /// This method allows the worker to give a response to the `run` context. After calling this
287    /// method, the host will call `worker_response` with the given response data or a copy of it.
288    ///
289    /// If this method fails, the data is considered as untransmitted and is returned to the caller.
290    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/// Errors potentially generated by [`Worker`](trait.Worker.html) methods
323#[derive(PartialEq, Eq, Clone, Copy, Debug)]
324pub enum WorkerError {
325    /// Unknown or general error
326    Unknown,
327    /// Failure due to a lack of space
328    NoSpace,
329}
330
331/// The non-realtime working extension for plugins.
332///
333/// This trait and the [`Schedule`](struct.Schedule.html) struct enable plugin creators to use the
334/// [Worker specification](https://lv2plug.in/doc/html/group__worker.html) for non-realtime working
335/// tasks.
336///
337/// In order to be used by the host, you need to to export the [`WorkerDescriptor`](struct.WorkerDescriptor.html)
338/// in the `extension_data` method. You can do that with the `match_extensions` macro from the `lv2-core` crate.
339pub trait Worker: Plugin {
340    /// Type of data sent to `work` by the schedule handler.
341    type WorkData: 'static + Send;
342    /// Type of data sent to `work_response` by the response handler.
343    type ResponseData: 'static + Send;
344    /// The work to do in a non-real-time context,
345    ///
346    /// This is called by the host in a non-realtime context as requested, probably in a separate
347    /// thread from `run()` and possibly with an arbitrary message to handle.
348    ///
349    /// A response can be sent to `run()` context using the response handler. The plugin MUST NOT make any assumptions
350    /// about which thread calls this method, except that there are no real-time requirements and
351    /// only one call may be executed at a time. That is, the host MAY call this method from any
352    /// non-real-time thread, but MUST NOT make concurrent calls to this method from several
353    /// threads.
354    fn work(
355        response_handler: &ResponseHandler<Self>,
356        data: Self::WorkData,
357    ) -> Result<(), WorkerError>;
358
359    /// Handle a response from the worker.
360    ///
361    /// This is called by the host in the `run()` context when a response from the worker is ready.
362    fn work_response(
363        &mut self,
364        _data: Self::ResponseData,
365        _features: &mut Self::AudioFeatures,
366    ) -> Result<(), WorkerError> {
367        Ok(())
368    }
369
370    ///Called when all responses for this cycle have been delivered.
371    ///
372    ///Since work_response() may be called after `run()` finished, this method provides a hook for code that
373    ///must run after the cycle is completed.
374    fn end_run(&mut self, _features: &mut Self::AudioFeatures) -> Result<(), WorkerError> {
375        Ok(())
376    }
377}
378
379///Raw wrapper of the [`Worker`](trait.Worker.html) extension.
380///
381/// This is a marker type that has the required external methods for the extension.
382pub 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    /// Extern unsafe version of `work` method actually called by the host
392    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        //build response handler
400        let response_handler = ResponseHandler {
401            response_function,
402            respond_handle,
403            phantom: PhantomData::<P>,
404        };
405        //build ref to worker data from raw pointer
406        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    /// Extern unsafe version of `work_response` method actually called by the host
420    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        //deref plugin_instance and get the plugin
426        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        //build ref to response data from raw pointer
433        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    /// Extern unsafe version of `end_run` method actually called by the host
449    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
463// Implementing the trait that contains the interface.
464impl<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    // structure to test drooping issue
485    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    // URI identifier
521    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        //trash trick i use Plugin ptr insteas of Pluginstance ptr
651        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        //trash trick i use Plugin ptr insteas of Pluginstance ptr
671        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        //trash trick i use Plugin ptr insteas of Pluginstance ptr
692        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        //trash trick i use Plugin ptr insteas of Pluginstance ptr
706        unsafe {
707            WorkerDescriptor::<TestDropWorker>::extern_work_response(ptr_tdw, size, ptr_hd);
708        }
709    }
710}