dvcompute_cons/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="cons_mode")]
10use std::mem;
11
12#[cfg(feature="cons_mode")]
13use std::ptr;
14
15#[cfg(feature="cons_mode")]
16use libc::*;
17
18use crate::simulation;
19use crate::simulation::point::Point;
20use crate::simulation::event::Event;
21
22#[cfg(feature="cons_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 + '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 DisposableFnBox>
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<()> + '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 Disposable for DisposableBox {
136
137    #[doc(hidden)]
138    #[inline]
139    fn dispose(self, p: &Point) -> simulation::Result<()> {
140        self.call_box((p,))
141    }
142
143    #[inline]
144    fn into_boxed(self) -> DisposableBox
145        where Self: Sized + 'static
146    {
147        self
148    }
149}
150
151/// A trait to support the stable version of Rust, where there is no `FnBox`.
152trait DisposableFnBox {
153
154    /// Call the corresponding function.
155    fn call_box(self: Box<Self>, args: (&Point,)) -> simulation::Result<()>;
156}
157
158impl<F> DisposableFnBox for F
159    where F: for<'a> FnOnce(&'a Point) -> simulation::Result<()>
160{
161    fn call_box(self: Box<Self>, args: (&Point,)) -> simulation::Result<()> {
162        let this: Self = *self;
163        this(args.0)
164    }
165}
166
167/// It represents a raw trait object.
168#[cfg(feature="cons_mode")]
169#[repr(C)]
170#[derive(Copy, Clone)]
171struct DisposableTraitObject {
172
173    field1: *mut c_void,
174    field2: *mut c_void
175}
176
177/// A C-friendly representaton of the `Disposable` action.
178#[cfg(feature="cons_mode")]
179#[repr(C)]
180pub struct DisposableRepr {
181
182    /// Delete the object.
183    delete: unsafe extern "C" fn(obj: *mut DisposableTraitObject),
184
185    /// The callback.
186    callback: unsafe extern "C" fn(obj: *mut DisposableTraitObject, p: *const Point) -> *mut ErrorRepr,
187
188    /// The trait object.
189    trait_object: DisposableTraitObject
190}
191
192#[cfg(feature="cons_mode")]
193impl Drop for DisposableRepr {
194
195    fn drop(&mut self) {
196        unsafe {
197            (self.delete)(&mut self.trait_object);
198        }
199    }
200}
201
202#[cfg(feature="cons_mode")]
203impl DisposableRepr {
204
205    /// Convert to a C-friendly representation.
206    #[inline]
207    pub fn into_repr(comp: DisposableBox) -> DisposableRepr {
208        unsafe {
209            DisposableRepr {
210                delete: delete_disposable_repr,
211                callback: call_disposable_repr,
212                trait_object: mem::transmute(comp)
213            }
214        }
215    }
216
217    /// Call the representation.
218    #[inline]
219    fn call_repr(mut self, p: &Point) -> *mut ErrorRepr {
220        unsafe {
221            let x = (self.callback)(&mut self.trait_object, p);
222            mem::forget(self);
223            x
224        }
225    }
226}
227
228/// Call the `DisposableBox` representation.
229#[cfg(feature="cons_mode")]
230unsafe extern "C" fn call_disposable_repr(comp: *mut DisposableTraitObject, p: *const Point) -> *mut ErrorRepr {
231    let comp: DisposableBox = mem::transmute(*comp);
232    match comp.call_box((&*p,)) {
233        Result::Ok(()) => ptr::null_mut(),
234        Result::Err(e) => {
235            let e = ErrorRepr::new(e);
236            Box::into_raw(Box::new(e))
237        }
238    }
239}
240
241/// Delete the `DisposableBox` representation.
242#[cfg(feature="cons_mode")]
243unsafe extern "C" fn delete_disposable_repr(comp: *mut DisposableTraitObject) {
244    let _: DisposableBox = mem::transmute(*comp);
245}
246
247#[cfg(feature="cons_mode")]
248impl Disposable for DisposableRepr {
249
250    #[doc(hidden)]
251    #[inline]
252    fn dispose(self, p: &Point) -> simulation::Result<()> {
253        unsafe {
254            let e = self.call_repr(p);
255            if e == ptr::null_mut() {
256                Result::Ok(())
257            } else {
258                let e = ffi_error_repr_into_error(e);
259                Result::Err(e)
260            }
261        }
262    }
263}
264
265/// The `Disposable` constructed by the specified function.
266#[derive(Clone)]
267pub struct Cons<F> {
268
269    /// The function that disposes the subscription.
270    f: F
271}
272
273impl<F> Disposable for Cons<F>
274    where F: FnOnce(&Point) -> simulation::Result<()>
275{
276    #[doc(hidden)]
277    #[inline]
278    fn dispose(self, p: &Point) -> simulation::Result<()> {
279        let Cons { f } = self;
280        f(p)
281    }
282}
283
284/// An empty `Disposable` that disposes nothing.
285#[derive(Clone)]
286pub struct Empty {}
287
288impl Disposable for Empty {
289
290    #[doc(hidden)]
291    #[inline]
292    fn dispose(self, _p: &Point) -> simulation::Result<()> {
293        Result::Ok(())
294    }
295}
296
297/// A merge of two `Disposable` computations.
298#[derive(Clone)]
299pub struct Merge<M, U> {
300
301    /// The current computation.
302    disposable: M,
303
304    /// Another computation.
305    other: U
306}
307
308impl<M, U> Disposable for Merge<M, U>
309    where M: Disposable,
310          U: Disposable
311{
312    #[doc(hidden)]
313    #[inline]
314    fn dispose(self, p: &Point) -> simulation::Result<()> {
315        let Merge { disposable, other } = self;
316        disposable.dispose(p)?;
317        other.dispose(p)
318    }
319}
320
321/// An event that calls the `Disposable` computation.
322#[derive(Clone)]
323pub struct DisposableEvent<D> {
324
325    /// The disposable computation.
326    disposable: D
327}
328
329impl<D> Event for DisposableEvent<D>
330    where D: Disposable
331{
332    type Item = ();
333
334    #[doc(hidden)]
335    #[inline]
336    fn call_event(self, p: &Point) -> simulation::Result<Self::Item> {
337        let DisposableEvent { disposable } = self;
338        disposable.dispose(p)
339    }
340}
341
342/// A concatenation of the `Disposable` computations.
343#[derive(Clone)]
344pub struct Concat<I, M> {
345
346    /// The disposable computations.
347    disposables: I,
348
349    /// To keep the type parameter.
350    _phantom: PhantomData<M>
351}
352
353impl<I, M> Disposable for Concat<I, M>
354    where I: Iterator<Item = M>,
355          M: Disposable
356{
357    #[doc(hidden)]
358    #[inline]
359    fn dispose(self, p: &Point) -> simulation::Result<()> {
360        let Concat { disposables, _phantom } = self;
361        for disposable in disposables {
362            disposable.dispose(p)?;
363        }
364        Result::Ok(())
365    }
366}
367
368/// Trace the computation.
369#[must_use = "computations are lazy and do nothing unless to be run"]
370#[derive(Clone)]
371pub struct Trace<M> {
372
373    /// The computation.
374    comp: M,
375
376    /// The message to print.
377    msg: String
378}
379
380impl<M> Disposable for Trace<M>
381    where M: Disposable
382{
383    #[doc(hidden)]
384    #[inline]
385    fn dispose(self, p: &Point) -> simulation::Result<()> {
386        let Trace { comp, msg } = self;
387        p.trace(&msg);
388        comp.dispose(p)
389    }
390}