Reentrant objects and using clauses
A reentrant object supports multiple concurrent uses of a single lock in a single thread. Reentrancy maintains a count of the number of times the thread sets and releases the lock. In reentrant lock use, the thread does not deadlock on setting an already locked lock, nor release the lock when other code in the thread still retains that lock. Reentrant objects help manage safe access to data that is shared by reentrant or concurrent code execution. For example, if you must store data that is shared by multiple threads, ensure that you protect against concurrent access from multiple threads to prevent data corruption. The most prominent type of shared data is class static variables, which are variables that are stored on the Gosu class itself.
For Gosu to recognize a valid reentrant object, the object must have at least one of the following attributes:
- The object implements the
java.util.concurrent.locks.Lockinterface. For example, the following Java classes in that package implement this interface:ReentrantLock,ReadWriteLock,Condition. - You cast the object to the Gosu interface
IMonitorLock. You can cast any arbitrary object toIMonitorLock. It is useful to cast Java monitor locks to this Gosu interface. - The object implements the Gosu class gw.lang.IReentrant. This
interface contains two methods with no arguments:
enterandexit. Your implementation code must properly lock or synchronize data access as appropriate during the enter method and release any locks in the exit method.The profiler tag class (gw.api.profiler.ProfilerTag) implements the IReentrant interface. This class adds hints to the Gosu profiler about what actions happen in a block of code.
For blocks of code that use locks by implementing
java.util.concurrent.locks.Lock, a using clause
simplifies the code. The using statement always cleans up the lock, even if
the code throws an exception.
uses java.util.concurrent
// In your class variable definitions...
var _lock : ReentrantLock = new ReentrantLock()
...
function useReentrantLockNew() {
using ( _lock ) {
// Do your main work here
}
}
Similarly, you can cast any object to a monitor lock by adding
as IMonitorLock after the object. For example, the following method code
uses the object itself, by using the keyword this, as the monitor lock:
function monitorLock() {
using ( this as IMonitorLock ) {
// Do stuff
}
}
This approach is equivalent to a synchronized block in the
Java language.
The following code uses the java.util.concurrent.locks.ReentrantLock
class using more verbose try and finally statements. This
approach is not recommended:
// In your class variable definitions...
var _lock : ReentrantLock = new ReentrantLock()
function useReentrantLockOld() {
_lock.lock()
try {
// Do your main work here
}
finally {
lock.unlock()
}
}
Alternatively, you can do your change in a Gosu block. Guidewire does not recommend this
approach. Returning the value from a block imposes more restrictions on how you implement
return statements. It is typically better to use the
using statement structure shown earlier in this topic.
A using clause supports passing multiple objects. If you pass
multiple objects to the using clause, exit actions (closing,
disposing, unlocking) happen in reverse order of the object declarations.
See also
http://en.wikipedia.org/wiki/Monitor_(synchronization)- Object life-cycle management with using clauses
- Concurrency and thread safety
- Using profiler tags
