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 }