A simple sketch of a library for how you could implement some good locking primitives on top of Nice. For an object myLock implementing the Lockable interface you can use it as follows:
locking(myLock)
{
// code
}
This will do whatever is neccessary (as specified by the implementation of the interface) to lock and then unlock the object.
Lockables can be combined with the & operator. myFirstLock & mySecondLock will acquire both locks 'simultaneously' - it will try them in some order. If it doesn't acquire all the locks it will unlock those it has acquired and wait until it can acquire the one it failed to acquire, with some random waits in there to avoid live lock.
Unfortunately Java's locking libraries don't appear to be all that great, so this is of limited use at the moment. :-) It's more a demonstration of a point and might be portable to C#.
package locking;
import java.util.concurrent.locks.*;
import java.util.concurrent.*;
import java.util.*;
interface Lockable { }
class NiceLock implements Lock, Lockable {
private final Lock lock;
lock() = lock.lock();
lockInterruptibly() = lock.lockInterruptibly();
newCondition() = lock.newCondition();
tryLock() = lock.tryLock();
tryLock(long time, TimeUnit unit) = lock.tryLock(time, unit);
tryLock(long time, null) = lock.tryLock(time, TimeUnit.MILLISECONDS);
unlock() = lock.unlock();}
class SynchronisedLock implements Lockable{
Object object;
}
override void locking(SynchronisedLock lock, () -> void action) {
synchronized(lock.object) { action(); }}
/** This is wrong. I can't figure out how to make it work properly at the minute. */
override void tryLocking(SynchronisedLock lock, () -> void action) = locking(lock, action);
void locking(Lockable lock, () -> void action);
void tryLocking(Lockable lock, () -> void action);
class LockNotAvailableException extends Exception { Lockable lock; }
override void locking(NiceLock lock, () -> void action){
try {
lock.lock.lock();
action();}
finally{ lock.lock.unlock(); }}
override void tryLocking (NiceLock lock, () -> void action){
try {
if (!lock.tryLock()) throw new LockNotAvailableException(lock : lock);
else action();}
finally{
lock.unlock();}}
void tryLocking(List<Lockable> locks, () -> void action){
if (locks.size() == 0) action();
else{
tryLocking(locks[0]){
tryLocking(locks.slice(from : 1), action);}}}
class JoinedLock implements Lockable{
private Lockable[] locks;
private Random random = new Random();}
override void locking (JoinedLock lock, () -> void action){
boolean failed = false;
do{
try{
locking (lock.locks[0]){
tryLocking(lock.locks.slice(from : 1), action);
failed = false;}}
catch (LockNotAvailableException e){
lock.locks = concat([e.lock], lock.locks.filter(Lockable l => (l != e.lock)));
failed = true;
/* Sleep for a random amount of time to prevent live locking. */
Thread.sleep(lock.random.nextInt(100));}}
while (failed);}
override void tryLocking (JoinedLock lock, () -> void action) = tryLocking (lock.locks, action);
Lockable `&` (Lockable first, Lockable second) = new JoinedLock (locks : [first, second]);
override Lockable `&` (JoinedLock first, Lockable second) = new JoinedLock (locks : concat(first.locks, [second]));
override Lockable `&` (Lockable first, JoinedLock second) = new JoinedLock (locks : concat([first], second.locks));
override Lockable `&` (JoinedLock first, JoinedLock second) = new JoinedLock (locks : concat(first.locks, second.locks));