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