1 /+++++++++++++++++++++++++++++
2  + This module defines the concept of Disposable.
3  +/
4 module rx.disposable;
5 
6 import core.atomic;
7 import core.sync.mutex;
8 import rx.util;
9 
10 ///Tests if something is a Disposable.
11 template isDisposable(T)
12 {
13     enum bool isDisposable = is(typeof({
14                 T disposable = void;
15                 disposable.dispose();
16             }()));
17 }
18 ///
19 unittest
20 {
21     struct A
22     {
23         void dispose()
24         {
25         }
26     }
27 
28     class B
29     {
30         void dispose()
31         {
32         }
33     }
34 
35     interface C
36     {
37         void dispose();
38     }
39 
40     static assert(isDisposable!A);
41     static assert(isDisposable!B);
42     static assert(isDisposable!C);
43 }
44 
45 ///Tests if something is a Cancelable
46 template isCancelable(T)
47 {
48     enum isCancelable = isDisposable!T && is(typeof((inout int n = 0) {
49                 T disposable = void;
50                 bool b = disposable.isDisposed;
51             }));
52 }
53 ///
54 unittest
55 {
56     struct A
57     {
58         bool isDisposed() @property
59         {
60             return true;
61         }
62 
63         void dispose()
64         {
65         }
66     }
67 
68     class B
69     {
70         bool isDisposed() @property
71         {
72             return true;
73         }
74 
75         void dispose()
76         {
77         }
78     }
79 
80     interface C
81     {
82         bool isDisposed() @property;
83         void dispose();
84     }
85 
86     static assert(isCancelable!A);
87     static assert(isCancelable!B);
88     static assert(isCancelable!C);
89 }
90 
91 ///Wrapper for disposable objects.
92 interface Disposable
93 {
94     ///
95     void dispose();
96 }
97 ///Wrapper for cancelable objects.
98 interface Cancelable : Disposable
99 {
100     ///
101     bool isDisposed() @property;
102 }
103 
104 ///Simply implements for Cancelable interface. Its propagates notification that operations should be canceled.
105 class CancellationToken : Cancelable
106 {
107 public:
108     ///
109     bool isDisposed() @property
110     {
111         return atomicLoad(_disposed);
112     }
113     ///
114     alias isDisposed isCanceled;
115 
116 public:
117     ///
118     void dispose()
119     {
120         atomicStore(_disposed, true);
121     }
122     ///
123     alias dispose cancel;
124 
125 private:
126     shared(bool) _disposed;
127 }
128 
129 unittest
130 {
131     auto c = new CancellationToken;
132     assert(!c.isDisposed);
133     assert(!c.isCanceled);
134     c.dispose();
135     assert(c.isDisposed);
136     assert(c.isCanceled);
137 }
138 
139 unittest
140 {
141     auto c = new CancellationToken;
142     assert(!c.isDisposed);
143     assert(!c.isCanceled);
144     c.cancel();
145     assert(c.isDisposed);
146     assert(c.isCanceled);
147 }
148 
149 ///Class that implements the Disposable interface and wraps the dispose methods in virtual functions.
150 class DisposableObject(T) : Disposable
151 {
152 public:
153     ///
154     this(T disposable)
155     {
156         _disposable = disposable;
157     }
158 
159 public:
160     ///
161     void dispose()
162     {
163         _disposable.dispose();
164     }
165 
166 private:
167     T _disposable;
168 }
169 ///Class that implements the Cancelable interface and wraps the  isDisposed property in virtual functions.
170 class CancelableObject(T) : DisposableObject!T, Cancelable
171 {
172 public:
173     ///
174     this(T disposable)
175     {
176         super(disposable);
177     }
178 
179 public:
180     ///
181     bool isDisposed() @property
182     {
183         return _disposable.isDisposed;
184     }
185 }
186 
187 ///Wraps dispose method in virtual functions.
188 auto disposableObject(T)(T disposable)
189 {
190     static assert(isDisposable!T);
191 
192     static if (is(T : Cancelable) || is(T : Disposable))
193     {
194         return disposable;
195     }
196     else static if (isCancelable!T)
197     {
198         return new CancelableObject!T(disposable);
199     }
200     else
201     {
202         return new DisposableObject!T(disposable);
203     }
204 }
205 
206 ///
207 unittest
208 {
209     int count = 0;
210     struct TestDisposable
211     {
212         void dispose()
213         {
214             count++;
215         }
216     }
217 
218     TestDisposable test;
219     Disposable disposable = disposableObject(test);
220     assert(count == 0);
221     disposable.dispose();
222     assert(count == 1);
223 }
224 
225 unittest
226 {
227     int count = 0;
228     class TestDisposable : Disposable
229     {
230         void dispose()
231         {
232             count++;
233         }
234     }
235 
236     auto test = new TestDisposable;
237     Disposable disposable = disposableObject(test);
238     assert(disposable is test);
239     assert(count == 0);
240     disposable.dispose();
241     assert(count == 1);
242 }
243 
244 unittest
245 {
246     int count = 0;
247     struct TestCancelable
248     {
249         bool isDisposed() @property
250         {
251             return _disposed;
252         }
253 
254         void dispose()
255         {
256             count++;
257             _disposed = true;
258         }
259 
260         bool _disposed;
261     }
262 
263     TestCancelable test;
264     Cancelable cancelable = disposableObject(test);
265 
266     assert(!cancelable.isDisposed);
267     assert(count == 0);
268     cancelable.dispose();
269     assert(cancelable.isDisposed);
270     assert(count == 1);
271 }
272 
273 ///Defines a instance property that return NOP Disposable.
274 final class NopDisposable : Disposable
275 {
276 private:
277     this()
278     {
279     }
280 
281 public:
282     void dispose()
283     {
284     }
285 
286 public:
287     ///
288     static Disposable instance() @property
289     {
290         import std.concurrency : initOnce;
291 
292         static __gshared NopDisposable inst;
293         return initOnce!inst(new NopDisposable);
294     }
295 }
296 ///
297 unittest
298 {
299     Disposable d1 = NopDisposable.instance;
300     Disposable d2 = NopDisposable.instance;
301     assert(d1 !is null);
302     assert(d1 is d2);
303 }
304 
305 package final class DisposedMarker : Cancelable
306 {
307 private:
308     this()
309     {
310     }
311 
312 public:
313     bool isDisposed() @property
314     {
315         return true;
316     }
317 
318 public:
319     void dispose()
320     {
321     }
322 
323 public:
324     static Cancelable instance()
325     {
326         import std.concurrency : initOnce;
327 
328         static __gshared DisposedMarker inst;
329         return initOnce!inst(new DisposedMarker);
330     }
331 }
332 
333 ///
334 final class SingleAssignmentDisposable : Cancelable
335 {
336 public:
337     ///
338     void setDisposable(Disposable disposable)
339     {
340         import core.atomic;
341 
342         if (!cas(&_disposable, shared(Disposable).init, cast(shared) disposable))
343             assert(false);
344     }
345 
346 public:
347     ///
348     bool isDisposed() @property
349     {
350         return _disposable is cast(shared) DisposedMarker.instance;
351     }
352 
353 public:
354     ///
355     void dispose()
356     {
357         import rx.util;
358 
359         auto temp = exchange(_disposable, cast(shared) DisposedMarker.instance);
360         if (temp !is null)
361             temp.dispose();
362     }
363 
364 private:
365     shared(Disposable) _disposable;
366 }
367 ///
368 unittest
369 {
370     int count = 0;
371     class TestDisposable : Disposable
372     {
373         void dispose()
374         {
375             count++;
376         }
377     }
378 
379     auto temp = new SingleAssignmentDisposable;
380     temp.setDisposable(new TestDisposable);
381     assert(!temp.isDisposed);
382     assert(count == 0);
383     temp.dispose();
384     assert(temp.isDisposed);
385     assert(count == 1);
386 }
387 
388 unittest
389 {
390     static assert(isDisposable!SingleAssignmentDisposable);
391 }
392 
393 unittest
394 {
395     import core.exception;
396 
397     class TestDisposable : Disposable
398     {
399         void dispose()
400         {
401         }
402     }
403 
404     auto temp = new SingleAssignmentDisposable;
405     temp.setDisposable(new TestDisposable);
406     try
407     {
408         temp.setDisposable(new TestDisposable);
409     }
410     catch (AssertError)
411     {
412         return;
413     }
414     assert(false);
415 }
416 
417 ///
418 class SerialDisposable : Cancelable
419 {
420 public:
421     this()
422     {
423         _gate = new Mutex;
424     }
425 
426 public:
427     ///
428     bool isDisposed() @property
429     {
430         return _disposed;
431     }
432 
433     ///
434     void disposable(Disposable value) @property
435     {
436         auto shouldDispose = false;
437         Disposable old = null;
438         synchronized (_gate)
439         {
440             shouldDispose = _disposed;
441             if (!shouldDispose)
442             {
443                 old = _disposable;
444                 _disposable = value;
445             }
446         }
447         if (old !is null)
448             old.dispose();
449         if (shouldDispose && value !is null)
450             value.dispose();
451     }
452 
453     ///
454     Disposable disposable() @property
455     {
456         return _disposable;
457     }
458 
459 public:
460     ///
461     void dispose()
462     {
463         Disposable old = null;
464         synchronized (_gate)
465         {
466             if (!_disposed)
467             {
468                 _disposed = true;
469                 old = _disposable;
470                 _disposable = null;
471             }
472         }
473         if (old !is null)
474             old.dispose();
475     }
476 
477 private:
478     Mutex _gate;
479     bool _disposed;
480     Disposable _disposable;
481 }
482 
483 unittest
484 {
485     int count = 0;
486     struct A
487     {
488         void dispose()
489         {
490             count++;
491         }
492     }
493 
494     auto d = new SerialDisposable;
495     d.disposable = disposableObject(A());
496     assert(count == 0);
497     d.disposable = disposableObject(A());
498     assert(count == 1);
499     d.dispose();
500     assert(count == 2);
501     d.disposable = disposableObject(A());
502     assert(count == 3);
503 }
504 
505 unittest
506 {
507     int count = 0;
508     struct A
509     {
510         void dispose()
511         {
512             count++;
513         }
514     }
515 
516     auto d = new SerialDisposable;
517     d.dispose();
518     assert(count == 0);
519     d.disposable = disposableObject(A());
520     assert(count == 1);
521 }
522 
523 ///
524 class SignalDisposable : Disposable
525 {
526 public:
527     this()
528     {
529         _signal = new EventSignal;
530     }
531 
532 public:
533     ///
534     EventSignal signal() @property
535     {
536         return _signal;
537     }
538 
539 public:
540     ///
541     void dispose()
542     {
543         _signal.setSignal();
544     }
545 
546 private:
547     EventSignal _signal;
548 }
549 
550 unittest
551 {
552     auto d = new SignalDisposable;
553     auto signal = d.signal;
554     assert(!signal.signal);
555     d.dispose();
556     assert(signal.signal);
557 }
558 
559 ///
560 class CompositeDisposable : Disposable
561 {
562 public:
563     ///
564     this(Disposable[] disposables...)
565     {
566         _disposables = disposables.dup;
567     }
568 
569 public:
570     ///
571     void dispose()
572     {
573         foreach (ref d; _disposables)
574             d.dispose();
575     }
576 
577 private:
578     Disposable[] _disposables;
579 }
580 ///
581 unittest
582 {
583     auto d1 = new SingleAssignmentDisposable;
584     auto d2 = new SerialDisposable;
585     auto d = new CompositeDisposable(d1, d2);
586     d.dispose();
587 }
588 
589 ///
590 class AnonymouseDisposable : Disposable
591 {
592 public:
593     ///
594     this(void delegate() dispose)
595     {
596         assert(dispose !is null);
597         _dispose = dispose;
598     }
599 
600 public:
601     ///
602     void dispose()
603     {
604         if (_dispose !is null)
605         {
606             _dispose();
607             _dispose = null;
608         }
609     }
610 
611 private:
612     void delegate() _dispose;
613 }
614 ///
615 unittest
616 {
617     int count = 0;
618     auto d = new AnonymouseDisposable({ count++; });
619     assert(count == 0);
620     d.dispose();
621     assert(count == 1);
622     d.dispose();
623     assert(count == 1);
624 }