1 package org.sentrysoftware.jawk.jrt; 2 3 /*- 4 * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲ 5 * Jawk 6 * ჻჻჻჻჻჻ 7 * Copyright (C) 2006 - 2023 Sentry Software 8 * ჻჻჻჻჻჻ 9 * This program is free software: you can redistribute it and/or modify 10 * it under the terms of the GNU Lesser General Public License as 11 * published by the Free Software Foundation, either version 3 of the 12 * License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Lesser Public License for more details. 18 * 19 * You should have received a copy of the GNU General Lesser Public 20 * License along with this program. If not, see 21 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 22 * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱ 23 */ 24 25 import java.util.LinkedList; 26 import java.util.List; 27 28 import org.sentrysoftware.jawk.util.AwkLogger; 29 import org.slf4j.Logger; 30 31 /** 32 * Manages multiple blocking code segments simultaneously such that 33 * unblocking one block condition releases the block of all other 34 * block code segments. 35 * 36 * @see BlockObject 37 * @author Danny Daglas 38 */ 39 public class BlockManager { 40 41 private static final Logger LOG = AwkLogger.getLogger(BlockManager.class); 42 43 private final Object notifierLock = "NOTIFIER_LOCK"; 44 private String notifier = null; 45 46 /** 47 * Executes all block segments simultaneously, waiting for 48 * one block release. 49 * <p> 50 * The algorithm is as follows: 51 * <ul> 52 * <li>Collect linked block objects into a List. 53 * <li>Spawn a BlockThread for each block object. 54 * <li>Wait for notification from any of the BlockThreads. 55 * <li>Interrupt remaining block threads. 56 * <li>Wait for each BlockThread to die. 57 * <li>Return the block object notifier which satisfied their block condition. 58 * </ul> 59 * <p> 60 * And, the BlockThread algorithm is as follows: 61 * 62 * <ul> 63 * <li>try, catch for InterruptedException ... 64 * <ul> 65 * <li>Execute the BlockObject block segment. 66 * <li>Assign the notifier from this BlockObject 67 * if one isn't already assigned (to mitigate 68 * a race condition). 69 * <li>Notify the BlockManager. 70 * </ul> 71 * <li>If interrupted, do nothing and return. 72 * </ul> 73 * 74 * @param bo BlockObject to employ. Other block objects 75 * may be linked to this block object. In this event, 76 * employ all block objects simultaneously. 77 * @return a {@link java.lang.String} object 78 */ 79 public String block(BlockObject bo) { 80 // get all block objects 81 List<BlockObject> bos = bo.getBlockObjects(); 82 // each block object contains a wait statement 83 // (either indefinite or timed) 84 85 // for each block object 86 // spawn a thread (preferably using a threadpool) 87 // do the wait 88 // signal a break in the block 89 // interrupt all other threads, resulting in InterruptedExceptions 90 91 List<Thread> threadList = new LinkedList<Thread>(); 92 synchronized (BlockManager.this) { 93 for (BlockObject blockobj : bos) { 94 // spawn a thread 95 Thread t = new BlockThread(blockobj); 96 t.start(); 97 threadList.add(t); 98 } 99 100 // now, wait for notification from one of the BlockThreads 101 try { 102 BlockManager.this.wait(); 103 } catch (InterruptedException ie) {} 104 } 105 106 // block successful, interrupt other blockers 107 // and wait for thread deaths 108 for (Thread t : threadList) { 109 t.interrupt(); 110 try { 111 t.join(); 112 } catch (InterruptedException ie) {} 113 } 114 115 // return who was the notifier 116 assert notifier != null; 117 return notifier; 118 } 119 120 private final class BlockThread extends Thread { 121 122 private BlockObject bo; 123 124 private BlockThread(BlockObject bo) { 125 setName("BlockThread for " + bo.getNotifierTag()); 126 this.bo = bo; 127 } 128 129 @Override 130 public void run() { 131 try { 132 bo.block(); 133 synchronized (notifierLock) { 134 if (notifier == null) { 135 notifier = bo.getNotifierTag(); 136 } 137 } 138 synchronized (BlockManager.this) { 139 BlockManager.this.notify(); 140 } 141 } catch (InterruptedException ie) { 142 } catch (RuntimeException re) { 143 LOG.error("exitting", re); 144 System.exit(1); 145 } 146 } 147 } 148 }