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 = assumeThreadLocal(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     private enum ShrinkThreshold = 64;
563 
564 public:
565     ///
566     this(Disposable[] disposables...)
567     {
568         _gate = new Object;
569         _disposables = disposables.dup;
570     }
571 
572 public:
573     ///
574     size_t count() const nothrow @nogc @property
575     {
576         return atomicLoad(_count);
577     }
578 
579     ///
580     void dispose()
581     {
582         Disposable[] currentDisposables;
583         synchronized (_gate)
584         {
585             if (!_disposed)
586             {
587                 _disposed = true;
588                 currentDisposables = _disposables;
589                 _disposables = [];
590                 _count = 0;
591             }
592         }
593 
594         if (currentDisposables)
595         {
596             foreach (d; currentDisposables)
597             {
598                 if (d)
599                     d.dispose();
600             }
601         }
602     }
603 
604     ///
605     void clear()
606     {
607         Disposable[] currentDisposables;
608         synchronized (_gate)
609         {
610             currentDisposables = _disposables;
611             _disposables = [];
612             _count = 0;
613         }
614 
615         foreach (d; currentDisposables)
616         {
617             if (d)
618                 d.dispose();
619         }
620     }
621 
622     ///
623     void insert(Disposable item)
624     {
625         assert(item !is null);
626 
627         bool shouldDispose = void;
628         synchronized (_gate)
629         {
630             shouldDispose = _disposed;
631             if (!_disposed)
632             {
633                 _disposables ~= item;
634             }
635             atomicOp!"+="(_count, 1);
636         }
637 
638         if (shouldDispose)
639         {
640             item.dispose();
641         }
642     }
643 
644     ///
645     bool remove(Disposable item)
646     {
647         assert(item !is null);
648 
649         synchronized (_gate)
650         {
651             auto current = _disposables;
652 
653             import std.algorithm : countUntil;
654 
655             auto i = countUntil(current, item);
656             if (i < 0)
657             {
658                 // not found, just return
659                 return false;
660             }
661 
662             current[i] = null;
663             const cap = current.capacity;
664             if (cap > ShrinkThreshold && _count < cap / 2)
665             {
666                 Disposable[] fresh;
667                 fresh.reserve(cap / 2);
668 
669                 foreach (d; current)
670                 {
671                     if (d !is null)
672                     {
673                         fresh ~= d;
674                     }
675                 }
676 
677                 _disposables = fresh;
678             }
679             atomicOp!"-="(_count, 1);
680             return true;
681         }
682     }
683 
684 private:
685     Object _gate;
686     Disposable[] _disposables;
687     shared(bool) _disposed;
688     shared(size_t) _count;
689 }
690 
691 ///
692 unittest
693 {
694     auto d1 = new SingleAssignmentDisposable;
695     auto d2 = new SerialDisposable;
696     auto d = new CompositeDisposable(d1, d2);
697     d.dispose();
698     assert(d.count == 0);
699 }
700 
701 ///
702 unittest
703 {
704     auto composite = new CompositeDisposable;
705     auto disposed = false;
706     auto inner = new AnonymousDisposable({ disposed = true; });
707     composite.insert(inner);
708     composite.dispose();
709     assert(disposed);
710 }
711 
712 ///
713 unittest
714 {
715     auto composite = new CompositeDisposable;
716     size_t _count = 0;
717     auto inner = new AnonymousDisposable({ _count++; });
718     composite.insert(inner);
719     composite.clear(); // clear items and dispose all
720     assert(_count == 1);
721 
722     composite.clear();
723     assert(_count == 1);
724 }
725 
726 ///
727 unittest
728 {
729     auto composite = new CompositeDisposable;
730     composite.dispose();
731 
732     auto disposed = false;
733     auto inner2 = new AnonymousDisposable({ disposed = true; });
734     composite.insert(inner2);
735     assert(disposed);
736 }
737 
738 ///
739 unittest
740 {
741     auto composite = new CompositeDisposable;
742     bool disposed = false;
743     auto disposable = new AnonymousDisposable({ disposed = true; });
744     composite.insert(disposable);
745     composite.remove(disposable);
746     composite.dispose();
747     assert(!disposed);
748 }
749 
750 ///
751 unittest
752 {
753     auto composite = new CompositeDisposable;
754     Disposable[] ds;
755     size_t disposedCount = 0;
756     foreach (_; 0 .. 100)
757     {
758         auto temp = new AnonymousDisposable({ disposedCount++; });
759         composite.insert(temp);
760         ds ~= temp;
761     }
762     foreach (i; 0 .. 80)
763     {
764         composite.remove(ds[i]);
765     }
766     assert(composite.count == 20);
767     composite.dispose();
768     assert(composite.count == 0);
769     assert(disposedCount == 20);
770 }
771 
772 ///
773 unittest
774 {
775     auto composite = new CompositeDisposable;
776     auto disposed = false;
777     auto item = new AnonymousDisposable({ disposed = true; });
778     composite.insert(item);
779     composite.remove(item);
780     composite.clear();
781     assert(!disposed);
782 }
783 
784 ///
785 class AnonymousDisposable : Disposable
786 {
787 public:
788     ///
789     this(void delegate() dispose)
790     {
791         assert(dispose !is null);
792         _dispose = dispose;
793     }
794 
795 public:
796     ///
797     void dispose()
798     {
799         if (_dispose !is null)
800         {
801             _dispose();
802             _dispose = null;
803         }
804     }
805 
806 private:
807     void delegate() _dispose;
808 }
809 ///
810 unittest
811 {
812     int count = 0;
813     auto d = new AnonymousDisposable({ count++; });
814     assert(count == 0);
815     d.dispose();
816     assert(count == 1);
817     d.dispose();
818     assert(count == 1);
819 }
820 
821 ///
822 class RefCountDisposable : Disposable
823 {
824 public:
825     ///
826     this(Disposable disposable, bool throwWhenDisposed = false)
827     {
828         assert(disposable !is null);
829 
830         _throwWhenDisposed = throwWhenDisposed;
831         _gate = new Object();
832         _disposable = disposable;
833         _isPrimaryDisposed = false;
834         _count = 0;
835     }
836 
837 public:
838     ///
839     Disposable getDisposable()
840     {
841         synchronized (_gate)
842         {
843             if (_disposable is null)
844             {
845                 if (_throwWhenDisposed)
846                 {
847                     throw new Exception("RefCountDisposable is already disposed.");
848                 }
849                 return NopDisposable.instance;
850             }
851             else
852             {
853                 _count++;
854                 return new AnonymousDisposable(&this.release);
855             }
856         }
857     }
858 
859     ///
860     void dispose()
861     {
862         Disposable disposable = null;
863         synchronized (_gate)
864         {
865             if (_disposable is null)
866                 return;
867 
868             if (!_isPrimaryDisposed)
869             {
870                 _isPrimaryDisposed = true;
871 
872                 if (_count == 0)
873                 {
874                     disposable = _disposable;
875                     _disposable = null;
876                 }
877             }
878         }
879         if (disposable !is null)
880         {
881             disposable.dispose();
882         }
883     }
884 
885 private:
886     void release()
887     {
888         Disposable disposable = null;
889         synchronized (_gate)
890         {
891             if (_disposable is null)
892                 return;
893 
894             assert(_count > 0);
895             _count--;
896 
897             if (_isPrimaryDisposed)
898             {
899                 if (_count == 0)
900                 {
901                     disposable = _disposable;
902                     _disposable = null;
903                 }
904             }
905         }
906         if (disposable !is null)
907         {
908             disposable.dispose();
909         }
910     }
911 
912 private:
913     size_t _count;
914     Disposable _disposable;
915     bool _isPrimaryDisposed;
916     Object _gate;
917     bool _throwWhenDisposed;
918 }
919 
920 ///
921 unittest
922 {
923     bool disposed = false;
924     auto disposable = new RefCountDisposable(new AnonymousDisposable({
925             disposed = true;
926         }));
927 
928     auto subscription = disposable.getDisposable();
929 
930     assert(!disposed);
931     disposable.dispose();
932     assert(!disposed);
933 
934     subscription.dispose();
935     assert(disposed);
936 }
937 
938 unittest
939 {
940     bool disposed = false;
941     auto disposable = new RefCountDisposable(new AnonymousDisposable({
942             disposed = true;
943         }));
944 
945     assert(!disposed);
946     disposable.dispose();
947     assert(disposed);
948 }
949 
950 unittest
951 {
952     bool disposed = false;
953     auto disposable = new RefCountDisposable(new AnonymousDisposable({
954             disposed = true;
955         }));
956 
957     auto subscription = disposable.getDisposable();
958     assert(!disposed);
959     subscription.dispose();
960     assert(!disposed);
961     disposable.dispose();
962     assert(disposed);
963 }
964 
965 unittest
966 {
967     bool disposed = false;
968     auto disposable = new RefCountDisposable(new AnonymousDisposable({
969             disposed = true;
970         }));
971 
972     auto subscription1 = disposable.getDisposable();
973     auto subscription2 = disposable.getDisposable();
974     assert(!disposed);
975     subscription1.dispose();
976     assert(!disposed);
977     subscription2.dispose();
978     assert(!disposed);
979     disposable.dispose();
980     assert(disposed);
981 }
982 
983 unittest
984 {
985     bool disposed = false;
986     auto disposable = new RefCountDisposable(new AnonymousDisposable({
987             disposed = true;
988         }));
989 
990     auto subscription1 = disposable.getDisposable();
991     auto subscription2 = disposable.getDisposable();
992 
993     disposable.dispose();
994     assert(!disposed);
995 
996     subscription1.dispose();
997     assert(!disposed);
998     subscription1.dispose();
999     assert(!disposed);
1000 
1001     subscription2.dispose();
1002     assert(disposed);
1003 }
1004 
1005 ///
1006 template withDisposed(alias f)
1007 {
1008     auto withDisposed(TDisposable)(auto ref TDisposable disposable)
1009             if (isDisposable!TDisposable)
1010     {
1011         return new CompositeDisposable(disposable, new AnonymousDisposable({
1012                 f();
1013             }));
1014     }
1015 }
1016 
1017 ///ditto
1018 auto withDisposed(TDisposable)(auto ref TDisposable disposable, void delegate() disposed)
1019         if (isDisposable!TDisposable)
1020 {
1021     return new CompositeDisposable(disposable, new AnonymousDisposable(disposed));
1022 }
1023 
1024 ///
1025 unittest
1026 {
1027     import rx;
1028 
1029     auto sub = new SubjectObject!int;
1030     size_t putCount = 0;
1031     size_t disposedCount = 0;
1032 
1033     auto disposable = sub.doSubscribe!(_ => putCount++)
1034         .withDisposed!(() => disposedCount++);
1035 
1036     sub.put(1);
1037     disposable.dispose();
1038 
1039     assert(putCount == 1);
1040     assert(disposedCount == 1);
1041 }
1042 
1043 ///
1044 unittest
1045 {
1046     import rx;
1047 
1048     auto sub = new SubjectObject!int;
1049     size_t putCount = 0;
1050 
1051     bool disposed = false;
1052     alias traceDispose = withDisposed!(() => disposed = true);
1053 
1054     auto disposable = traceDispose(sub.doSubscribe!(_ => putCount++));
1055 
1056     sub.put(1);
1057     sub.completed();
1058 
1059     assert(putCount == 1);
1060     assert(!disposed);
1061 }
1062 
1063 ///
1064 unittest
1065 {
1066     import rx;
1067 
1068     auto sub = new SubjectObject!int;
1069     size_t putCount = 0;
1070 
1071     bool disposed = false;
1072     alias traceDispose = withDisposed!(() => disposed = true);
1073 
1074     auto disposable = traceDispose(sub.doSubscribe!(_ => putCount++));
1075 
1076     sub.put(1);
1077     disposable.dispose();
1078 
1079     assert(putCount == 1);
1080     assert(disposed);
1081 }
1082 
1083 ///
1084 unittest
1085 {
1086     import rx;
1087 
1088     auto sub = new SubjectObject!int;
1089     size_t putCount = 0;
1090 
1091     bool disposed = false;
1092     auto disposable = sub.doSubscribe!(_ => putCount++).withDisposed(() {
1093         disposed = true;
1094     });
1095 
1096     sub.put(1);
1097     disposable.dispose();
1098 
1099     assert(putCount == 1);
1100     assert(disposed);
1101 }
1102 
1103 ///
1104 unittest
1105 {
1106     import rx;
1107 
1108     auto sub = new SubjectObject!int;
1109     size_t disposedCount = 0;
1110 
1111     auto disposable = sub.doSubscribe!((int) {})
1112         .withDisposed!(() { disposedCount++; });
1113 
1114     disposable.dispose();
1115     disposable.dispose();
1116 
1117     assert(disposedCount == 1);
1118 }
1119 
1120 ///
1121 unittest
1122 {
1123     import rx;
1124 
1125     auto sub = new SubjectObject!int;
1126     size_t putCount = 0;
1127 
1128     bool disposed = false;
1129     auto disposable = sub.doSubscribe!(_ => putCount++).withDisposed(() {
1130         disposed = true;
1131     });
1132 
1133     sub.put(1);
1134     disposable.dispose();
1135 
1136     assert(putCount == 1);
1137     assert(disposed);
1138 }
1139 
1140 ///
1141 unittest
1142 {
1143     import rx;
1144 
1145     auto sub = new SubjectObject!int;
1146     size_t disposedCount = 0;
1147 
1148     auto disposable = sub.doSubscribe!((int) {}).withDisposed(() {
1149         disposedCount++;
1150     });
1151 
1152     disposable.dispose();
1153     disposable.dispose();
1154 
1155     assert(disposedCount == 1);
1156 }
1157 
1158 ///
1159 void atomicDispose(T)(ref shared(T) disposable)
1160         if (isDisposable!T && (is(T == class) || is(T == interface)))
1161 {
1162     auto temp = exchange(disposable, null).assumeThreadLocal();
1163     if (temp !is null)
1164         temp.dispose();
1165 }
1166 ///
1167 unittest
1168 {
1169     size_t count;
1170     class MyDisposable
1171     {
1172         void dispose()
1173         {
1174             count++;
1175         }
1176     }
1177 
1178     auto disposable = cast(shared) new MyDisposable;
1179 
1180     .atomicDispose(disposable);
1181     assert(count == 1);
1182     assert(disposable is null);
1183 
1184     .atomicDispose(disposable);
1185     assert(count == 1);
1186     assert(disposable is null);
1187 }
1188 ///
1189 unittest
1190 {
1191     size_t count;
1192     class MyDisposable : Disposable
1193     {
1194         void dispose()
1195         {
1196             count++;
1197         }
1198     }
1199 
1200     shared(Disposable) disposable = cast(shared) new MyDisposable;
1201 
1202     .atomicDispose(disposable);
1203     assert(count == 1);
1204     assert(disposable is null);
1205 
1206     .atomicDispose(disposable);
1207     assert(count == 1);
1208     assert(disposable is null);
1209 }
1210 ///
1211 unittest
1212 {
1213     class MyDisposable
1214     {
1215         void dispose()
1216         {
1217         }
1218     }
1219 
1220     shared(MyDisposable) disposable1 = null;
1221     .atomicDispose(disposable1);
1222     shared(Disposable) disposable2 = null;
1223     .atomicDispose(disposable2);
1224 }
1225 
1226 ///
1227 void tryDispose(T : Disposable)(ref T disposable)
1228 {
1229     if (disposable)
1230     {
1231         import std.algorithm : swap;
1232         T temp = null;
1233         swap(disposable, temp);
1234         if (temp !is null)
1235         {
1236             temp.dispose();
1237         }
1238     }
1239 }
1240 
1241 unittest
1242 {
1243     size_t count = 0;
1244     auto d = new AnonymousDisposable({ count++; });
1245 
1246     assert(count == 0);
1247     tryDispose(d);
1248     assert(count == 1);
1249     assert(d is null);
1250     tryDispose(d);
1251     assert(count == 1);
1252     assert(d is null);
1253 }