digitalmars.D - How to use synchronized() {} as the basis for a freely (un)lockable
- downs (40/68) Jan 09 2008 Disclaimer: My StackThreads are neither particularly fast (130 cycles pe...
- Sean Kelly (7/13) Jan 09 2008 The Tango mutexes can also be used with the 'synchronized' statement.
- downs (28/51) Jan 09 2008 Because the structure of the code looks like this:
- Sean Kelly (19/74) Jan 09 2008 I can't say I've ever actually seen the need for such an algorithm in
Disclaimer: My StackThreads are neither particularly fast (130 cycles per context switch), nor particularly stable. This is primarily intended as a Proof of Concept, even though I do use it in some of my code. :) Have you ever wished D had a Mutex class that could be locked or unlocked at any time? D's synchronized() {} statement is nice and all, but does have some weaknesses, primarily that it can only be used to synchronize some scope - it is not possible to unlock the underlying mutex in the middle of a block. Of course, I could write code that depends upon the OS' mutexes, but that's hardly in the spirit of D. Of course, I could write a busy-spin based mutex class, but it is my firm belief that busyspin *expletive* *expletive*. So, there remains only one solution: twist synchronized(){} so that it becomes freely (un)lockable. This requires a way to break out of the middle of a synchronized block, without ending the synchronization. Stackthreads offer such a way. Consider the following code from scrapple.tools.threads:import tools.stackthreads, std.thread; class Lock { StackThread!(bool, void)[] toggle; Thread[] toggle_thr; Object toggle_sync; this() { toggle_sync=new Object; } StackThread!(bool, void) getMyST() { auto thr=Thread.getThis(); foreach (id, entry; toggle_thr) if (entry is thr) return toggle[id]; auto nt=stackthread=(Object This, bool delegate() which) { while (true) { if (!which()) throw new Exception("Cannot double-free lock"); synchronized(This) if (which()) throw new Exception("Cannot double-claim lock"); } } /fix/ this; synchronized (toggle_sync) { toggle_thr ~= thr; toggle ~= nt; } return nt; } void lock(bool locking) { getMyST()(locking); } void lock() { lock(true); } void unlock() { lock(false); } void Synchronized(void delegate() dg) { lock; scope(exit) unlock; dg(); } void Unsynchronized(void delegate() dg) { unlock; scope(exit) lock; dg(); } }What does this code do? Basically, it assigns every thread that tries to use the lock, a Stackthread (slightly wasteful but meh). getMyST returns the current thread's Stackthread, or creates it if it doesn't exist yet. Basically, each StackThread takes bools for input, and nothing for output ( "!(bool, void)" ). The ST must be fed true and false in turn; true for "Go and enter the synchronized {} block", and false for "Leave the synchronized {} block". ( For those unfamiliar with stackthreads, whenever the delegate calls "which", the stackthread is suspended until the surrounding function calls it with a bool value. At this point, the stackthread delegate resumes, with "which" returning the value the stackthread was called with. ) An example application would be the creation of a cache class. class Cache(VAL, KEY) { VAL[KEY] buffer; Lock lock; VAL delegate(KEY) dg; this(typeof(dg) _dg) { dg=_dg; New(lock); } VAL get(KEY key) { VAL res; lock.Synchronized = { if (key in buffer) res = buffer[key]; else { lock.Unsynchronized = { res = dg(key); }; buffer[key] = res; }; return res; } } And yes, I know this class doesn't handle the case of simulataneous evaluation of dg with the same key. It's only an example. :) --downs
Jan 09 2008
downs wrote:Disclaimer: My StackThreads are neither particularly fast (130 cycles per context switch), nor particularly stable. This is primarily intended as a Proof of Concept, even though I do use it in some of my code. :) Have you ever wished D had a Mutex class that could be locked or unlocked at any time?Tango does :-)D's synchronized() {} statement is nice and all, but does have some weaknesses, primarily that it can only be used to synchronize some scope - it is not possible to unlock the underlying mutex in the middle of a block.The Tango mutexes can also be used with the 'synchronized' statement. However, I'm not sure I like the idea of being inside a 'synchronized' block and having the mutex unlocked. Why not just break the code into two sequential 'synchronized' blocks? Sean
Jan 09 2008
Sean Kelly wrote:downs wrote:Clarification. D/Phobos.Disclaimer: My StackThreads are neither particularly fast (130 cycles per context switch), nor particularly stable. This is primarily intended as a Proof of Concept, even though I do use it in some of my code. :) Have you ever wished D had a Mutex class that could be locked or unlocked at any time?Tango does :-)Because the structure of the code looks like this: foo; synchronized { bar; whee { lol; *unsynchronized* { lmao; } meep; } baz; } So, to break it into two synchronized statements, my code would have to look like so ... foo; synchronized { bar; whee { lol; } lmao; synchronized { meep; } // closing bracket of whee baz; } See the problem? :) --downsD's synchronized() {} statement is nice and all, but does have some weaknesses, primarily that it can only be used to synchronize some scope - it is not possible to unlock the underlying mutex in the middle of a block.The Tango mutexes can also be used with the 'synchronized' statement. However, I'm not sure I like the idea of being inside a 'synchronized' block and having the mutex unlocked. Why not just break the code into two sequential 'synchronized' blocks? Sean
Jan 09 2008
downs wrote:Sean Kelly wrote:I can't say I've ever actually seen the need for such an algorithm in practice (outside of condition variables), but in Tango you could do: synchronized( mutex ) { bar; whee { lol; { mutex.unlock; scope(exit) mutex.lock; meep; } baz; } } Can't say I have a solution for Phobos though. Seandowns wrote:Clarification. D/Phobos.Disclaimer: My StackThreads are neither particularly fast (130 cycles per context switch), nor particularly stable. This is primarily intended as a Proof of Concept, even though I do use it in some of my code. :) Have you ever wished D had a Mutex class that could be locked or unlocked at any time?Tango does :-)Because the structure of the code looks like this: foo; synchronized { bar; whee { lol; *unsynchronized* { lmao; } meep; } baz; } So, to break it into two synchronized statements, my code would have to look like so ... foo; synchronized { bar; whee { lol; } lmao; synchronized { meep; } // closing bracket of whee baz; } See the problem? :)D's synchronized() {} statement is nice and all, but does have some weaknesses, primarily that it can only be used to synchronize some scope - it is not possible to unlock the underlying mutex in the middle of a block.The Tango mutexes can also be used with the 'synchronized' statement. However, I'm not sure I like the idea of being inside a 'synchronized' block and having the mutex unlocked. Why not just break the code into two sequential 'synchronized' blocks? Sean
Jan 09 2008