Changeset 40 for trunk/infrastructure/st
- Timestamp:
- 10/24/06 01:24:13 (2 years ago)
- Files:
-
- trunk/infrastructure/st/stackcontext.d (modified) (33 diffs)
- trunk/infrastructure/st/stackthread.d (modified) (1 diff)
- trunk/infrastructure/st/tls.d (added)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/infrastructure/st/stackcontext.d
r37 r40 19 19 * implementations. 20 20 * 21 * Version: 0.1 022 * Date: June 30, 200621 * Version: 0.12 22 * Date: October 17, 2006 23 23 * Authors: Mikola Lysenko, mclysenk@mtu.edu 24 24 * License: Use/copy/modify freely, just give credit. … … 31 31 * since overflows are now trapped. 32 32 * 33 * Implementation is not thread safe.34 *35 33 * DMD has a bug on linux with multiple delegates in a 36 34 * scope. Be aware that the linux version may have … … 48 46 * removeRange I have set it as optional. 49 47 * 48 * GDC version does not support assembler optimizations, since 49 * it uses a different calling convention. 50 * 50 51 * History: 52 * v0.12 - Workaround for DMD bug. 53 * 54 * v0.11 - Implementation is now thread safe. 55 * 51 56 * v0.10 - Added the LEAK_FIX flag to work around the 52 57 * slowness of std.gc.removeRange … … 81 86 std.stdio, 82 87 std.string, 83 std.gc; 88 std.gc, 89 st.tls; 90 91 //Handle versions 92 version(D_InlineAsm_X86) 93 { 94 version(DigitalMars) 95 { 96 version(Win32) version = SC_WIN_ASM; 97 version(linux) version = SC_LIN_ASM; 98 } 99 100 //GDC uses a different calling conventions, need to reverse engineer them later 101 } 102 84 103 85 104 /// The default size of a StackContext's stack … … 136 155 } 137 156 } 157 158 138 159 139 160 … … 255 276 * 256 277 ******************************************************/ 257 public class StackContext278 public final class StackContext 258 279 { 259 280 /** … … 335 356 { 336 357 assert(state != CONTEXT_STATE.RUNNING); 337 assert(current_context !is this);358 assert(current_context.val !is this); 338 359 } 339 360 body … … 357 378 * bubbled up through this method. 358 379 */ 359 public void run()380 public final void run() 360 381 { 361 382 debug (StackContext) writefln("Running %s", this.toString); 362 383 363 384 //We must be ready to run 364 if(state != CONTEXT_STATE.READY) 365 { 366 throw new ContextException(this, 367 "Context is not in a runnable state"); 368 } 385 assert(state == CONTEXT_STATE.READY, 386 "Context is not in a runnable state"); 369 387 370 388 //Save the old context 371 StackContext tmp = current_context ;389 StackContext tmp = current_context.val; 372 390 373 391 version(LEAK_FIX) … … 379 397 380 398 //Set new context 381 current_context = this;399 current_context.val = this; 382 400 ctx.switchIn(); 383 current_context = tmp;401 current_context.val = tmp; 384 402 385 403 assert(state != CONTEXT_STATE.RUNNING); … … 428 446 * running context. 429 447 */ 430 public static void yield() 431 { 432 if(current_context is null) 433 { 434 throw new ContextException( 435 null, 436 "Tried to yield without any running contexts."); 437 } 438 439 debug (StackContext) writefln("Yielding %s", current_context.toString); 440 441 assert(current_context.running); 448 public final static void yield() 449 { 450 StackContext cur_ctx = current_context.val; 451 452 //Make sure we are actually running 453 assert(cur_ctx !is null, 454 "Tried to yield without any running contexts."); 455 456 debug (StackContext) writefln("Yielding %s", cur_ctx.toString); 457 458 assert(cur_ctx.running); 442 459 443 460 //Leave the current context 444 cur rent_context.state = CONTEXT_STATE.READY;445 StackContext tmp = cur rent_context;461 cur_ctx.state = CONTEXT_STATE.READY; 462 StackContext tmp = cur_ctx; 446 463 447 464 version(LEAK_FIX) 448 465 { 449 466 //Save the GC range 450 cur rent_context.gc_start = cast(void*)&tmp;467 cur_ctx.gc_start = cast(void*)&tmp; 451 468 debug (LogGC) writefln("Adding range: %8x-%8x", 452 cur rent_context.gc_start, current_context.ctx.stack_top);453 addRange(cur rent_context.gc_start, current_context.ctx.stack_top);469 cur_ctx.gc_start, cur_ctx.ctx.stack_top); 470 addRange(cur_ctx.gc_start, cur_ctx.ctx.stack_top); 454 471 } 455 472 456 473 //Swap 457 cur rent_context.ctx.switchOut();474 cur_ctx.ctx.switchOut(); 458 475 459 476 version(LEAK_FIX) 460 477 { 478 StackContext t_ctx = current_context.val; 479 461 480 //Remove the GC range 462 481 debug (LogGC) writefln("Removing range: %8x", 463 current_context.gc_start);464 assert( current_context.gc_start !is null);465 removeRange( current_context.gc_start);466 current_context.gc_start = null;482 t_ctx.gc_start); 483 assert(t_ctx.gc_start !is null); 484 removeRange(t_ctx.gc_start); 485 t_ctx.gc_start = null; 467 486 } 468 487 469 488 //Return 470 current_context = tmp;471 current_context.state = CONTEXT_STATE.RUNNING;472 473 debug (StackContext) writefln("Resuming context: %s", current_context.toString);489 current_context.val = tmp; 490 tmp.state = CONTEXT_STATE.RUNNING; 491 492 debug (StackContext) writefln("Resuming context: %s", tmp.toString); 474 493 } 475 494 … … 484 503 * t = The exception object we will propagate. 485 504 */ 486 public static void throwYield(Object t)487 { 488 last_exception = t;505 public final static void throwYield(Object t) 506 { 507 current_context.val.last_exception = t; 489 508 yield(); 490 509 } … … 496 515 * A ContextException if the context is running. 497 516 */ 498 public void restart()517 public final void restart() 499 518 { 500 519 debug (StackContext) writefln("Restarting %s", this.toString); 501 520 502 if(state == CONTEXT_STATE.RUNNING) 503 { 504 throw new ContextException(this, 505 "Cannot reset this context while it is running!"); 506 } 521 assert(state != CONTEXT_STATE.RUNNING, 522 "Cannot restart a context while it is running"); 507 523 508 524 //Reset the context 525 restartStack(); 526 } 527 528 /** 529 * Recycles the context by restarting it with a new delegate. This 530 * can save resources by allowing a program to reuse previously 531 * allocated contexts. 532 * 533 * Params: 534 * dg = The delegate which we will be running. 535 */ 536 public final void recycle(void delegate() dg) 537 { 538 debug (StackContext) writefln("Recycling %s", this.toString); 539 540 assert(state != CONTEXT_STATE.RUNNING, 541 "Cannot recycle a context while it is running"); 542 543 //Set the delegate and restart 544 proc = dg; 509 545 restartStack(); 510 546 } … … 519 555 * A ContextException if the context is not READY. 520 556 */ 521 public void kill() 522 { 523 if(state == CONTEXT_STATE.RUNNING) 524 { 525 throw new ContextException(this, "Cannot kill a context if it is not ready"); 526 } 527 else if(state == CONTEXT_STATE.DEAD) 528 { 529 return; 530 } 557 public final void kill() 558 { 559 assert(state != CONTEXT_STATE.RUNNING, 560 "Cannot kill a context while it is running."); 561 531 562 532 563 version(LEAK_FIX) 533 564 { 565 if(state == CONTEXT_STATE.DEAD) 566 { 567 return; 568 } 569 534 570 //Clear the GC ranges if necessary 535 571 if(gc_start !is null) … … 550 586 * Returns: A string describing the context. 551 587 */ 552 public char[] toString()588 public final char[] toString() 553 589 { 554 590 static char[][] state_names = … … 622 658 public static StackContext getRunning() 623 659 { 624 return current_context ;660 return current_context.val; 625 661 } 626 662 … … 633 669 //Make sure context is running 634 670 //assert(ctx.old_stack_pointer !is null); 635 assert(current_context !is null);671 assert(current_context.val !is null); 636 672 637 673 case CONTEXT_STATE.READY: … … 655 691 } 656 692 } 657 658 693 659 694 version(LEAK_FIX) 660 695 { … … 669 704 private CONTEXT_STATE state; 670 705 671 //FIXME: All static objects should be in thread local672 //storage. Not sure how to do this effectively yet.673 674 /*BEGIN TLS {*/675 676 706 // The last exception generated 677 707 private static Object last_exception = null; 678 708 709 /*BEGIN TLS {*/ 710 679 711 // The currently running stack context 680 private static StackContextcurrent_context = null;681 712 private static ThreadLocal!(StackContext) current_context = null; 713 682 714 /*} END TLS*/ 683 715 … … 767 799 in 768 800 { 769 assert(current_context !is null);801 assert(current_context.val !is null); 770 802 version(LEAK_FIX) 771 assert(current_context. gc_start is null);803 assert(current_context.val.gc_start is null); 772 804 } 773 805 body 774 806 { 807 StackContext cur_ctx = current_context.val; 808 775 809 try 776 810 { 777 811 //Set state to running, enter the context 778 cur rent_context.state = CONTEXT_STATE.RUNNING;779 debug (StackContext) writefln("Starting %s", cur rent_context.toString);780 cur rent_context.proc();781 debug (StackContext) writefln("Finished %s", cur rent_context.toString);812 cur_ctx.state = CONTEXT_STATE.RUNNING; 813 debug (StackContext) writefln("Starting %s", cur_ctx.toString); 814 cur_ctx.proc(); 815 debug (StackContext) writefln("Finished %s", cur_ctx.toString); 782 816 } 783 817 catch(Object o) 784 818 { 785 819 //Save exceptions so we can throw them later 786 debug (StackContext) writefln("Got an exception: %s, in %s", o.toString, cur rent_context.toString);787 last_exception = o;820 debug (StackContext) writefln("Got an exception: %s, in %s", o.toString, cur_ctx.toString); 821 cur_ctx.last_exception = o; 788 822 } 789 823 finally … … 791 825 //Leave the object. Don't need to worry about 792 826 //GC, since it should already be released. 793 cur rent_context.state = CONTEXT_STATE.DEAD;794 debug (StackContext) writefln("Leaving %s", cur rent_context.toString);795 cur rent_context.ctx.switchOut();827 cur_ctx.state = CONTEXT_STATE.DEAD; 828 debug (StackContext) writefln("Leaving %s", cur_ctx.toString); 829 cur_ctx.ctx.switchOut(); 796 830 } 797 831 … … 807 841 version(Win32) 808 842 { 809 if(current_context is null) 843 StackContext cur = current_context.val; 844 845 if(cur is null) 810 846 return os_query_stackBottom(); 811 847 812 return cur rent_context.ctx.stack_top;848 return cur.ctx.stack_top; 813 849 } 814 850 else … … 819 855 } 820 856 } 857 858 static this() 859 { 860 StackContext.current_context = new ThreadLocal!(StackContext); 861 862 version(SC_WIN_ASM) 863 { 864 //Get the system's page size 865 SYSTEM_INFO sys_info; 866 GetSystemInfo(&sys_info); 867 page_size = sys_info.dwPageSize; 868 } 869 } 870 821 871 822 872 /******************************************************** … … 828 878 ********************************************************/ 829 879 830 private version ( Win32)880 private version (SC_WIN_ASM) 831 881 { 832 882 … … 904 954 size_t page_size; 905 955 906 static this()907 {908 //Get the system's page size909 SYSTEM_INFO sys_info;910 GetSystemInfo(&sys_info);911 page_size = sys_info.dwPageSize;912 }913 956 914 957 private struct SysContext … … 1035 1078 void killStack() 1036 1079 { 1080 //Work around for bug in DMD 0.170 1081 if(stack_bottom is null) 1082 { 1083 debug(StackContext) 1084 writefln("WARNING!!!! Accidentally deleted a context twice"); 1085 return; 1086 } 1087 1037 1088 debug (LogStack) 1038 1089 { … … 1141 1192 } 1142 1193 } 1143 else private version( linux)1194 else private version(SC_LIN_ASM) 1144 1195 { 1145 1196 … … 1225 1276 //Initialize stack pointer 1226 1277 stack_pointer = stack_top; 1227 1228 //Initialize stack state 1229 void push(uint val) 1230 { 1231 stack_pointer -= 4; 1232 *cast(uint*)stack_pointer = val; 1233 } 1234 1235 push(cast(uint)&StackContext.startContext); //Start point 1236 push(0); //EBP 1237 push(0); //EBX 1238 push(0); //ESI 1239 push(0); //EDI 1278 1279 //Initialize stack state 1280 *cast(uint*)(stack_pointer-4) = cast(uint)&StackContext.startContext; 1281 stack_pointer -= 20; 1240 1282 } 1241 1283 … … 1245 1287 void killStack() 1246 1288 { 1289 //Make sure the GC didn't accidentally double collect us... 1290 if(stack_bottom is null) 1291 { 1292 debug(StackContext) writefln("WARNING!!! Accidentally killed stack twice"); 1293 return; 1294 } 1295 1247 1296 //Deallocate the stack 1248 1297 if(munmap(stack_bottom, (stack_top - stack_bottom))) … … 1343 1392 else 1344 1393 { 1345 //Unsupported system 1346 static assert(false, "Stack Context: System Unsupported"); 1394 static assert(false, "System currently unsupported"); 1347 1395 } 1348 1396 … … 1393 1441 assert(s1 == 0); 1394 1442 assert(a.getState == CONTEXT_STATE.DEAD); 1395 assert(b.getState == CONTEXT_STATE.READY); 1396 1397 try 1398 { 1399 a.run(); 1400 assert(false); 1401 } 1402 catch(ContextException ce) 1403 { 1404 debug writefln("Generated exception correctly"); 1405 } 1406 1443 assert(b.getState == CONTEXT_STATE.READY); 1407 1444 1408 1445 assert(b.getState == CONTEXT_STATE.READY); … … 1681 1718 } 1682 1719 1720 writefln("blah2"); 1721 1683 1722 assert(a); 1684 1723 assert(b); … … 1900 1939 delete a; 1901 1940 1902 StackContext b;1903 1904 b = new StackContext(1905 delegate void()1906 {1907 b.restart();1908 assert(false);1909 });1910 1911 try1912 {1913 b.run();1914 assert(false);1915 }1916 catch(ContextException e)1917 {1918 e.print;1919 }1920 1921 try1922 {1923 StackContext.yield();1924 assert(false);1925 }1926 catch(ContextException e)1927 {1928 e.print;1929 }1930 1941 1931 1942 writefln("Standard exceptions passed"); … … 2215 2226 } 2216 2227 2228 unittest 2229 { 2230 writefln("Testing thread safety"); 2231 2232 int x = 0, y = 0; 2233 2234 StackContext sc0 = new StackContext( 2235 { 2236 while(true) 2237 { 2238 x++; 2239 StackContext.yield; 2240 } 2241 }); 2242 2243 StackContext sc1 = new StackContext( 2244 { 2245 while(true) 2246 { 2247 y++; 2248 StackContext.yield; 2249 } 2250 }); 2251 2252 Thread t0 = new Thread( 2253 { 2254 for(int i=0; i<10000; i++) 2255 sc0.run(); 2256 2257 return 0; 2258 }); 2259 2260 Thread t1 = new Thread( 2261 { 2262 for(int i=0; i<10000; i++) 2263 sc1.run(); 2264 2265 return 0; 2266 }); 2267 2268 assert(sc0); 2269 assert(sc1); 2270 assert(t0); 2271 assert(t1); 2272 2273 t0.start; 2274 t1.start; 2275 t0.wait; 2276 t1.wait; 2277 2278 assert(x == 10000); 2279 assert(y == 10000); 2280 2281 writefln("Thread safety passed!"); 2282 } 2283 trunk/infrastructure/st/stackthread.d
r37 r40 21 21 * 22 22 * Bugs: 23 * None known yet. 23 * Not thread safe. May be changed in future versions, 24 * however this will require a radical refactoring. 24 25 * 25 26 * History:
