View Javadoc
1   package org.metricshub.jawk.jrt;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2025 MetricsHub
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  /**
29   * Manages multiple blocking code segments simultaneously such that
30   * unblocking one block condition releases the block of all other
31   * block code segments.
32   *
33   * @see BlockObject
34   * @author Danny Daglas
35   */
36  public class BlockManager {
37  
38  	private final Object notifierLock = "NOTIFIER_LOCK";
39  	private String notifier = null;
40  
41  	/**
42  	 * Executes all block segments simultaneously, waiting for
43  	 * one block release.
44  	 * <p>
45  	 * The algorithm is as follows:
46  	 * <ul>
47  	 * <li>Collect linked block objects into a List.
48  	 * <li>Spawn a BlockThread for each block object.
49  	 * <li>Wait for notification from any of the BlockThreads.
50  	 * <li>Interrupt remaining block threads.
51  	 * <li>Wait for each BlockThread to die.
52  	 * <li>Return the block object notifier which satisfied their block condition.
53  	 * </ul>
54  	 * <p>
55  	 * And, the BlockThread algorithm is as follows:
56  	 * <ul>
57  	 * <li>try, catch for InterruptedException ...
58  	 * <ul>
59  	 * <li>Execute the BlockObject block segment.
60  	 * <li>Assign the notifier from this BlockObject
61  	 * if one isn't already assigned (to mitigate
62  	 * a race condition).
63  	 * <li>Notify the BlockManager.
64  	 * </ul>
65  	 * <li>If interrupted, do nothing and return.
66  	 * </ul>
67  	 *
68  	 * @param bo BlockObject to employ. Other block objects
69  	 *        may be linked to this block object. In this event,
70  	 *        employ all block objects simultaneously.
71  	 * @return a {@link java.lang.String} object
72  	 */
73  	public String block(BlockObject bo) {
74  		// get all block objects
75  		List<BlockObject> bos = bo.getBlockObjects();
76  		// each block object contains a wait statement
77  		// (either indefinite or timed)
78  
79  		// for each block object
80  		// spawn a thread (preferably using a threadpool)
81  		// do the wait
82  		// signal a break in the block
83  		// interrupt all other threads, resulting in InterruptedExceptions
84  
85  		List<Thread> threadList = new LinkedList<Thread>();
86  		synchronized (this) {
87  			notifier = null;
88  			for (BlockObject blockobj : bos) {
89  				// spawn a thread
90  				Thread t = new BlockThread(blockobj);
91  				t.start();
92  				threadList.add(t);
93  			}
94  
95  			// now, wait for notification from one of the BlockThreads
96  			while (notifier == null) {
97  				try {
98  					this.wait();
99  				} catch (InterruptedException ie) {
100 					Thread.currentThread().interrupt();
101 				}
102 			}
103 		}
104 
105 		// block successful, interrupt other blockers
106 		// and wait for thread deaths
107 		for (Thread t : threadList) {
108 			t.interrupt();
109 			try {
110 				t.join();
111 			} catch (InterruptedException ie) {
112 				Thread.currentThread().interrupt();
113 			}
114 		}
115 
116 		// return who was the notifier
117 		assert notifier != null;
118 		return notifier;
119 	}
120 
121 	private final class BlockThread extends Thread {
122 
123 		private BlockObject bo;
124 
125 		private BlockThread(BlockObject bo) {
126 			setName("BlockThread for " + bo.getNotifierTag());
127 			this.bo = bo;
128 		}
129 
130 		@Override
131 		public void run() {
132 			try {
133 				bo.block();
134 				synchronized (notifierLock) {
135 					if (notifier == null) {
136 						notifier = bo.getNotifierTag();
137 					}
138 				}
139 				synchronized (BlockManager.this) {
140 					BlockManager.this.notify();
141 				}
142 			} catch (InterruptedException ie) {
143 				currentThread().interrupt();
144 			} catch (RuntimeException re) {
145 				throw re;
146 			}
147 		}
148 	}
149 }