From b9cf360b29133c9d15b3a8cd5f4a6aecb0d94b0a Mon Sep 17 00:00:00 2001 From: Jake Hillion Date: Thu, 7 Nov 2019 22:31:15 +0000 Subject: [PATCH] Attempt 1 --- .../cam/jsh77/fjava/tick3/BankSimulator.java | 8 +- .../ac/cam/jsh77/fjava/tick3/QueueTest.java | 18 ++-- .../jsh77/fjava/tick3/SafeMessageQueue.java | 83 +++++++++++++++++++ .../jsh77/fjava/tick3/UnsafeMessageQueue.java | 57 ++++++++++--- 4 files changed, 145 insertions(+), 21 deletions(-) create mode 100644 src/uk/ac/cam/jsh77/fjava/tick3/SafeMessageQueue.java diff --git a/src/uk/ac/cam/jsh77/fjava/tick3/BankSimulator.java b/src/uk/ac/cam/jsh77/fjava/tick3/BankSimulator.java index 837cb82..112d968 100644 --- a/src/uk/ac/cam/jsh77/fjava/tick3/BankSimulator.java +++ b/src/uk/ac/cam/jsh77/fjava/tick3/BankSimulator.java @@ -36,8 +36,12 @@ public class BankSimulator { } public void transferTo(BankAccount b, int amount) { - balance -= amount; - b.balance += amount; + synchronized (this.acc > b.acc ? this : b) { + synchronized (this.acc > b.acc ? b : this) { + balance -= amount; + b.balance += amount; + } + } } } diff --git a/src/uk/ac/cam/jsh77/fjava/tick3/QueueTest.java b/src/uk/ac/cam/jsh77/fjava/tick3/QueueTest.java index c1ce6e6..07f1731 100644 --- a/src/uk/ac/cam/jsh77/fjava/tick3/QueueTest.java +++ b/src/uk/ac/cam/jsh77/fjava/tick3/QueueTest.java @@ -22,6 +22,7 @@ public class QueueTest { private int sent = 0; public void run() { + // Adds 50000 messages to the queue, each containing the string representation of the numbers counting up for (int i = 0; i < 50000; ++i) { q.put("" + i); sent++; @@ -65,15 +66,18 @@ public class QueueTest { } public void run() { - + // Start the requested number of consumer threads for (Consumer c : consumers) { c.start(); } + // Start the requested number of producer threads for (Producer p : producers) { p.start(); } + // Join each producer thread - this awaits completion of each thread by joining this thread to its execution + // This loops awaits the completion of all producers for (Producer p : producers) { try { p.join(); @@ -83,6 +87,7 @@ public class QueueTest { } q.put("EOF"); // terminate join at 10 secs since EOF marker may get lost + // perform the same waiting for each consumer for (Consumer c : consumers) { try { c.join(10000); @@ -91,6 +96,7 @@ public class QueueTest { } } + // Compare the total number of messages received to the total number sent int recv = 0; for (Consumer consumer : consumers) { recv += consumer.numberConsumed(); @@ -109,10 +115,10 @@ public class QueueTest { new QueueTest(new UnsafeMessageQueue(), 1, 3).run(); new QueueTest(new UnsafeMessageQueue(), 3, 3).run(); - // System.out.println("** SAFE ** "); - // new QueueTest(new SafeMessageQueue(), 1, 1).run(); - // new QueueTest(new SafeMessageQueue(), 3, 1).run(); - // new QueueTest(new SafeMessageQueue(), 1, 3).run(); - // new QueueTest(new SafeMessageQueue(), 3, 3).run(); + System.out.println("** SAFE ** "); + new QueueTest(new SafeMessageQueue(), 1, 1).run(); + new QueueTest(new SafeMessageQueue(), 3, 1).run(); + new QueueTest(new SafeMessageQueue(), 1, 3).run(); + new QueueTest(new SafeMessageQueue(), 3, 3).run(); } } diff --git a/src/uk/ac/cam/jsh77/fjava/tick3/SafeMessageQueue.java b/src/uk/ac/cam/jsh77/fjava/tick3/SafeMessageQueue.java new file mode 100644 index 0000000..39a54ec --- /dev/null +++ b/src/uk/ac/cam/jsh77/fjava/tick3/SafeMessageQueue.java @@ -0,0 +1,83 @@ +/* + * Copyright 2019 Andrew Rice , Alastair Beresford , J.S. Hillion + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.ac.cam.jsh77.fjava.tick3; + +public class SafeMessageQueue implements MessageQueue { + private Link first = null; + private Link last = null; + + public synchronized void put(T val) { + Link link = new Link<>(val); + if (first == null && last == null) { + first = last = link; + } else { + last.next = link; + last = link; + } + + this.notify(); + } + + public synchronized T take() { + while (first == null) { // use a loop to block thread until data is available + try { + this.wait(); + } catch (InterruptedException ie) { + // QUESTION: what causes this exception to be thrown? and what should + // you do with it ideally? + + /* + The exception is thrown when the thread is interrupted while blocking (sleeping here). + To correctly support interrupts, the Thread should exit cleanly. In this case, the best way to do that + is to return null. + */ + return null; + } + } + + Link oldFirst = first; + if (last == oldFirst) last = null; + first = oldFirst.next; + return oldFirst.val; + } + + @Override + public String toString() { + return "UnsafeMessageQueue{" + + "first=" + first + + ", last=" + last + + '}'; + } + + private static class Link { + L val; + Link next; + + Link(L val) { + this.val = val; + this.next = null; + } + + @Override + public String toString() { + return "Link{" + + "val=" + val + + ", next=" + next + + '}'; + } + } +} diff --git a/src/uk/ac/cam/jsh77/fjava/tick3/UnsafeMessageQueue.java b/src/uk/ac/cam/jsh77/fjava/tick3/UnsafeMessageQueue.java index ad1abc4..430f004 100644 --- a/src/uk/ac/cam/jsh77/fjava/tick3/UnsafeMessageQueue.java +++ b/src/uk/ac/cam/jsh77/fjava/tick3/UnsafeMessageQueue.java @@ -17,16 +17,6 @@ package uk.ac.cam.jsh77.fjava.tick3; public class UnsafeMessageQueue implements MessageQueue { - private static class Link { - L val; - Link next; - - Link(L val) { - this.val = val; - this.next = null; - } - } - private Link first = null; private Link last = null; @@ -34,6 +24,13 @@ public class UnsafeMessageQueue implements MessageQueue { // TODO: given a new "val", create a new Link // element to contain it and update "first" and // "last" as appropriate + Link link = new Link<>(val); + if (first == null && last == null) { + first = last = link; + } else { + last.next = link; + last = link; + } } public T take() { @@ -44,10 +41,44 @@ public class UnsafeMessageQueue implements MessageQueue { // Ignored exception // TODO: what causes this exception to be thrown? and what should // you do with it ideally? + /* + The exception is thrown when the thread is interrupted while blocking (sleeping here). + To correctly support interrupts, the Thread should exit cleanly. In this case, the best way to do that + is to return null. + */ + return null; } } - // TODO: retrieve "val" from "first", update "first" to refer - // to next element in list (if any). Return "val" - return null; + + Link oldFirst = first; + if (last == oldFirst) last = null; + first = oldFirst.next; + return oldFirst.val; + } + + @Override + public String toString() { + return "UnsafeMessageQueue{" + + "first=" + first + + ", last=" + last + + '}'; + } + + private static class Link { + L val; + Link next; + + Link(L val) { + this.val = val; + this.next = null; + } + + @Override + public String toString() { + return "Link{" + + "val=" + val + + ", next=" + next + + '}'; + } } }