1 module rc.handler; 2 3 import rc.allocator; 4 5 version(rcdebugprint) 6 { 7 pragma(msg,"!!use rc debug print"); 8 import std.stdio; 9 } 10 11 version(unittest) 12 { 13 import std.range; 14 import std.array; 15 import std.format; 16 import std.algorithm; 17 } 18 19 /// 20 struct RCObject(T) 21 { 22 /// 23 T obj; 24 /// 25 alias obj this; 26 27 /// 28 this(this) { incRef(); } 29 30 /// 31 this( T o ) { obj = o; incRef(); } 32 33 /// no increment ref 34 package static auto make( T o ) 35 { 36 RCObject!T ret; 37 ret.obj = o; 38 return ret; 39 } 40 41 version(rcdebugprint) 42 { 43 /// no increment ref 44 package static auto make( string n, T o ) 45 { 46 RCObject!T ret; 47 ret.name = n; 48 ret.obj = o; 49 return ret; 50 } 51 52 this( string n, T o ) { name = n; obj = o; incRef(); } 53 string name = "__obj__"; 54 } 55 56 /// nothrow need for dispose 57 ~this() nothrow 58 { 59 if( obj is null ) return; 60 try 61 { 62 version(rcdebugprint) 63 writefln( " ** %12s dtor", name ); 64 decRef(); 65 } 66 catch(Exception e) 67 { 68 import std.experimental.logger; 69 try errorf( "ERROR: ", e ); 70 catch(Exception e) {} 71 } 72 } 73 74 /// 75 void incRef(string file=__FILE__,size_t line=__LINE__) // file and line for debug 76 { 77 version(rcdebugprint) 78 writef( " ** +++ ref %12s: ", name ); 79 if( obj is null ) 80 { 81 version(rcdebugprint) 82 writefln( "is null (%s:%d)", file, line ); 83 return; 84 } 85 RC.incRef(obj); 86 version(rcdebugprint) 87 writefln( "ok [%d] (%s:%d)", refCount, file, line ); 88 } 89 90 /// dispose object if refCount == 0 91 void decRef(string file=__FILE__,size_t line=__LINE__) // file and line for debug 92 { 93 version(rcdebugprint) 94 writef( " ** --- ref %12s: ", name ); 95 if( obj is null ) 96 { 97 version(rcdebugprint) 98 writefln( "is null (%s:%d)", file, line ); // debug 99 return; 100 } 101 assert( refCount > 0, "not null object have 0 refs" ); 102 103 obj = RC.decRef(obj); 104 105 version(rcdebugprint) 106 { 107 if( obj is null ) 108 writefln( " ok no refs (%s:%d)", file, line ); 109 else 110 writefln( " ok [%d] (%s:%d)", refCount, file, line ); 111 } 112 } 113 114 /// return 0 if object is null 115 size_t refCount() @property const 116 { 117 if( obj is null ) return 0; 118 return RC.refCount(obj); 119 } 120 121 /// 122 void opAssign(X=this)( auto ref RCObject!T r ) 123 { 124 version(rcdebugprint) 125 writefln( " opAssign %12s <- %12s", name, r.name ); 126 decRef(); 127 obj = r.obj; 128 version(rcdebugprint) 129 name = "<" ~ r.name ~ ">"; 130 incRef(); 131 } 132 133 /// 134 void opAssign(X=this)( auto ref T r ) 135 { 136 version(rcdebugprint) 137 writefln( " opAssign %12s <- %12s", name, r.name ); 138 decRef(); 139 obj = r; 140 version(rcdebugprint) 141 name = "<" ~ r.name ~ ">"; 142 incRef(); 143 } 144 } 145 146 /// 147 struct RCArray(T) 148 { 149 /// if a slice this link to original array 150 private T[] orig; 151 /// 152 T[] work; 153 154 /// 155 private void init( T[] origin, T[] slice ) 156 { 157 if( slice !is null ) 158 assert( slice.ptr >= origin.ptr && 159 slice.ptr < origin.ptr + origin.length, 160 "slice is not in original" ); 161 162 orig = origin; 163 incRef(); 164 165 work = slice is null ? orig : slice; 166 167 static if( isRCType!T ) 168 foreach( ref w; work ) w.incRef; 169 } 170 171 /// 172 alias work this; 173 174 /// 175 this(this) { incRef(); } 176 177 /// 178 this( T[] orig, T[] slice=null ) { init( orig, slice ); } 179 180 /// no increment ref 181 package static auto make( T[] o ) 182 { 183 RCArray!T ret; 184 ret.orig = o; 185 ret.work = o; 186 return ret; 187 } 188 189 version(rcdebugprint) 190 { 191 /// no increment ref 192 package static auto make( string n, T[] o ) 193 { 194 RCArray!T ret; 195 ret.name = n; 196 ret.orig = o; 197 ret.work = o; 198 return ret; 199 } 200 201 this( string n, T[] orig, T[] slice=null ) // debug 202 { 203 name = n; 204 init( orig, slice ); 205 } 206 207 auto opSlice( size_t i, size_t j ) 208 { return RCArray!T( name~".p", orig, work[i..j] ); } 209 } 210 else 211 { 212 /// 213 auto opSlice( size_t i, size_t j ) 214 { return RCArray!T( orig, work[i..j] ); } 215 } 216 217 /// 218 void opAssign(X=this)( auto ref RCArray!T arr ) 219 { 220 decRef(); 221 init( arr.orig, arr.work ); 222 // incRef in init 223 } 224 225 /// 226 void incRef(string file=__FILE__,size_t line=__LINE__) // file and line for debug 227 { 228 version(rcdebugprint) 229 writef( " [] +++ ref %12s: ", name ); 230 if( orig is null ) 231 { 232 version(rcdebugprint) 233 writefln( "is null (%s:%d)", file, line ); 234 return; 235 } 236 RC.incRef(orig); 237 version(rcdebugprint) 238 writefln( "ok [%d] (%s:%d)", refCount, file, line ); 239 } 240 241 /// dispose array if `refCount == 0` 242 void decRef(string file=__FILE__,size_t line=__LINE__) // file and line for debug 243 { 244 version(rcdebugprint) 245 writef( " [] --- ref %12s: ", name ); 246 if( orig is null ) 247 { 248 version(rcdebugprint) 249 writefln( "is null (%s:%d)", file, line ); 250 return; 251 } 252 253 assert( refCount > 0, "not null object have 0 refs" ); 254 255 orig = RC.decRef(orig); 256 257 version(rcdebugprint) 258 { 259 if( orig is null ) 260 writefln( " ok no refs (%s:%d)", file, line ); 261 else 262 writefln( " ok [%d] (%s:%d)", refCount, file, line ); 263 } 264 } 265 266 /// 267 size_t refCount() @property const 268 { 269 if( orig is null ) return 0; 270 return RC.refCount(orig); 271 } 272 273 version(rcdebugprint) 274 { 275 private string _name = "__arr__"; 276 string name() const @property { return _name; } 277 void name( string n ) @property { _name = n; } 278 } 279 280 ~this() 281 { 282 version(rcdebugprint) 283 writefln( "[] %s dtor start", name ); 284 285 if( refCount ) 286 { 287 /+ logic: 288 if `orig` saved only in this object 289 it means what only `work` set must have `refCount` > 0 290 otherwise it means what one ore more elements are saved 291 outside of `orig` and on dispose(orig) they must be saved 292 +/ 293 if( refCount == 1 ) 294 { 295 static if( isRCType!T ) 296 foreach( ref w; orig ) 297 if( w.refCount ) 298 // `work` set decriments futher 299 w.incRef; 300 } 301 302 static if( isRCType!T ) 303 foreach( ref w; work ) w.decRef; 304 305 // if `orig` saved only in this object 306 // additional decriment for all performs 307 // if they isRCType because RCObject and 308 // RCArray have decriment in destructor 309 decRef; 310 } 311 } 312 } 313 314 /// true if T is RCObject or RCArray 315 template isRCType(T) 316 { 317 static if( is( T E == RCObject!X, X ) || is( T E == RCArray!X, X ) ) 318 enum isRCType = true; 319 else 320 enum isRCType = false; 321 } 322 323 /// 324 auto rcMake(T,A...)( A args ) 325 { return RCObject!(T).make( RC.make!T(args) ); } 326 327 /// 328 unittest 329 { 330 static string[] log; 331 332 static class Par 333 { 334 this( uint i ) { log ~= "+par"; } 335 ~this() { log ~= "-par"; } 336 } 337 338 static class Ch : Par 339 { 340 this() { super(1); log ~= "+ch"; } 341 ~this() { log ~= "-ch"; } 342 } 343 344 assert( log.length == 0 ); 345 346 auto inc = 0; 347 { 348 RCObject!Ch c; 349 { 350 auto a = rcMake!Ch(); 351 assert( equal( log, ["+par","+ch"] ) ); 352 assert( a.refCount == 1 ); 353 auto b = a; 354 assert( equal( log, ["+par","+ch"] ) ); 355 assert( a.refCount == 2 ); 356 assert( b.refCount == 2 ); 357 c = a; 358 assert( a.refCount == 3 ); 359 assert( b.refCount == 3 ); 360 assert( c.refCount == 3 ); 361 b = rcMake!Ch(); 362 assert( a.refCount == 2 ); 363 assert( b.refCount == 1 ); 364 assert( c.refCount == 2 ); 365 assert( equal( log, ["+par","+ch","+par","+ch"] ) ); 366 b = rcMake!Ch(); 367 assert( a.refCount == 2 ); 368 assert( b.refCount == 1 ); 369 assert( c.refCount == 2 ); 370 assert( equal( log, ["+par","+ch","+par","+ch", 371 "+par","+ch", // new Ch stored in b 372 "-ch","-par" // old Ch in b 373 ] ) ); 374 } 375 assert( c.refCount == 1 ); 376 } 377 assert( equal( log, ["+par","+ch","+par","+ch", 378 "+par","+ch","-ch","-par", 379 "-ch","-par","-ch","-par"] ) ); 380 } 381 382 unittest 383 { 384 static string[] log; 385 386 static class A 387 { 388 this() { log ~= "+"; } 389 ~this() { log ~= "-"; } 390 } 391 392 static class B 393 { 394 A a; 395 this() { this.a = RC.make!A(); } 396 this( A a ) 397 { 398 this.a = a; 399 RC.incRef(a); 400 } 401 402 ~this() { RC.decRef(a); } 403 } 404 405 { 406 assert( log.length == 0 ); 407 RCObject!B b; 408 { 409 auto a = rcMake!A(); 410 auto x = rcMake!B(a); 411 assert( equal( log, ["+"] ) ); 412 assert( RC.refCount(x.a) == 2 ); 413 assert( x.refCount == 1 ); 414 b = x; 415 assert( a.refCount == 2 ); 416 assert( RC.refCount(x.a) == 2 ); 417 assert( x.refCount == 2 ); 418 x = rcMake!B(); 419 assert( b.refCount == 1 ); 420 assert( x.refCount == 1 ); 421 assert( a.refCount == 2 ); 422 assert( a.obj is b.a ); 423 assert( RC.refCount(b.a) == 2 ); 424 assert( RC.refCount(x.a) == 1 ); 425 } 426 assert( RC.refCount(b.a) == 1 ); 427 assert( b.refCount == 1 ); 428 assert( equal( log, ["+","+","-"] ) ); 429 } 430 assert( equal( log, ["+","+","-","-"] ) ); 431 } 432 433 /// 434 auto rcMakeArray(T,A...)( A args ) 435 { return RCArray!(T).make( RC.makeArray!T(args) ); } 436 437 unittest 438 { 439 static string[] log; 440 static string[] exp; 441 static void change(string s)( int[] i... ) 442 { exp ~= i.map!(a=>format("%s%d",s,a)).array; } 443 static void add( int[] i... ) { change!"+"(i); } 444 static void rem( int[] i... ) { change!"-"(i); } 445 446 static class A 447 { 448 int no; 449 this( int i ) { no=i; log ~= format("+%d",i); } 450 ~this() { log ~= format("-%d",no); } 451 } 452 453 alias RCA = RCObject!A; 454 455 { 456 RCA obj1; 457 RCA obj2; 458 459 assert( equal( log, exp ) ); 460 { 461 RCArray!RCA tmp; 462 { 463 auto arr = rcMakeArray!RCA(6); 464 assert( equal( log, exp ) ); 465 foreach( int i, ref a; arr ) 466 { 467 a = rcMake!A(i); 468 add(i); 469 } 470 assert( equal( log, exp ) ); 471 472 foreach( ref a; arr ) 473 assert( a.refCount == 1 ); 474 475 obj1 = arr[1]; 476 obj2 = arr[2]; 477 478 assert( equal( log, exp ) ); 479 480 assert( obj1.refCount == 2 ); 481 assert( obj2.refCount == 2 ); 482 assert( arr[1].refCount == 2 ); 483 assert( arr[2].refCount == 2 ); 484 485 arr[2] = rcMake!A(11); 486 add(11); 487 assert( equal( log, exp ) ); 488 assert( obj2.refCount == 1 ); 489 assert( arr[2].refCount == 1 ); 490 assert( arr[2].obj != obj2.obj ); 491 492 arr[3] = rcMake!A(88); 493 add(88); rem(3); 494 assert( equal( log, exp ) ); 495 assert( arr[3].refCount == 1 ); 496 497 tmp = arr[3..5]; 498 assert( equal( log, exp ) ); 499 assert( arr[0].refCount == 1 ); 500 assert( arr[1].refCount == 2 ); 501 assert( arr[2].refCount == 1 ); 502 assert( arr[2].obj.no == 11 ); 503 assert( arr[3].refCount == 2 ); 504 assert( arr[3].obj.no == 88 ); 505 assert( arr[4].refCount == 2 ); 506 assert( arr[5].refCount == 1 ); 507 } 508 rem( 0, 11, 5 ); 509 assert( equal( log, exp ) ); 510 } 511 rem( 88, 4 ); 512 assert( equal( log, exp ) ); 513 assert( obj1.refCount == 1 ); 514 assert( obj1.obj.no == 1 ); 515 assert( obj2.refCount == 1 ); 516 assert( obj2.obj.no == 2 ); 517 } 518 rem( 2, 1 ); 519 assert( equal( log, exp ) ); 520 } 521 522 // 523 unittest 524 { 525 static string[] log; // what happens 526 static string[] exp; // expected 527 528 // write to expected 529 static void change(string s)( int[] i... ) 530 { exp ~= i.map!(a=>format("%s%d",s,a)).array; } 531 532 // ditto 533 static void add( int[] i... ) { change!"+"(i); } 534 // ditto 535 static void rem( int[] i... ) { change!"-"(i); } 536 537 // writes to log construction and destruction events 538 static class A 539 { 540 int no; 541 this( int i ) { no=i; log ~= format("+%d",i); } 542 ~this() { log ~= format("-%d",no); } 543 } 544 545 alias RCA = RCObject!A; 546 547 { 548 RCA obj; 549 { 550 RCArray!RCA tmp1; 551 { 552 RCArray!RCA tmp2; 553 { 554 assert( equal( log, exp ) ); 555 auto arr = rcMakeArray!RCA(6); 556 assert( equal( log, exp ) ); 557 foreach( int i, ref a; arr ) 558 { 559 a = rcMake!A(i); 560 add(i); 561 } 562 assert( equal( log, exp ) ); 563 assert( arr[0].refCount == 1 ); 564 assert( arr[1].refCount == 1 ); 565 assert( arr[2].refCount == 1 ); 566 assert( arr[3].refCount == 1 ); 567 assert( arr[4].refCount == 1 ); 568 assert( arr[5].refCount == 1 ); 569 570 tmp1 = arr[1..4]; 571 assert( equal( log, exp ) ); 572 assert( arr[0].refCount == 1 ); 573 assert( arr[1].refCount == 2 ); 574 assert( arr[2].refCount == 2 ); 575 assert( arr[3].refCount == 2 ); 576 assert( arr[4].refCount == 1 ); 577 assert( arr[5].refCount == 1 ); 578 579 tmp2 = arr[3..5]; 580 assert( equal( log, exp ) ); 581 assert( arr[0].refCount == 1 ); 582 assert( arr[1].refCount == 2 ); 583 assert( arr[2].refCount == 2 ); 584 assert( arr[3].refCount == 3 ); 585 assert( arr[4].refCount == 2 ); 586 assert( arr[5].refCount == 1 ); 587 588 obj = tmp2[0]; 589 assert( equal( log, exp ) ); 590 assert( arr[0].refCount == 1 ); 591 assert( arr[1].refCount == 2 ); 592 assert( arr[2].refCount == 2 ); 593 assert( arr[3].refCount == 4 ); 594 assert( arr[4].refCount == 2 ); 595 assert( arr[5].refCount == 1 ); 596 } 597 rem(0,5); 598 assert( equal( log, exp ) ); 599 assert( tmp1[0].refCount == 1 ); 600 assert( tmp1[1].refCount == 1 ); 601 assert( tmp1[2].refCount == 3 ); 602 603 assert( obj.refCount == 3 ); 604 605 assert( tmp2[0].refCount == 3 ); 606 assert( tmp2[0].obj.no == 3 ); 607 assert( tmp2[1].refCount == 1 ); 608 assert( tmp2[1].obj.no == 4 ); 609 } 610 rem(4); 611 assert( equal( log, exp ) ); 612 assert( tmp1[0].refCount == 1 ); 613 assert( tmp1[1].refCount == 1 ); 614 assert( tmp1[2].refCount == 2 ); 615 assert( obj.refCount == 2 ); 616 } 617 rem(1,2); 618 assert( equal( log, exp ) ); 619 assert( obj.refCount == 1 ); 620 } 621 rem(3); 622 assert( equal( log, exp ) ); 623 } 624 625 version(rcdebugprint) 626 { 627 auto rcMakeNamed(T,A...)( string name, A args ) 628 { return RCObject!(T).make( name, RC.make!T(args) ); } 629 630 auto rcMakeNamedArray(T,A...)( string name, A args ) 631 { return RCArray!(T).make( name, RC.makeArray!T(args) ); } 632 }