├── Makefile ├── Run.java ├── DieThread.java ├── ThreadID.java ├── IntVar.java ├── AtomRef.java ├── GuardTask.java ├── Guard.java └── Main.java /Makefile: -------------------------------------------------------------------------------- 1 | all : Guard.class 2 | bash ./x.sh 3 | #java -ea Main 4 | 5 | Guard.class : *.java 6 | javac *.java 7 | 8 | clean : 9 | rm -f *.class 10 | -------------------------------------------------------------------------------- /Run.java: -------------------------------------------------------------------------------- 1 | import java.util.TreeSet; 2 | 3 | public class Run { 4 | public static void run(Runnable r) { 5 | try { 6 | r.run(); 7 | } catch(Throwable t) { 8 | t.printStackTrace(); 9 | System.exit(2); 10 | } finally { 11 | ; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DieThread.java: -------------------------------------------------------------------------------- 1 | public class DieThread extends Thread { 2 | public DieThread(Runnable r) { 3 | super(()->{ 4 | try { 5 | r.run(); 6 | } catch(Throwable t) { 7 | t.printStackTrace(); 8 | System.exit(4); 9 | } 10 | }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ThreadID.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicInteger; 2 | 3 | public class ThreadID { 4 | final static AtomicInteger nextId = new AtomicInteger(0); 5 | final static ThreadLocal id = new ThreadLocal<>() { 6 | public Integer initialValue() { 7 | return nextId.getAndIncrement(); 8 | } 9 | }; 10 | public static int get() { 11 | return id.get(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /IntVar.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.ThreadLocalRandom; 2 | 3 | public class IntVar { 4 | volatile int id; 5 | volatile int val; 6 | public IntVar() {} 7 | public IntVar(int n) { val = n; } 8 | synchronized int _get() { 9 | id = ThreadID.get(); 10 | return val; 11 | } 12 | public int get() { 13 | int r = _get(); 14 | try { 15 | int sl = ThreadLocalRandom.current().nextInt(3); 16 | Thread.sleep(sl); 17 | } catch(InterruptedException ie) {} 18 | return r; 19 | } 20 | public synchronized void set(int val) { 21 | int nid = ThreadID.get(); 22 | assert nid == id; 23 | this.val = val; 24 | } 25 | public void incr() { 26 | int v = get(); 27 | set(v+1); 28 | } 29 | public String toString() { return ""+val+"{"+id+"}"; } 30 | } 31 | -------------------------------------------------------------------------------- /AtomRef.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicInteger; 2 | public class AtomRef { 3 | final static AtomicInteger nextId = new AtomicInteger(0); 4 | final int id = nextId.getAndIncrement(); 5 | private T data; 6 | private int count = 0; 7 | public synchronized T getAndSet(T t) { 8 | T tmp = data; 9 | data = t; 10 | return tmp; 11 | } 12 | public synchronized boolean compareAndSet(T oldval,T newval) { 13 | if(count == 0) assert data == null; 14 | if(count == 1) assert data != null; 15 | count++; 16 | assert count <= 2 : "Only 2 violated"; 17 | if(data == oldval) { 18 | data = newval; 19 | return true; 20 | } else { 21 | return false; 22 | } 23 | } 24 | public String toString() { 25 | return "AtomRef("+id+","+(null==data)+")"; 26 | } 27 | public synchronized T get() { 28 | return data; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /GuardTask.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicReference; 2 | import java.util.concurrent.atomic.AtomicBoolean; 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | import java.util.TreeSet; 5 | import java.util.Set; 6 | 7 | public class GuardTask { 8 | final static AtomicInteger nextId = new AtomicInteger(-1); 9 | final int id = nextId.getAndIncrement(); 10 | public String toString() { return "gt["+id+","+guard+","+next+"]"; } 11 | final Guard guard; 12 | final static GuardTask DONE = new GuardTask(null,()->{ 13 | assert false : "DONE should not be executed"; 14 | },null); 15 | final AtomicReference next = new AtomicReference<>(); 16 | 17 | final TreeSet guards_held; 18 | 19 | final static ThreadLocal> GUARDS_HELD = new ThreadLocal<>(); 20 | 21 | private Runnable r; 22 | final boolean cleanup; 23 | public GuardTask(Guard g,TreeSet guards_held) { 24 | guard = g; 25 | cleanup = false; 26 | this.guards_held = guards_held; 27 | } 28 | public GuardTask(Guard g,Runnable r,TreeSet guards_held) { 29 | guard = g; 30 | this.r = r; 31 | cleanup = true; 32 | this.guards_held = guards_held; 33 | } 34 | public void setRun(Runnable r) { 35 | assert this.r == null; 36 | this.r = r; 37 | } 38 | private void run_() { 39 | int id = ThreadID.get(); 40 | assert guard.locked.compareAndSet(false,true) : String.format("%s %d %d",this,ThreadID.get(),guards_held.size()); 41 | if(cleanup) { 42 | GUARDS_HELD.set(guards_held); 43 | for(Guard g : guards_held) 44 | assert g.locked.get(); 45 | } 46 | Run.run(r); 47 | } 48 | public void run() { 49 | run_(); 50 | if(cleanup) 51 | endRun_(); 52 | } 53 | public void endRun() { 54 | assert !cleanup : "Manual cleanup on GuardTask set to auto clean"; 55 | endRun_(); 56 | } 57 | private void endRun_() { 58 | assert guard.locked.compareAndSet(true,false); 59 | var n = next; 60 | while(!n.compareAndSet(null,DONE)) { 61 | final GuardTask gt = n.get(); 62 | gt.run_(); 63 | if(!gt.cleanup) return; 64 | assert gt.guard.locked.compareAndSet(true,false); 65 | n = gt.next; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Guard.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicReference; 2 | import java.util.concurrent.atomic.AtomicBoolean; 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | import java.util.TreeSet; 5 | import java.util.List; 6 | import java.util.ArrayList; 7 | 8 | public class Guard implements Comparable { 9 | final static AtomicInteger nextId = new AtomicInteger(0); 10 | final AtomicBoolean locked = new AtomicBoolean(false); 11 | final int id = nextId.getAndIncrement(); 12 | public String toString() { return "g["+id+","+(locked.get() ? "T":"F")+"]"; } 13 | public int compareTo(Guard g) { 14 | return this.id - g.id; 15 | } 16 | final AtomRef next = new AtomRef<>(); 17 | 18 | public void runGuarded(Runnable r) { 19 | TreeSet guards_held = new TreeSet<>(); 20 | guards_held.add(this); 21 | runGuarded_(r,guards_held); 22 | } 23 | 24 | private void runGuarded_(Runnable r,TreeSet guards_held) { 25 | GuardTask gtask = new GuardTask(this,r,guards_held); 26 | assert gtask.cleanup; 27 | var prev = next.getAndSet(gtask); 28 | if(prev == null) { 29 | gtask.run(); 30 | } else { 31 | if(!prev.next.compareAndSet(null,gtask)) { 32 | gtask.run(); 33 | } 34 | } 35 | } 36 | 37 | private void startGuarded(Runnable r,TreeSet guards_held) { 38 | GuardTask gtask = new GuardTask(this, guards_held); 39 | gtask.setRun(r); 40 | startGuarded(gtask); 41 | } 42 | 43 | private void startGuarded(GuardTask gtask) { 44 | //System.out.println("startGuarded: "+gtask); 45 | assert !gtask.cleanup; 46 | var prev = next.getAndSet(gtask); 47 | if(prev == null) { 48 | gtask.run(); 49 | } else { 50 | if(!prev.next.compareAndSet(null,gtask)) { 51 | gtask.run(); 52 | } 53 | } 54 | } 55 | 56 | public static void runGuarded(Runnable r,final Guard g1) { 57 | g1.runGuarded(r); 58 | } 59 | 60 | // This special case is not strictly necessry 61 | public static void runGuarded(Runnable r,final Guard g1,final Guard g2) { 62 | if(g1.compareTo(g2) > 0) { 63 | runGuarded(r,g2,g1); 64 | } else { 65 | assert g1.compareTo(g2) < 0 : "Not sorted"; 66 | TreeSet gh = new TreeSet<>(); 67 | gh.add(g1); 68 | gh.add(g2); 69 | final GuardTask gtask1 = new GuardTask(g1,gh); 70 | gtask1.setRun(()->{ 71 | g2.runGuarded_(()->{ 72 | Run.run(r); 73 | gtask1.endRun(); 74 | },gh); 75 | }); 76 | g1.startGuarded(gtask1); 77 | } 78 | } 79 | 80 | // This special case is not strictly necessry 81 | /* 82 | public static void runGuarded(Runnable r,final Guard g1,final Guard g2,final Guard g3) { 83 | if(g1.compareTo(g2) > 0) { 84 | runGuarded(r,g2,g1,g3); 85 | } else if(g1.compareTo(g3) > 0) { 86 | runGuarded(r,g3,g2,g1); 87 | } else if(g2.compareTo(g3) > 0) { 88 | runGuarded(r,g1,g3,g2); 89 | } else { 90 | assert g1.compareTo(g2) < 0; 91 | assert g2.compareTo(g3) < 0; 92 | TreeSet gh = new TreeSet<>(); 93 | gh.add(g1); 94 | gh.add(g2); 95 | gh.add(g3); 96 | final GuardTask gtask1 = new GuardTask(g1,gh); 97 | final GuardTask gtask2 = new GuardTask(g2,gh); 98 | gtask2.setRun(()->{ 99 | g3.runGuarded_(()->{ 100 | Run.run(r); 101 | gtask2.endRun(); 102 | gtask1.endRun(); 103 | },gh); 104 | }); 105 | gtask1.setRun(()->{ 106 | g2.startGuarded(gtask2); 107 | }); 108 | g1.startGuarded(gtask1); 109 | } 110 | } 111 | */ 112 | 113 | /* 114 | public static void runGuarded(Runnable r,Guard g1_,Guard g2_,Guard g3_,Guard g4_) { 115 | if(g1_.compareTo(g2_) > 0) { var t = g1_; g1_ = g2_; g2_ = t; } 116 | if(g1_.compareTo(g3_) > 0) { var t = g1_; g1_ = g3_; g3_ = t; } 117 | if(g1_.compareTo(g4_) > 0) { var t = g1_; g1_ = g4_; g4_ = t; } 118 | if(g2_.compareTo(g3_) > 0) { var t = g2_; g2_ = g3_; g3_ = t; } 119 | if(g2_.compareTo(g4_) > 0) { var t = g2_; g2_ = g4_; g4_ = t; } 120 | if(g3_.compareTo(g4_) > 0) { var t = g3_; g3_ = g4_; g4_ = t; } 121 | assert g1_.compareTo(g2_) < 0; 122 | assert g2_.compareTo(g3_) < 0; 123 | assert g3_.compareTo(g4_) < 0; 124 | final Guard g1 = g1_; 125 | final Guard g2 = g2_; 126 | final Guard g3 = g3_; 127 | final Guard g4 = g4_; 128 | TreeSet gh = new TreeSet<>(); 129 | gh.add(g1); 130 | gh.add(g2); 131 | gh.add(g3); 132 | gh.add(g4); 133 | final GuardTask gtask1 = new GuardTask(g1,gh); 134 | final GuardTask gtask2 = new GuardTask(g2,gh); 135 | final GuardTask gtask3 = new GuardTask(g3,gh); 136 | gtask3.setRun(()->{ 137 | g4.runGuarded_(()->{ 138 | Run.run(r); 139 | gtask3.endRun(); 140 | gtask2.endRun(); 141 | gtask1.endRun(); 142 | },gh); 143 | }); 144 | gtask2.setRun(()->{ 145 | g3.startGuarded(gtask3); 146 | }); 147 | gtask1.setRun(()->{ 148 | g2.startGuarded(gtask2); 149 | }); 150 | g1.startGuarded(gtask1); 151 | } 152 | */ 153 | 154 | // Four or more guards 155 | public static void runGuarded(Runnable r,final Guard... garray) { 156 | if(garray.length==0) { 157 | Run.run(r); 158 | } else if(garray.length == 1) { 159 | garray[0].runGuarded(r); 160 | } else { 161 | TreeSet ts = new TreeSet<>(); 162 | for(Guard g : garray) 163 | ts.add(g); 164 | runGuarded(r,ts); 165 | } 166 | } 167 | 168 | public static void runGuarded(Runnable r,TreeSet ts) { 169 | assert ts.size() > 1; 170 | 171 | List lig = new ArrayList<>(); 172 | lig.addAll(ts); 173 | 174 | assert lig.size() == ts.size(); 175 | 176 | TreeSet guards_held = new TreeSet<>(); 177 | guards_held.addAll(ts); 178 | 179 | List ligt = new ArrayList<>(); 180 | for(int i=0;i{ 187 | // set up the last task 188 | lig.get(last).runGuarded_(()->{ 189 | Run.run(r); 190 | // last to run unlocks everything 191 | for(int i=0;i{ 204 | guardNext.startGuarded(guardTaskNext); 205 | }); 206 | } 207 | // kick the whole thing off 208 | lig.get(0).startGuarded(ligt.get(0)); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /Main.java: -------------------------------------------------------------------------------- 1 | import java.util.concurrent.atomic.AtomicInteger; 2 | import java.util.concurrent.ThreadLocalRandom; 3 | import java.util.*; 4 | 5 | public class Main { 6 | final static int nmax = 3*20; 7 | final static Map intmap = new HashMap<>(); 8 | final static AtomicInteger testCount = new AtomicInteger(0); 9 | 10 | public static void finalTest(int num) { 11 | System.out.println("Done: "+num); 12 | int n = testCount.incrementAndGet(); 13 | assert n == 2*num || n == 2*num-1 : String.format("%d != %d",testCount.get(),num); 14 | } 15 | 16 | static Guard mkGuard() { 17 | Guard g = new Guard(); 18 | intmap.put(g.id, new IntVar()); 19 | return g; 20 | } 21 | static void incr(Guard g) { 22 | IntVar iv = intmap.get(g.id); 23 | assert GuardTask.GUARDS_HELD.get().contains(g); 24 | iv.incr(); 25 | } 26 | 27 | public static void main(String[] args) { 28 | try { 29 | main(); 30 | } catch(Throwable t) { 31 | t.printStackTrace(); 32 | System.exit(3); 33 | } 34 | } 35 | public static void main() throws Exception { 36 | Guard g = mkGuard(); 37 | g.runGuarded(()->{ System.out.println("Hello"); }); 38 | g.runGuarded(()->{ System.out.println("World"); }); 39 | Runnable w = ()->{ 40 | for(int i=0;i{ 43 | if(n == nmax-1) finalTest(1); 44 | incr(g); 45 | }); 46 | } 47 | }; 48 | 49 | // Test one guard 50 | { 51 | Thread t1 = new DieThread(w); 52 | Thread t2 = new DieThread(w); 53 | t1.start(); 54 | t2.start(); 55 | t1.join(); 56 | t2.join(); 57 | } 58 | System.out.println("Test of one guard complete"); 59 | 60 | Guard g2 = mkGuard(); 61 | 62 | Guard.runGuarded(()->{ System.out.println("Two"); },g,g2); 63 | 64 | Runnable w2=()->{ 65 | for(int i=0;i{ 104 | for(int i=0;i{ 107 | if(n == nmax-1) finalTest(3); 108 | if(n % 3 == 0) { 109 | incr(g); 110 | incr(g2); 111 | } else if(n % 3 == 1) { 112 | incr(g2); 113 | incr(g3); 114 | } else if(n % 3 == 2) { 115 | incr(g); 116 | incr(g2); 117 | incr(g3); 118 | } 119 | }; 120 | if(n % 3 == 0) 121 | Guard.runGuarded(r,g,g2); 122 | else if(n % 3 == 1) 123 | Guard.runGuarded(r,g2,g3); 124 | else if(n % 3 == 2) 125 | Guard.runGuarded(r,g,g2,g3); 126 | } 127 | }; 128 | 129 | // Test three guards 130 | { 131 | Thread t1 = new DieThread(w3); 132 | Thread t2 = new DieThread(w3); 133 | t1.start(); 134 | t2.start(); 135 | t1.join(); 136 | t2.join(); 137 | } 138 | System.out.println("Test of three guards complete"); 139 | 140 | Guard g4 = mkGuard(); 141 | Runnable w4=()->{ 142 | for(int i=0;i{ 145 | if(n % 3 == 0) { 146 | incr(g); incr(g2); incr(g3); 147 | } else if(n % 3 == 1) { 148 | incr(g2); incr(g3); incr(g4); 149 | } else if(n % 3 == 2) { 150 | incr(g); incr(g2); incr(g3); incr(g4); 151 | } 152 | if(n == nmax-1) finalTest(4); 153 | }; 154 | if(n % 3 == 0) 155 | Guard.runGuarded(r,g,g2,g3); 156 | else if(n % 3 == 1) 157 | Guard.runGuarded(r,g2,g3,g4); 158 | else if(n % 3 == 2) 159 | Guard.runGuarded(r,g,g2,g3,g4); 160 | } 161 | }; 162 | 163 | // Test four guards 164 | { 165 | Thread t1 = new DieThread(w4); 166 | Thread t2 = new DieThread(w4); 167 | t1.start(); 168 | t2.start(); 169 | t1.join(); 170 | t2.join(); 171 | } 172 | System.out.println("Test of four guards complete"); 173 | assert testCount.get() == 8; 174 | 175 | List liv = new ArrayList<>(); 176 | List livg = new ArrayList<>(); 177 | for(int i =0;i<60;i++) { 178 | liv.add(new IntVar(i)); 179 | livg.add(new Guard()); 180 | } 181 | AtomicInteger ai = new AtomicInteger(); 182 | Runnable w5 = ()->{ 183 | for(int i=0;i{ 192 | int tmp = i1.get(); 193 | i1.set(i2.get()); 194 | i2.set(tmp); 195 | ai.getAndIncrement(); 196 | }; 197 | Guard.runGuarded(r,gu1,gu2); 198 | } 199 | }; 200 | List threads = new ArrayList<>(); 201 | for(int i=0;i<5;i++) { 202 | threads.add(new DieThread(w5)); 203 | } 204 | for(Thread t : threads) t.start(); 205 | for(Thread t : threads) t.join(); 206 | while(ai.get() != nmax*threads.size()) 207 | Thread.sleep(1); 208 | System.out.println(liv); 209 | System.out.println("Done Test 5"); 210 | Set ints = new HashSet<>(); 211 | for(IntVar iv : liv) { 212 | int v = iv.get(); 213 | ints.add(v); 214 | } 215 | boolean found = false; 216 | for(int i=0;i