dvcompute_dist/simulation/observable/
disposable.rs

1// Copyright (c) 2020-2022  David Sorokin <davsor@mail.ru>, based in Yoshkar-Ola, Russia
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at https://mozilla.org/MPL/2.0/.
6
7use std::marker::PhantomData;
8
9#[cfg(feature="dist_mode")]
10use std::mem;
11
12#[cfg(feature="dist_mode")]
13use std::ptr;
14
15#[cfg(feature="dist_mode")]
16use libc::*;
17
18use crate::simulation;
19use crate::simulation::point::Point;
20use crate::simulation::event::Event;
21
22#[cfg(feature="dist_mode")]
23use crate::simulation::error::*;
24
25/// Create a `Disposable` by the specified function.
26#[inline]
27pub fn cons_disposable<F>(f: F) -> Cons<F>
28    where F: FnOnce(&Point) -> simulation::Result<()>
29{
30    Cons { f: f }
31}
32
33/// Create an empty `Disposable`.
34#[inline]
35pub fn empty_disposable() -> Empty {
36    Empty {}
37}
38
39/// Concatenate `Disposable` objects.
40#[inline]
41pub fn concat_disposables<I, M>(disposables: I) -> Concat<I::IntoIter, M>
42    where I: IntoIterator<Item = M>,
43          M: Disposable
44{
45    Concat { disposables: disposables.into_iter(), _phantom: PhantomData }
46}
47
48/// Trace the computation.
49#[inline]
50pub fn trace_disposable<M>(msg: String, comp: M) -> Trace<M>
51    where M: Disposable
52{
53    Trace { comp: comp, msg: msg}
54}
55
56/// The computation that disposes the observable subscription.
57pub trait Disposable {
58
59    /// Dispose something at the specified modeling time.
60    #[doc(hidden)]
61    fn dispose(self, p: &Point) -> simulation::Result<()>;
62
63    /// Convert to the `Event` computation.
64    #[inline]
65    fn into_event(self) -> DisposableEvent<Self>
66        where Self: Sized
67    {
68        DisposableEvent { disposable: self }
69    }
70
71    /// Merge with another `Disposable` computation.
72    #[inline]
73    fn merge<U>(self, other: U) -> Merge<Self, U>
74        where Self: Sized,
75              U: Disposable
76    {
77        Merge { disposable: self, other: other }
78    }
79
80    /// Convert into a boxed representation.
81    #[inline]
82    fn into_boxed(self) -> DisposableBox
83        where Self: Sized + Clone + 'static
84    {
85        DisposableBox::new(move |p: &Point| { self.dispose(p) })
86    }
87}
88
89/// Allows converting to `Disposable` computations.
90pub trait IntoDisposable {
91
92    type Disposable: Disposable;
93
94    /// Convert to the `Disposable` computation.
95    fn into_disposable(self) -> Self::Disposable;
96}
97
98impl<M: Disposable> IntoDisposable for M {
99
100    type Disposable = M;
101
102    #[inline]
103    fn into_disposable(self) -> Self::Disposable {
104        self
105    }
106}
107
108/// The `Disposable` box value.
109pub struct DisposableBox {
110    f: Box<dyn DisposableFnBoxClone>
111}
112
113impl DisposableBox {
114
115    /// Create a new boxed computation.
116    #[doc(hidden)]
117    #[inline]
118    fn new<F>(f: F) -> Self
119        where F: FnOnce(&Point) -> simulation::Result<()> + Clone + 'static
120    {
121        DisposableBox {
122            f: Box::new(f)
123        }
124    }
125
126    /// Call the boxed function.
127    #[doc(hidden)]
128    #[inline]
129    pub fn call_box(self, arg: (&Point,)) -> simulation::Result<()> {
130        let DisposableBox { f } = self;
131        f.call_box(arg)
132    }
133}
134
135impl Clone for DisposableBox {
136
137    fn clone(&self) -> Self {
138        DisposableBox {
139            f: self.f.call_clone()
140        }
141    }
142}
143
144impl Disposable for DisposableBox {
145
146    #[doc(hidden)]
147    #[inline]
148    fn dispose(self, p: &Point) -> simulation::Result<()> {
149        self.call_box((p,))
150    }
151
152    #[inline]
153    fn into_boxed(self) -> DisposableBox
154        where Self: Sized + 'static
155    {
156        self
157    }
158}
159
160/// A trait to support the stable version of Rust, where there is no `FnBox`.
161trait DisposableFnBox {
162
163    /// Call the corresponding function.
164    fn call_box(self: Box<Self>, args: (&Point,)) -> simulation::Result<()>;
165}
166
167impl<F> DisposableFnBox for F
168    where F: for<'a> FnOnce(&'a Point) -> simulation::Result<()>
169{
170    fn call_box(self: Box<Self>, args: (&Point,)) -> simulation::Result<()> {
171        let this: Self = *self;
172        this(args.0)
173    }
174}
175
176/// A trait to implement a cloneable `FnBox`.
177trait DisposableFnBoxClone: DisposableFnBox {
178
179    /// Clone the function.
180    fn call_clone(&self) -> Box<dyn DisposableFnBoxClone>;
181}
182
183impl<F> DisposableFnBoxClone for F
184    where F: for<'a> FnOnce(&'a Point) -> simulation::Result<()> + Clone + 'static
185{
186    fn call_clone(&self) -> Box<dyn DisposableFnBoxClone> {
187        Box::new(self.clone())
188    }
189}
190
191/// It represents a raw trait object.
192#[cfg(feature="dist_mode")]
193#[repr(C)]
194#[derive(Copy, Clone)]
195struct DisposableTraitObject {
196
197    field1: *mut c_void,
198    field2: *mut c_void
199}
200
201/// A C-friendly representaton of the `Disposable` action.
202#[cfg(feature="dist_mode")]
203#[repr(C)]
204pub struct DisposableRepr {
205
206    /// Delete the object.
207    delete: unsafe extern "C" fn(obj: *mut DisposableTraitObject),
208
209    /// Clone the action.
210    clone: unsafe extern "C" fn(obj: *const DisposableTraitObject) -> DisposableRepr,
211
212    /// The callback.
213    callback: unsafe extern "C" fn(obj: *mut DisposableTraitObject, p: *const Point) -> *mut ErrorRepr,
214
215    /// The trait object.
216    trait_object: DisposableTraitObject
217}
218
219#[cfg(feature="dist_mode")]
220impl Drop for DisposableRepr {
221
222    fn drop(&mut self) {
223        unsafe {
224            (self.delete)(&mut self.trait_object);
225        }
226    }
227}
228
229#[cfg(feature="dist_mode")]
230impl Clone for DisposableRepr {
231
232    fn clone(&self) -> Self {
233        unsafe {
234            (self.clone)(&self.trait_object)
235        }
236    }
237}
238
239#[cfg(feature="dist_mode")]
240impl DisposableRepr {
241
242    /// Convert to a C-friendly representation.
243    #[inline]
244    pub fn into_repr(comp: DisposableBox) -> DisposableRepr {
245        unsafe {
246            DisposableRepr {
247                delete: delete_disposable_repr,
248                clone: clone_disposable_repr,
249                callback: call_disposable_repr,
250                trait_object: mem::transmute(comp)
251            }
252        }
253    }
254
255    /// Call the representation.
256    #[inline]
257    fn call_repr(mut self, p: &Point) -> *mut ErrorRepr {
258        unsafe {
259            let x = (self.callback)(&mut self.trait_object, p);
260            mem::forget(self);
261            x
262        }
263    }
264}
265
266/// Call the `DisposableBox` representation.
267#[cfg(feature="dist_mode")]
268unsafe extern "C" fn call_disposable_repr(comp: *mut DisposableTraitObject, p: *const Point) -> *mut ErrorRepr {
269    let comp: DisposableBox = mem::transmute(*comp);
270    match comp.call_box((&*p,)) {
271        Result::Ok(()) => ptr::null_mut(),
272        Result::Err(e) => {
273            let e = ErrorRepr::new(e);
274            Box::into_raw(Box::new(e))
275        }
276    }
277}
278
279/// Clone the `DisposableBox` representation.
280#[cfg(feature="dist_mode")]
281unsafe extern "C" fn clone_disposable_repr(comp: *const DisposableTraitObject) -> DisposableRepr {
282    let comp: DisposableBox = mem::transmute(*comp);
283    let x = DisposableRepr {
284        delete: delete_disposable_repr,
285        clone: clone_disposable_repr,
286        callback: call_disposable_repr,
287        trait_object: mem::transmute(comp.clone())
288    };
289    mem::forget(comp);
290    x
291}
292
293/// Delete the `DisposableBox` representation.
294#[cfg(feature="dist_mode")]
295unsafe extern "C" fn delete_disposable_repr(comp: *mut DisposableTraitObject) {
296    let _: DisposableBox = mem::transmute(*comp);
297}
298
299#[cfg(feature="dist_mode")]
300impl Disposable for DisposableRepr {
301
302    #[doc(hidden)]
303    #[inline]
304    fn dispose(self, p: &Point) -> simulation::Result<()> {
305        unsafe {
306            let e = self.call_repr(p);
307            if e == ptr::null_mut() {
308                Result::Ok(())
309            } else {
310                let e = ffi_error_repr_into_error(e);
311                Result::Err(e)
312            }
313        }
314    }
315}
316
317/// The `Disposable` constructed by the specified function.
318#[derive(Clone)]
319pub struct Cons<F> {
320
321    /// The function that disposes the subscription.
322    f: F
323}
324
325impl<F> Disposable for Cons<F>
326    where F: FnOnce(&Point) -> simulation::Result<()>
327{
328    #[doc(hidden)]
329    #[inline]
330    fn dispose(self, p: &Point) -> simulation::Result<()> {
331        let Cons { f } = self;
332        f(p)
333    }
334}
335
336/// An empty `Disposable` that disposes nothing.
337#[derive(Clone)]
338pub struct Empty {}
339
340impl Disposable for Empty {
341
342    #[doc(hidden)]
343    #[inline]
344    fn dispose(self, _p: &Point) -> simulation::Result<()> {
345        Result::Ok(())
346    }
347}
348
349/// A merge of two `Disposable` computations.
350#[derive(Clone)]
351pub struct Merge<M, U> {
352
353    /// The current computation.
354    disposable: M,
355
356    /// Another computation.
357    other: U
358}
359
360impl<M, U> Disposable for Merge<M, U>
361    where M: Disposable,
362          U: Disposable
363{
364    #[doc(hidden)]
365    #[inline]
366    fn dispose(self, p: &Point) -> simulation::Result<()> {
367        let Merge { disposable, other } = self;
368        disposable.dispose(p)?;
369        other.dispose(p)
370    }
371}
372
373/// An event that calls the `Disposable` computation.
374#[derive(Clone)]
375pub struct DisposableEvent<D> {
376
377    /// The disposable computation.
378    disposable: D
379}
380
381impl<D> Event for DisposableEvent<D>
382    where D: Disposable
383{
384    type Item = ();
385
386    #[doc(hidden)]
387    #[inline]
388    fn call_event(self, p: &Point) -> simulation::Result<Self::Item> {
389        let DisposableEvent { disposable } = self;
390        disposable.dispose(p)
391    }
392}
393
394/// A concatenation of the `Disposable` computations.
395#[derive(Clone)]
396pub struct Concat<I, M> {
397
398    /// The disposable computations.
399    disposables: I,
400
401    /// To keep the type parameter.
402    _phantom: PhantomData<M>
403}
404
405impl<I, M> Disposable for Concat<I, M>
406    where I: Iterator<Item = M>,
407          M: Disposable
408{
409    #[doc(hidden)]
410    #[inline]
411    fn dispose(self, p: &Point) -> simulation::Result<()> {
412        let Concat { disposables, _phantom } = self;
413        for disposable in disposables {
414            disposable.dispose(p)?;
415        }
416        Result::Ok(())
417    }
418}
419
420/// Trace the computation.
421#[must_use = "computations are lazy and do nothing unless to be run"]
422#[derive(Clone)]
423pub struct Trace<M> {
424
425    /// The computation.
426    comp: M,
427
428    /// The message to print.
429    msg: String
430}
431
432impl<M> Disposable for Trace<M>
433    where M: Disposable
434{
435    #[doc(hidden)]
436    #[inline]
437    fn dispose(self, p: &Point) -> simulation::Result<()> {
438        let Trace { comp, msg } = self;
439        p.trace(&msg);
440        comp.dispose(p)
441    }
442}