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 }