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 }