diff --git a/monitor-object/pom.xml b/monitor-object/pom.xml
new file mode 100644
index 000000000..011300547
--- /dev/null
+++ b/monitor-object/pom.xml
@@ -0,0 +1,24 @@
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.20.0-SNAPSHOT
+
+ monitor-object
+ monitor-object
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/AbstractMonitor.java b/monitor-object/src/main/java/com/iluwatar/monitor/AbstractMonitor.java
new file mode 100644
index 000000000..dd1b512e9
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/AbstractMonitor.java
@@ -0,0 +1,214 @@
+package com.iluwatar.monitor;
+
+import java.util.ArrayList;
+
+/**
+ * A class for Monitors. Monitors provide coordination of concurrent threads.
+ * Each Monitor protects some resource, usually data. At each point in time a
+ * monitor object is occupied by at most one thread.
+ */
+public abstract class AbstractMonitor {
+ final Semaphore entrance = new Semaphore(1);
+ volatile Thread occupant = null;
+ private final ArrayList listOfListeners = new ArrayList<>();
+ private final String name;
+
+ public String getName() {
+ return name;
+ }
+
+ protected AbstractMonitor() {
+ this(null);
+ }
+
+ protected AbstractMonitor(String name) {
+ this.name = name;
+ }
+
+ /**
+ * The invariant. The default implementation always returns true. This method
+ * should be overridden if at all possible with the strongest economically
+ * evaluable invariant.
+ */
+ protected boolean invariant() {
+ return true;
+ }
+
+ /**
+ * Enter the monitor. Any thread calling this method is delayed until the
+ * monitor is unoccupied. Upon returning from this method, the monitor is
+ * considered occupied. A thread must not attempt to enter a Monitor it is
+ * already in.
+ */
+ protected void enter() {
+ notifyCallEnter();
+ entrance.acquire();
+ // The following assertion should never trip!
+ Assertion.check(occupant == null, "2 threads in one monitor");
+ occupant = Thread.currentThread();
+ notifyReturnFromEnter();
+ Assertion.check(invariant(), "Invariant of monitor " + getName());
+ }
+
+ /**
+ * Leave the monitor. After returning from this method, the thread no longer
+ * occupies the monitor. Only a thread that is in the monitor may leave it.
+ *
+ * @throws AssertionError
+ * if the thread that leaves is not the occupant.
+ */
+ protected void leave() {
+ notifyLeaveMonitor();
+ leaveWithoutATrace();
+ }
+
+ /**
+ * Leave the monitor. After returning from this method, the thread no longer
+ * occupies the monitor. Only a thread that is in the monitor may leave it.
+ *
+ * @throws AssertionError
+ * if the thread that leaves is not the occupant.
+ */
+ protected T leave(T result) {
+ leave();
+ return result;
+ }
+
+ void leaveWithoutATrace() {
+ Assertion.check(invariant(), "Invariant of monitor " + getName());
+ Assertion.check(occupant == Thread.currentThread(), "Thread is not occupant");
+ occupant = null;
+ entrance.release();
+ }
+
+ /**
+ * Run the runnable inside the monitor. Any thread calling this method will be
+ * delayed until the monitor is empty. The "run" method of its argument is then
+ * executed within the protection of the monitor. When the run method returns,
+ * if the thread still occupies the monitor, it leaves the monitor.
+ *
+ * @param runnable
+ * A Runnable object.
+ */
+ protected void doWithin(Runnable runnable) {
+ enter();
+ try {
+ runnable.run();
+ } finally {
+ if (occupant == Thread.currentThread()) {
+ leave();
+ }
+ }
+ }
+
+ /**
+ * Run the runnable inside the monitor. Any thread calling this method will be
+ * delayed until the monitor is empty. The "run" method of its argument is then
+ * executed within the protection of the monitor. When the run method returns,
+ * if the thread still occupies the monitor, it leaves the monitor. Thus the
+ * signalAndLeave method may be called within the run method.
+ *
+ * @param runnable
+ * A RunnableWithResult object.
+ * @return The value computed by the run method of the runnable.
+ */
+ protected T doWithin(RunnableWithResult runnable) {
+ enter();
+ try {
+ return runnable.run();
+ } finally {
+ if (occupant == Thread.currentThread()) {
+ leave();
+ }
+ }
+ }
+
+ /**
+ * Create a condition queue associated with a checked Assertion. The Assertion
+ * will be checked prior to an signal of the condition.
+ */
+ protected Condition makeCondition(Assertion prop) {
+ return makeCondition(null, prop);
+ }
+
+ /**
+ * Create a condition queue with no associated checked Assertion.
+ */
+ protected Condition makeCondition() {
+ return makeCondition(null, TrueAssertion.singleton);
+ }
+
+ /**
+ * Create a condition queue associated with a checked Assertion. The Assertion
+ * will be checked prior to an signal of the condition.
+ */
+ protected Condition makeCondition(String name, Assertion prop) {
+ return new Condition(name, this, prop);
+ }
+
+ /**
+ * Create a condition queue with no associated checked Assertion.
+ */
+ protected Condition makeCondition(String name) {
+ return makeCondition(name, TrueAssertion.singleton);
+ }
+
+ /** Register a listener. */
+ public void addListener(MonitorListener newListener) {
+ listOfListeners.add(newListener);
+ }
+
+ private void notifyCallEnter() {
+ for (MonitorListener listener : listOfListeners) {
+ listener.callEnterMonitor(this);
+ }
+ }
+
+ private void notifyReturnFromEnter() {
+ for (MonitorListener listener : listOfListeners) {
+ listener.returnFromEnterMonitor(this);
+ }
+ }
+
+ private void notifyLeaveMonitor() {
+ for (MonitorListener listener : listOfListeners) {
+ listener.leaveMonitor(this);
+ }
+ }
+
+ void notifyCallAwait(Condition condition) {
+ for (MonitorListener listener : listOfListeners) {
+ listener.callAwait(condition, this);
+ }
+ }
+
+ void notifyReturnFromAwait(Condition condition) {
+ for (MonitorListener listener : listOfListeners) {
+ listener.returnFromAwait(condition, this);
+ }
+ }
+
+ void notifySignallerAwakesAwaitingThread(Condition condition) {
+ for (MonitorListener listener : listOfListeners) {
+ listener.signallerAwakesAwaitingThread(condition, this);
+ }
+ }
+
+ void notifySignallerLeavesTemporarily(Condition condition) {
+ for (MonitorListener listener : listOfListeners) {
+ listener.signallerLeavesTemporarily(condition, this);
+ }
+ }
+
+ void notifySignallerReenters(Condition condition) {
+ for (MonitorListener listener : listOfListeners) {
+ listener.signallerReenters(condition, this);
+ }
+ }
+
+ void notifySignallerLeavesMonitor(Condition condition) {
+ for (MonitorListener listener : listOfListeners) {
+ listener.signallerLeavesMonitor(condition, this);
+ }
+ }
+}
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/Assertion.java b/monitor-object/src/main/java/com/iluwatar/monitor/Assertion.java
new file mode 100644
index 000000000..b77586c10
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/Assertion.java
@@ -0,0 +1 @@
+package com.iluwatar.monitor;
/**
* Assertions that may be checked from time to time.
*/
public abstract class Assertion {
private static final String DEFAULTMESSAGE = "Assertion Failure";
protected String message = DEFAULTMESSAGE;
/** This method says whether the assertion is true. */
public abstract boolean isTrue();
/** Throw an AssertionError if the assertion is not true. */
public void check() {
check(isTrue(), message);
}
/** Throw an AssertionError if the parameter is not true. */
public static void check(boolean b) {
check(b, DEFAULTMESSAGE);
}
/** Throw an AssertionError if the boolean parameter is not true. */
public static void check(boolean b, String message) {
if (!b) {
throw new AssertionError(message);
}
}
}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/Condition.java b/monitor-object/src/main/java/com/iluwatar/monitor/Condition.java
new file mode 100644
index 000000000..be104770f
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/Condition.java
@@ -0,0 +1 @@
+package com.iluwatar.monitor;
/**
* A condition queue.
* Uses the signal and wait discipline (SW) or signal and leave (SL).
* Each Condition object is associated with a single {@link Assertion} object
* and a single AbstractMonitor
object. To construct a condition
* use the makeCondition
methods from
* {@link AbstractMonitor}
or {@link Monitor}
* Threads can wait for the assertion represented by the Assertion object to
* become true by calling method await()
. Threads can indicate that
* the assertion has become true by calling either method signal()
* (SW) or signalAndLeave()
(SL). All these methods check the
* assertion and the monitor's invariant as appropriate.
* Threads which wait on a Condition may supply a priority. In the absence of
* priority, waiting is fair -- in fact first-in last-out (FIFO).
* Each of the await()
, signal()
, and
* signalAndLeave()
methods have corresponding conditional
* versions, which first check the assertion before awaiting or signalling.
* These are: conditionalAwait()
, conditionalSignal()
,
* and conditionalSignalAndLeave()
.
* Conditions also support a {@link #count} accessor to determine the number of
* threads waiting on the condition.
* Condition objects are intended to be used only by the thread which occupies
* the monitor which created them.
*/
public class Condition {
private final AbstractMonitor homeMonitor;
private final Assertion assertion;
private final Semaphore queue;
private volatile int count;
private final String name;
Condition(String name, AbstractMonitor homeMonitor, Assertion assertion) {
this.name = name;
this.homeMonitor = homeMonitor;
this.assertion = assertion;
this.queue = new Semaphore(0);
this.count = 0;
}
public String getName() {
return name;
}
/**
* Just like await, but with a priority. Threads awaiting with a lesser priority
* value are re-admitted to the monitor in preference to threads awaiting with a
* greater priority value. When priority values are the same, the order is FIFO.
*
* @param priority
* Lower value means more urgent.
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if the invariant is not true to start
* @throws AssertionError
* if the assertion is not true on return
* @see #await()
*/
public void await(int priority) {
homeMonitor.notifyCallAwait(this);
Assertion.check(homeMonitor.occupant == Thread.currentThread(), "Thread is not occupant");
count += 1;
Assertion.check(homeMonitor.invariant(), "Invariant of monitor " + homeMonitor.getName());
homeMonitor.occupant = null;
homeMonitor.entrance.release();
queue.acquire(priority);
count -= 1;
homeMonitor.occupant = Thread.currentThread();
// It's not clear that the following check is needed anymore,
// as there is now a check made on the signal.
assertion.check();
homeMonitor.notifyReturnFromAwait(this);
}
/**
* Wait until a condition is signalled. The thread waits outside the monitor
* until the condition is signalled.
* Precondition: Increasing the count by 1 must make the invariant true. This
* thread is in the monitor.
* Postcondition: The assertion associated with this condition queue. This
* thread is in the monitor.
* Note: threads are queued in a FIFO manner unless a priority is used;
* cond.await() is equivalent to cond.await( Integer.MAX_VALUE
* ).
*
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if the invariant is not true to start
* @throws AssertionError
* if the assertion is not true on return
*/
public void await() {
await(Integer.MAX_VALUE);
}
/**
* Wait only if the condition is not already true.
*
* @throws AssertionError
* if neither the invariant nor the assertion associated with this
* object is true
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if the assertion is not true on return
* @see #await()
*/
public void conditionalAwait() {
conditionalAwait(Integer.MAX_VALUE);
}
/**
* Just like conditionalAwait, but with a priority.
*
* @param priority
* Lower value means more urgent.
* @throws AssertionError
* if neither the invariant nor the assertion associated with this
* object is true
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if the assertion is not true on return
* @see #conditionalAwait()
* @see #await( int priority )
*
*/
public void conditionalAwait(int priority) {
if (!assertion.isTrue()) {
await(priority);
}
}
/**
* Signal this condition if there is a waiting thread.
* Allows one thread that was waiting on the condition to reenter the monitor.
* Consequently the signalling thread waits outside. The signalling thread is
* allowed back into the monitor, once the monitor is again unoccupied. Threads
* which have signalled wait with a higher than normal priority and thus are
* allowed in ahead of other threads that are waiting to enter the monitor
* (e.g., those waiting in {@link AbstractMonitor#enter()}).
* If there is no waiting thread, then this is a no-op, but the invariant is
* still checked, as it is a postcondition.
* Preconditions:
*
* - If isEmpty(), the monitor's invariant must be true.
* - If not isEmpty(), then decreasing count() by 1 must make the proposition
* associated with this condition true.
* - This thread is in the monitor.
*
* Postcondition:
*
* - The monitor's invariant.
*
- This thread is in the monitor.
*
*
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if there is a waiting thread and the assertion is false (after
* decreasing the count by 1).
* @throws AssertionError
* if invariant is false on return.
*
*/
public void signal() {
Assertion.check(homeMonitor.occupant == Thread.currentThread(), "Thread is not occupant");
if (count > 0) {
try {
count -= 1;
assertion.check();
} finally {
count += 1;
}
homeMonitor.notifySignallerLeavesTemporarily(this);
homeMonitor.notifySignallerAwakesAwaitingThread(this);
homeMonitor.occupant = null;
queue.release();
homeMonitor.entrance.acquire(0); // Priority 0 puts the signaller ahead of others.
homeMonitor.occupant = Thread.currentThread();
homeMonitor.notifySignallerReenters(this);
}
Assertion.check(homeMonitor.invariant(), "Invariant of monitor " + homeMonitor.getName());
}
/**
* Allows one thread which was waiting on the condition to reenter the monitor.
* This thread (the one calling signalAndLeave) leaves the monitor immediately.
* Preconditions:
*
* - If isEmpty(), the monitor's invariant must be true.
* - If not isEmpty(), then decreasing count() by 1 must make the proposition
* associated with this condition true.
* - This thread is in the monitor.
*
* Postcondition: This thread is not in the monitor.
*
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if there is a waiting thread and the assertion is false (after
* decreasing the count by 1).
* @throws AssertionError
* if there is no waiting thread and the invariant is false.
* @see #signal()
*/
public void signalAndLeave() {
Assertion.check(homeMonitor.occupant == Thread.currentThread(), "Thread is not occupant");
if (count > 0) {
try {
count -= 1;
assertion.check();
} finally {
count += 1;
}
homeMonitor.notifySignallerAwakesAwaitingThread(this);
homeMonitor.occupant = null;
queue.release();
} else {
Assertion.check(homeMonitor.invariant(), "Invariant of monitor " + homeMonitor.getName());
homeMonitor.occupant = null;
homeMonitor.entrance.release();
}
homeMonitor.notifySignallerLeavesMonitor(this);
}
/**
* Signal if there is a waiting thread, then leave the monitor. Allows one
* thread which was waiting on the condition to reenter the monitor. This thread
* (the one calling signalAndLeave) leaves the monitor immediately.
* Preconditions:
*
* - If isEmpty(), the monitor's invariant must be true.
* - If not isEmpty(), then decreasing count() by 1 must make the proposition
* associated with this condition true.
* - This thread is in the monitor.
*
* Postcondition: This thread is not in the monitor.
*
* @param result
* A value to return.
* @return The value of the result parameter.
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if there is a waiting thread and the assertion is false (after
* decreasing the count by 1).
* @throws AssertionError
* there is no waiting thread and the invariant is false.
*/
public T signalAndLeave(T result) {
signalAndLeave();
return result;
}
/**
* Signal this condition if its assertion is true and there is a waiting thread.
* More precisely the condition is only signalled if its assertion would be true
* after the count is decreased by 1 and there is a waiting tread.
* In such a case, the signalling thread waits outside. The signalling thread is
* allowed back into the monitor, once the monitor is again unoccupied. Threads
* which have signalled wait with a higher than normal priority and thus are
* allowed in ahead of other threads that are waiting to enter the monitor
* (e.g., those waiting in {@link AbstractMonitor#enter()}).
* If the there are no awaiting threads, or the condition's assertion would not
* be true after the count were decreased by one, this method is essentially a
* no-op, although the invariant is still checked in such a case.
* Preconditions:
*
* - If isEmpty(), the monitor's invariant must be true.
* - This thread is in the monitor.
*
* Postcondition:
*
* - The monitor's invariant.
*
- This thread is in the monitor.
*
*
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* if invariant is false on return.
* @see #signal()
*/
public void conditionalSignal() {
Assertion.check(homeMonitor.occupant == Thread.currentThread(), "Thread is not occupant");
if (count > 0) {
boolean wouldBeTrue;
count -= 1;
wouldBeTrue = assertion.isTrue();
count += 1;
if (wouldBeTrue) {
homeMonitor.notifySignallerAwakesAwaitingThread(this);
homeMonitor.notifySignallerLeavesTemporarily(this);
homeMonitor.occupant = null;
queue.release();
homeMonitor.entrance.acquire();
homeMonitor.occupant = Thread.currentThread();
homeMonitor.notifySignallerReenters(this);
}
}
Assertion.check(homeMonitor.invariant(), "Invariant of monitor " + homeMonitor.getName());
}
/**
* Signal this condition if its assertion is true and there is a waiting thread;
* leave regardless.
* More precisely the condition is only signalled if the assertion would be true
* after the count is decreased by 1 and there is a waiting thread.
* This thread (the one calling signalAndLeave) leaves the monitor immediately.
* Preconditions:
*
* - If isEmpty() or the assertion would be false after decreasing the count
* by 1, the monitor's invariant must be true.
* - This thread is in the monitor.
*
* Postcondition:
*
* - This thread is not in the monitor.
*
*
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* there is no waiting thread and the invariant is false.
* @throws AssertionError
* if there is a waiting thread and the assertion is false (after
* decreasing the count by 1) and the invariant is false.
* @see #signalAndLeave()
* @see #conditionalSignal()
*
*/
public void conditionalSignalAndLeave() {
Assertion.check(homeMonitor.occupant == Thread.currentThread(), "Thread is not occupant");
if (count > 0) {
boolean wouldBeTrue;
count -= 1;
wouldBeTrue = assertion.isTrue();
count += 1;
if (wouldBeTrue) {
homeMonitor.notifySignallerAwakesAwaitingThread(this);
homeMonitor.notifySignallerLeavesMonitor(this);
homeMonitor.occupant = null;
queue.release();
} else {
homeMonitor.notifySignallerLeavesMonitor(this);
homeMonitor.leaveWithoutATrace();
}
} else {
homeMonitor.notifySignallerLeavesMonitor(this);
homeMonitor.leaveWithoutATrace();
}
}
/**
* Signal this condition if its assertion is true and there is a waiting thread.
* Leave regardless. More precisely the condition is only signalled if the
* assertion would be true after the count is decreased by 1.
* This thread (the one calling signalAndLeave) leaves the monitor immediately.
* Preconditions:
*
* - If isEmpty() or the assertion would be false after decreasing the count
* by 1, the monitor's invariant must be true.
* - This thread is in the monitor.
*
* Postcondition:
*
* - This thread is not in the monitor.
*
*
* @param result
* A value to be returned.
* @return The value of the result parameter.
* @throws AssertionError
* if the current thread is not the occupant.
* @throws AssertionError
* there is no waiting thread and the invariant is false.
* @throws AssertionError
* if there is a waiting thread and the assertion is false (after
* decreasing the count by 1) and the invariant is false.
* @see #conditionalSignalAndLeave()
*
*/
public T conditionalSignalAndLeave(T result) {
conditionalSignalAndLeave();
return result;
}
/**
* Test if any thread is waiting on this condition.
*
* @return count() == 0 .
* @throws AssertionError
* if the current thread is not the occupant.
*/
public boolean isEmpty() {
Assertion.check(homeMonitor.occupant == Thread.currentThread(), "Thread is not occupant");
return count == 0;
}
/**
* How many threads are waiting on this condition.
*
* @return the number of Threads waiting on this condition.
* @throws AssertionError
* if the current thread is not the occupant.
*/
public int count() {
Assertion.check(homeMonitor.occupant == Thread.currentThread(), "Thread is not occupant");
return count;
}
}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/Monitor.java b/monitor-object/src/main/java/com/iluwatar/monitor/Monitor.java
new file mode 100644
index 000000000..bf2ed5915
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/Monitor.java
@@ -0,0 +1,77 @@
+package com.iluwatar.monitor;
+
+/**
+ * A final class for Monitors.
+ */
+
+public final class Monitor extends AbstractMonitor {
+
+ private Assertion invariant;
+
+ public Monitor() {
+ this(TrueAssertion.singleton);
+ }
+
+ public Monitor(Assertion invariant) {
+ this.invariant = invariant;
+ }
+
+ public Monitor(String name) {
+ this(name, TrueAssertion.singleton);
+ }
+
+ public Monitor(String name, Assertion invariant) {
+ super(name);
+ this.invariant = invariant;
+ }
+
+ @Override
+ public boolean invariant() {
+ return invariant.isTrue();
+ }
+
+ @Override
+ public void enter() {
+ super.enter();
+ }
+
+ @Override
+ public void leave() {
+ super.leave();
+ }
+
+ @Override
+ public T leave(T result) {
+ return super.leave(result);
+ }
+
+ @Override
+ public void doWithin(Runnable runnable) {
+ super.doWithin(runnable);
+ }
+
+ @Override
+ public T doWithin(RunnableWithResult runnable) {
+ return super.doWithin(runnable);
+ }
+
+ @Override
+ public Condition makeCondition() {
+ return super.makeCondition();
+ }
+
+ @Override
+ public Condition makeCondition(Assertion assertion) {
+ return super.makeCondition(assertion);
+ }
+
+ @Override
+ public Condition makeCondition(String name) {
+ return super.makeCondition(name);
+ }
+
+ @Override
+ public Condition makeCondition(String name, Assertion assertion) {
+ return super.makeCondition(name, assertion);
+ }
+}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/MonitorListener.java b/monitor-object/src/main/java/com/iluwatar/monitor/MonitorListener.java
new file mode 100644
index 000000000..120e9bf35
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/MonitorListener.java
@@ -0,0 +1,25 @@
+package com.iluwatar.monitor;
+
+public interface MonitorListener {
+
+ void nameThisThread(String name);
+
+ void callEnterMonitor(AbstractMonitor monitor);
+
+ void returnFromEnterMonitor(AbstractMonitor monitor);
+
+ void leaveMonitor(AbstractMonitor monitor);
+
+ void callAwait(Condition condition, AbstractMonitor monitor);
+
+ void returnFromAwait(Condition condition, AbstractMonitor monitor);
+
+ void signallerAwakesAwaitingThread(Condition condition, AbstractMonitor monitor);
+
+ void signallerLeavesTemporarily(Condition condition, AbstractMonitor monitor);
+
+ void signallerReenters(Condition condition, AbstractMonitor monitor);
+
+ void signallerLeavesMonitor(Condition condition, AbstractMonitor monitor);
+
+}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/RunnableWithResult.java b/monitor-object/src/main/java/com/iluwatar/monitor/RunnableWithResult.java
new file mode 100644
index 000000000..48b4ea06d
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/RunnableWithResult.java
@@ -0,0 +1,5 @@
+package com.iluwatar.monitor;
+
+public interface RunnableWithResult {
+ public T run();
+}
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/Semaphore.java b/monitor-object/src/main/java/com/iluwatar/monitor/Semaphore.java
new file mode 100644
index 000000000..fe45eac11
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/Semaphore.java
@@ -0,0 +1 @@
+package com.iluwatar.monitor;
import java.util.LinkedList;
import java.util.ListIterator;
/**
* A FIFO semaphore.
*/
public class Semaphore {
// Each queue element is a a single use semaphore
class QueueElement {
final int priority;
volatile boolean enabled = false;
QueueElement(int priority) {
this.priority = priority;
}
synchronized void acquire() {
while (!enabled) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException("Unexpected interruption of "
+ "thread in monitor.Semaphore.acquire");
}
}
}
synchronized void release() {
enabled = true;
notify();
}
}
volatile int s1;
final LinkedList queue = new LinkedList();
// Invariant. All elements on the queue are in an unenabled state.
/** Initialize the semaphore to a value greater or equal to 0. */
public Semaphore(int initialvalue) {
Assertion.check(initialvalue >= 0);
this.s1 = initialvalue;
}
/**
* The P operation. If two threads are blocked at the same time, they will be
* served in FIFO order. sem.acquire() is equivalent to acquire(
* Integer.MAX_VALUE ).
*/
public void acquire() {
acquire(Integer.MAX_VALUE);
}
/**
* The P operation with a priority.
*
* @param priority
* The larger the integer, the less urgent the priority. If two
* thread are waiting with equal priority, they will complete acquire
* in FIFO order.
*/
public void acquire(int priority) {
QueueElement mine;
synchronized (this) {
if (s1 > 0) {
--s1;
return;
}
mine = new QueueElement(priority);
if (priority == Integer.MAX_VALUE) {
queue.add(mine);
} else {
ListIterator it = queue.listIterator(0);
int i = 0;
while (it.hasNext()) {
QueueElement elem = it.next();
if (elem.priority > priority) {
break;
}
++i;
}
queue.add(i, mine);
}
}
mine.acquire();
}
/** The V operation. */
public synchronized void release() {
QueueElement first = queue.poll();
if (first != null) {
first.release();
} else {
++s1;
}
}
}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/TrueAssertion.java b/monitor-object/src/main/java/com/iluwatar/monitor/TrueAssertion.java
new file mode 100644
index 000000000..82d6bb0e5
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/TrueAssertion.java
@@ -0,0 +1 @@
+package com.iluwatar.monitor;
/**
* An assertion that is always true.
*/
public class TrueAssertion extends Assertion {
public boolean isTrue() {
return true;
}
public static final TrueAssertion singleton = new TrueAssertion();
}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/examples/Queue.java b/monitor-object/src/main/java/com/iluwatar/monitor/examples/Queue.java
new file mode 100644
index 000000000..18ecba2ff
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/examples/Queue.java
@@ -0,0 +1,66 @@
+package com.iluwatar.monitor.examples;
+
+import com.iluwatar.monitor.AbstractMonitor;
+import com.iluwatar.monitor.Condition;
+
+/**
+ * FIFO Queue implementation using {@link AbstractMonitor}.
+ */
+public class Queue extends AbstractMonitor {
+
+ private final int capacity = 10;
+
+ private final Object[] queue = new Object[capacity];
+
+ private volatile int count = 0;
+
+ private volatile int front = 0;
+
+ /** Awaiting ensures: count < capacity. */
+ private final Condition notFull = makeCondition();
+
+ /** Awaiting ensures: count > 0. */
+ private final Condition notEmpty = makeCondition();
+
+ /**
+ * Method to pop the front element from queue.
+ * @return the top most element
+ */
+ public Object fetch() {
+ enter();
+ if (!(count > 0)) {
+ notEmpty.await();
+ assert count > 0;
+ }
+ count--;
+ front = (front + 1) % capacity;
+ assert count < capacity;
+ notFull.signal();
+ leave();
+ Object value = queue[front];
+ return value;
+ }
+
+ /**
+ * Method to push an element in queue.
+ * @param value the element to be pushed
+ */
+ public void deposit(Object value) {
+ enter();
+ if (!(count < capacity)) {
+ notFull.await();
+ assert count < capacity;
+ }
+ queue[(front + count) % capacity] = value;
+ count++;
+ assert count > 0;
+ notEmpty.signal();
+ leave();
+ }
+
+ @Override
+ protected boolean invariant() {
+ return 0 <= count && count <= capacity && 0 <= front && front < capacity;
+ }
+
+}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/examples/VoteInterface.java b/monitor-object/src/main/java/com/iluwatar/monitor/examples/VoteInterface.java
new file mode 100644
index 000000000..54111ea54
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/examples/VoteInterface.java
@@ -0,0 +1,9 @@
+package com.iluwatar.monitor.examples;
+
+/**
+ * Monitor Example.
+ */
+
+public interface VoteInterface {
+ public boolean castVoteAndWaitForResult(boolean vote);
+}
\ No newline at end of file
diff --git a/monitor-object/src/main/java/com/iluwatar/monitor/examples/VoteMonitor.java b/monitor-object/src/main/java/com/iluwatar/monitor/examples/VoteMonitor.java
new file mode 100644
index 000000000..a58919e60
--- /dev/null
+++ b/monitor-object/src/main/java/com/iluwatar/monitor/examples/VoteMonitor.java
@@ -0,0 +1,63 @@
+package com.iluwatar.monitor.examples;
+
+import com.iluwatar.monitor.AbstractMonitor;
+import com.iluwatar.monitor.Assertion;
+import com.iluwatar.monitor.Condition;
+import com.iluwatar.monitor.RunnableWithResult;
+
+/**
+ * Monitor Example.
+ */
+
+public class VoteMonitor extends AbstractMonitor implements VoteInterface {
+
+ private final int totalVotes;
+ private int votesFor = 0;
+ private int votesAgainst = 0;
+
+ private Condition electionDone = makeCondition(new Assertion() {
+ @Override
+ public boolean isTrue() {
+ return votesFor + votesAgainst == totalVotes;
+ }
+ });
+
+ public VoteMonitor(int n) {
+ assert n > 0;
+ this.totalVotes = n;
+ }
+
+ @Override
+ protected boolean invariant() {
+ return 0 <= votesFor && 0 <= votesAgainst && votesFor + votesAgainst < totalVotes;
+ }
+
+ /**
+ * Method to cast a vote and wait for the result.
+ */
+ public boolean castVoteAndWaitForResult(final boolean vote) {
+ return doWithin(new RunnableWithResult() {
+ public Boolean run() {
+ if (vote) {
+ votesFor++;
+ } else {
+ votesAgainst++;
+ }
+
+ electionDone.conditionalAwait();
+
+ // Assert: votesFor+votesAgainst == N
+ boolean result = votesFor > votesAgainst;
+
+ if (!electionDone.isEmpty()) {
+ electionDone.signalAndLeave();
+ } else {
+ votesFor = votesAgainst = 0;
+ }
+ // At this point the thread could be occupying
+ // the monitor, or not!
+ return result;
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/monitor-object/src/test/java/com/iluwatar/monitor/AssertionTest.java b/monitor-object/src/test/java/com/iluwatar/monitor/AssertionTest.java
new file mode 100644
index 000000000..9f6a48372
--- /dev/null
+++ b/monitor-object/src/test/java/com/iluwatar/monitor/AssertionTest.java
@@ -0,0 +1,44 @@
+package com.iluwatar.monitor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test Cases for Assertion.
+ */
+public class AssertionTest {
+ int varX = 0;
+ int varY = 0;
+
+ @Test
+ public void testAssertionTrue() {
+ Assertion a = new MyAssertion();
+ assertEquals(true, a.isTrue());
+ }
+
+ @Test
+ public void testAssertionFalse() {
+ Assertion a = new MyAssertion();
+ a.check();
+ varX = 1;
+ assertEquals(false, a.isTrue());
+ }
+
+ @Test
+ public void testAssertionError() {
+ assertThrows(NullPointerException.class, () -> {
+ Assertion a = new MyAssertion();
+ a.check();
+ varX = 1;
+ a.check();
+ });
+ }
+
+ class MyAssertion extends Assertion {
+ public boolean isTrue() {
+ return varX == varY;
+ }
+ }
+}
\ No newline at end of file
diff --git a/monitor-object/src/test/java/com/iluwatar/monitor/ThreadSignalTest.java b/monitor-object/src/test/java/com/iluwatar/monitor/ThreadSignalTest.java
new file mode 100644
index 000000000..553f88ac5
--- /dev/null
+++ b/monitor-object/src/test/java/com/iluwatar/monitor/ThreadSignalTest.java
@@ -0,0 +1,43 @@
+package com.iluwatar.monitor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import com.iluwatar.monitor.examples.VoteInterface;
+import com.iluwatar.monitor.examples.VoteMonitor;
+
+/**
+ * Test Case for Thread Signaling.
+ */
+public class ThreadSignalTest {
+
+ private Voter v0;
+ private Voter v1;
+ private Voter v2;
+
+ /**
+ * Setup method for Test Case.
+ */
+ @BeforeAll
+ public void setUp() {
+ VoteInterface vm = new VoteMonitor(3);
+ v0 = new Voter0(vm);
+ v1 = new Voter1(vm);
+ v2 = new Voter2(vm);
+ }
+
+ @Test
+ public void testVotingResult() throws InterruptedException {
+ v0.start();
+ v1.start();
+ v2.start();
+ v0.join();
+ v1.join();
+ v2.join();
+ assertEquals("Passed", v0.getTestResult());
+ assertEquals("Passed", v1.getTestResult());
+ assertEquals("Passed", v2.getTestResult());
+ }
+}
\ No newline at end of file
diff --git a/monitor-object/src/test/java/com/iluwatar/monitor/Voter.java b/monitor-object/src/test/java/com/iluwatar/monitor/Voter.java
new file mode 100644
index 000000000..501405a00
--- /dev/null
+++ b/monitor-object/src/test/java/com/iluwatar/monitor/Voter.java
@@ -0,0 +1,40 @@
+package com.iluwatar.monitor;
+
+import com.iluwatar.monitor.examples.VoteInterface;
+
+abstract class Voter extends Thread {
+
+ private VoteInterface vm;
+
+ private String testResult;
+
+ public String getTestResult() {
+ return testResult;
+ }
+
+ public void setTestResult(String testResult) {
+ this.testResult = testResult;
+ }
+
+ Voter(VoteInterface vm) {
+ this.vm = vm;
+ }
+
+ public void run() {
+ for (int i = 0; i < 100; ++i) {
+ boolean vote = makeVote(i);
+ boolean consensus = vm.castVoteAndWaitForResult(vote);
+ boolean expected = i % 6 == 1 || i % 6 == 2 || i % 6 == 5;
+ if (expected != consensus) {
+ System.out.println("Failed");
+ setTestResult("Failed");
+ System.exit(1);
+ }
+ setTestResult("Passed");
+ System.out.println(i + ": " + consensus);
+ }
+ System.out.println("Done");
+ }
+
+ abstract boolean makeVote(int i);
+}
\ No newline at end of file
diff --git a/monitor-object/src/test/java/com/iluwatar/monitor/Voter0.java b/monitor-object/src/test/java/com/iluwatar/monitor/Voter0.java
new file mode 100644
index 000000000..60d2e312b
--- /dev/null
+++ b/monitor-object/src/test/java/com/iluwatar/monitor/Voter0.java
@@ -0,0 +1,13 @@
+package com.iluwatar.monitor;
+
+import com.iluwatar.monitor.examples.VoteInterface;
+
+class Voter0 extends Voter {
+ Voter0(VoteInterface vm) {
+ super(vm);
+ }
+
+ boolean makeVote(int i) {
+ return i % 2 == 1;
+ }
+}
\ No newline at end of file
diff --git a/monitor-object/src/test/java/com/iluwatar/monitor/Voter1.java b/monitor-object/src/test/java/com/iluwatar/monitor/Voter1.java
new file mode 100644
index 000000000..291e13745
--- /dev/null
+++ b/monitor-object/src/test/java/com/iluwatar/monitor/Voter1.java
@@ -0,0 +1,13 @@
+package com.iluwatar.monitor;
+
+import com.iluwatar.monitor.examples.VoteInterface;
+
+class Voter1 extends Voter {
+ Voter1(VoteInterface vm) {
+ super(vm);
+ }
+
+ boolean makeVote(int i) {
+ return i % 3 == 2;
+ }
+}
\ No newline at end of file
diff --git a/monitor-object/src/test/java/com/iluwatar/monitor/Voter2.java b/monitor-object/src/test/java/com/iluwatar/monitor/Voter2.java
new file mode 100644
index 000000000..c672b693e
--- /dev/null
+++ b/monitor-object/src/test/java/com/iluwatar/monitor/Voter2.java
@@ -0,0 +1,13 @@
+package com.iluwatar.monitor;
+
+import com.iluwatar.monitor.examples.VoteInterface;
+
+class Voter2 extends Voter {
+ Voter2(VoteInterface vm) {
+ super(vm);
+ }
+
+ boolean makeVote(int i) {
+ return i % 3 != 0;
+ }
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 741367b30..eb6575ea0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -162,6 +162,7 @@
trampoline
serverless
component-object
+ monitor-object