/* * NBExchanger.java * * Created on May 21, 2007, 3:29 PM * * From "Multiprocessor Synchronization and Concurrent Data Structures", * by Maurice Herlihy and Nir Shavit. * Copyright 2007 Elsevier Inc. All rights reserved. */ package monitor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicStampedReference; /** * Non-Blocking Exchanger. * @author mph */ public class NBExchanger<T> { AtomicStampedReference<T> slot = new AtomicStampedReference<T>(null, 0); /** Creates a new instance of NBExchanger */ public NBExchanger() { } public T exchange(T x) throws InterruptedException { try { return doExchange(x, Integer.MAX_VALUE); } catch (TimeoutException cannotHappen) { throw new Error(cannotHappen); } } public T exchange(T x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { return doExchange(x, unit.toNanos(timeout)); } private T doExchange(T myItem, long nanos) throws TimeoutException { long timeBound = System.nanoTime() + nanos; int[] stampHolder = {0}; while (true) { if (System.nanoTime() > timeBound) throw new TimeoutException(); T yrItem = slot.get(stampHolder); int stamp = stampHolder[0]; switch(stamp % 3) { case 0: // slot is free if (slot.compareAndSet(yrItem, myItem, stamp, stamp + 1)) {// set stamp to 1 mod 3 while (System.nanoTime() < timeBound){ yrItem = slot.get(stampHolder); if (stampHolder[0] == stamp + 2) { // stamp is 2 mod 3: there was an exchange slot.set(null, stamp + 3); // increment stamp to 0 mod 3 return yrItem; // return other's myItem } } // timed out, try to reset stamp to 0 mod 3 if (slot.compareAndSet(myItem, null, stamp + 1, stamp)) { throw new TimeoutException(); } else { // someone arrived at last minute yrItem = slot.get(stampHolder); slot.set(null, stamp + 3); // increment stamp to 0 mod 3 return yrItem; // return myItem of other } } // CAS failed, retry. break; case 1: // someone waiting for me if (slot.compareAndSet(yrItem, myItem, stamp, stamp + 1)) return yrItem; break; case 2: // others in middle of exchanging break; default: // impossible break; } } } }