View Javadoc
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 }