This commit is contained in:
violette 2024-03-24 13:14:37 -04:00
parent 97ce72dd93
commit 3b99a35740
9 changed files with 289 additions and 139 deletions

View file

@ -5,51 +5,68 @@ import java.util.ArrayList;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
/** import java.util.concurrent.BlockingQueue;
* Hello world! import java.util.concurrent.LinkedBlockingQueue;
*
*/
public class App { public class App {
static private int numStops = 10;
static private int numBusses = 5; static private int numBusses = 5;
static private int numStopPerBus = 2; static private int numStopPerBus = 8;
static private int numPassengersPerBus = 2; static private int numStops = numBusses * numStopPerBus;
static private int numPassengersPerStop = 5; static private int numPassengersPerBus = 10;
static private int numPassengersPerStop = 3;
static private int numPassengers = numPassengersPerStop * numStops; static private int numPassengers = numPassengersPerStop * numStops;
static private int numThreads = 5; static private int numThreadsBus = 5;
static private int timeBetweenStops = 0; static private int numThreadsStop = 2;
static private int timeEmbark = 0; static private int timeBetweenStops = 5;
static private int timeEmbark = 2;
public static void main(String[] args) { public static void main(String[] args) {
ExecutorService threads = Executors.newFixedThreadPool(numThreads); ExecutorService threadsBus = Executors.newFixedThreadPool(numThreadsBus);
ArrayList<Stop> stops = new ArrayList<Stop>(); ExecutorService threadsStop = Executors.newFixedThreadPool(numThreadsStop);
ArrayList<Bus> busses = new ArrayList<Bus>(); ArrayList<Stop> stops = new ArrayList<Stop>();
ArrayList<Passenger> passengers = new ArrayList<Passenger>(); ArrayList<Bus> busses = new ArrayList<Bus>();
ArrayList<Passenger> passengers = new ArrayList<Passenger>();
BlockingQueue<BusInformationMessage> blockingQueue = new LinkedBlockingQueue<BusInformationMessage>();
// make stops
for (int k = 0; k < numStops; k++) { for (int k = 0; k < numStops; k++) {
Stop s = new Stop(k, numPassengersPerStop); Stop s = new Stop(k, numPassengersPerStop);
stops.add(s); s.setBlockingQueue(blockingQueue);
//threads.submit(s); stops.add(s);
s.setThreadPool(threadsStop);
threadsStop.submit(s);
//s.run();
} }
// make busses
for (int k = 0; k < numBusses; k++) { for (int k = 0; k < numBusses; k++) {
if (numStopPerBus > numStops)
{System.out.println("More stops per bus than stops."); return;}
ArrayList<Stop> s_list = new ArrayList<Stop>(); ArrayList<Stop> s_list = new ArrayList<Stop>();
Stop s; Stop s;
// add stop. Make a bus doesnt stop multiple time at the same stop
while (s_list.size() < numStopPerBus) { while (s_list.size() < numStopPerBus) {
do { do {
s = stops.get((int) (Math.random() * numStops)); s = stops.get((int) (Math.random() * numStops));
} while (s_list.contains(s)); } while (s_list.contains(s));
s_list.add(s); s_list.add(s);
} }
Bus b = new Bus(s_list, k, timeBetweenStops, timeEmbark, numPassengersPerBus); Bus b = new Bus(s_list, k, timeBetweenStops, timeEmbark, numPassengersPerBus);
b.setBlockingQueue(blockingQueue);
busses.add(b); busses.add(b);
} }
// make passenger
int idPassenger = 0; int idPassenger = 0;
for (Bus b : busses) { for (Bus b : busses) {
for (Stop start : b.getStops()) { for (Stop start : b.getStops()) {
// make sure passenger can leave.
if (start == b.getTerminus()) if (start == b.getTerminus())
continue; continue;
@ -70,22 +87,30 @@ public class App {
} }
} }
// pretty print!
for (Bus b : busses) { for (Bus b : busses) {
b.printDetails(); b.printDetails();
} }
System.out.println("-----".repeat(5) + " START " + "-----".repeat(5));
// start bus thread // start bus thread
// (here so init logs are clean) // (here so init logs are clean)
for (Bus b : busses) { for (Bus b : busses) {
threads.submit(b); threadsBus.submit(b);
//b.run();
} }
threads.shutdown(); // block bus pool
threadsBus.shutdown();
try { try {
threads.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); threadsBus.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {}
// block stop pool once bus have finished
threadsStop.shutdown();
try {
threadsStop.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
} catch (InterruptedException e) {} } catch (InterruptedException e) {}
} }
} }

View file

@ -1,31 +1,39 @@
package usherbrooke.ift630; package usherbrooke.ift630;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.BlockingQueue;
public class Bus extends Thread { public class Bus extends Thread {
private ArrayList<Passenger> passengers; private ArrayList<Passenger> passengers;
private ArrayList<Stop> stops; private ArrayList<Stop> stops;
private String name; private String name;
private int id; private int id;
private int maxCapacity; private int maxCapacity;
private int currentCapacity; private int currentCapacity;
private int timeBetweenStops; private int timeBetweenStops;
private int timeEmbark; private int timeToNextStop;
private Stop nextStop; private int timeEmbark;
private Stop nextStop;
private BlockingQueue<BusInformationMessage> blockingQueue;
// returns time for a single passenger to embark.
private int timeEmbark() { private int timeEmbark() {
return (int) (Math.random() * timeEmbark); return (int) (Math.random() * timeEmbark);
} }
private int timeBetweenStops() { // set time between stop.
return (int) (Math.random() * timeBetweenStops); private int calculateTimeBetweenStops() {
timeToNextStop = (int) ((timeBetweenStops / 3) + (Math.random() * 2 * timeBetweenStops / 3));
return timeToNextStop;
} }
// check if a passenger must leave at next stop.
private boolean stopAsked() { private boolean stopAsked() {
boolean res = false; boolean res = false;
for (Passenger p : passengers){ for (Passenger p : passengers){
if (p.getDest() == nextStop) { if (p.getDest() == nextStop) {
res = true; res = true;
Logger.getInstance().print(id, "[BUS] " + name + " stop asked!");
break; break;
} }
} }
@ -33,25 +41,39 @@ public class Bus extends Thread {
return res; return res;
} }
private Stop goToNextStop() { // travel to next stop. Waits the travel time.
private Stop goToNextStop() throws InterruptedException, IndexOutOfBoundsException {
// if no reason to stop, skip current stop // if no reason to stop, skip current stop
try { boolean nextStopEmpty, overMaxCapacity;
do { do {
nextStop = stops.remove(0); nextStop = stops.remove(0);
} while ((getNextStopPassengers().isEmpty() || currentCapacity >= maxCapacity) && !stopAsked());
} catch (IndexOutOfBoundsException e) { sendNextStopInfo();
nextStop = null;
} waitStop();
overMaxCapacity = currentCapacity >= maxCapacity;
nextStopEmpty = getNextStopPassengers().isEmpty();
if (overMaxCapacity)
Logger.getInstance().print(id, "[BUS] " + name + " im full, sorry!");
if (nextStopEmpty && !stopAsked())
Logger.getInstance().print(id, "[BUS] " + name + " next " + nextStop.getStopName() + " is empty!");
} while (!stopAsked() && (nextStopEmpty || overMaxCapacity));
try {
Thread.sleep(timeBetweenStops() * 1000);
} catch (InterruptedException e) {
}
return nextStop; return nextStop;
} }
// TODO what // send Bus info msg to next stop
private void sendNextStopInfo() throws InterruptedException{
BusInformationMessage info = new BusInformationMessage(this, nextStop, timeToNextStop);
synchronized(blockingQueue) {
blockingQueue.put(info);
}
}
// get all passenger waiting at next stop
// useful for knowing if we should stop
private ArrayList<Passenger> getNextStopPassengers() { private ArrayList<Passenger> getNextStopPassengers() {
ArrayList<Passenger> res = new ArrayList<Passenger>(); ArrayList<Passenger> res = new ArrayList<Passenger>();
@ -66,57 +88,82 @@ public class Bus extends Thread {
return res; return res;
} }
// lock until passenger got in
private void waitEmbark() throws InterruptedException {
synchronized (this) {
sleep(timeEmbark());
}
}
// lock until passenger got out
private void waitStop() throws InterruptedException {
synchronized (this) {
sleep(timeToNextStop);
}
}
@Override @Override
public void run() { public void run() {
try { try {
while (nextStop != null) { // run until terminus
Logger.getInstance().print(id, "[BUS] hop into " + name + " at stop " + nextStop.getName() + "!"); while (nextStop != null) {
disembarkPassengers(); // new stop !
embarkPassengers(); Logger.getInstance().print(id, "[BUS] " + name + " reached stop " + nextStop.getStopName() + "!");
nextStop = goToNextStop(); disembarkPassengers();
embarkPassengers();
calculateTimeBetweenStops();
try {
nextStop = goToNextStop();
} catch (IndexOutOfBoundsException e) {
nextStop = null;
}
}
} catch (InterruptedException e) {
Logger.getInstance().print(id, "[BUS] exception: " + e.toString());
} }
} catch (Exception e) { Logger.getInstance().print(id, "[BUS] " + name + " exiting!");
Logger.getInstance().print(id, "[BUS] exception: " + e.getMessage());
}
Logger.getInstance().print(id, "[BUS]" + name + " exiting!");
} }
Bus(ArrayList<Stop> s, int id, int timeStop, int timeEmbark, int maxCapacity) { Bus(ArrayList<Stop> s, int id, int timeStop, int timeEmbark, int maxCapacity) {
this.timeBetweenStops = timeStop; this.timeBetweenStops = timeStop * 1000 + 1;
this.timeEmbark = timeEmbark; this.timeEmbark = timeEmbark * 1000 + 1;
this.name = "Bus n°" + id; this.name = "Bus n°" + id;
this.id = id; this.id = id;
this.passengers = new ArrayList<Passenger>(); this.passengers = new ArrayList<Passenger>();
this.stops = s; this.stops = s;
this.nextStop = stops.remove(0); this.nextStop = stops.get(0);
this.currentCapacity = 0; this.currentCapacity = 0;
this.maxCapacity = maxCapacity; this.maxCapacity = maxCapacity;
} }
public synchronized void disembarkPassengers() { // disembark passenger at a stop. Synchronizes, locks on that stop, and waits.
Logger.getInstance().print(id, "[BUS] waiting mutex for " + nextStop.getName()); public synchronized void disembarkPassengers() throws InterruptedException {
synchronized (nextStop.getMutex()) { // lock on next stop
synchronized (nextStop) {
for (Passenger p : passengers) { for (Passenger p : passengers) {
waitEmbark();
Logger.getInstance().print(id, Logger.getInstance().print(id,
"\t[DISEMBARK] " + p.getName() + " at " + nextStop.getName()); "\t[DISEMBARK] " + p.getName() + " at " + nextStop.getStopName());
if (p.getDest() == nextStop) { if (p.getDest() == nextStop) {
p.disembark(); p.disembark();
currentCapacity--; currentCapacity--;
} }
} }
Logger.getInstance().print(id, "[BUS] release mutex for " + nextStop.getName());
} }
} }
public synchronized void embarkPassengers() { // embark passenger at a stop. Synchronizes, locks on that stop, and waits.
Logger.getInstance().print(id, "[BUS] waiting mutex for " + nextStop.getName()); // check for overflow, and if passenger should embark in the first place
synchronized (nextStop.getMutex()) { public synchronized void embarkPassengers() throws InterruptedException {
synchronized (nextStop) {
try { try {
ArrayList<Passenger> list = nextStop.getPassengerByDest(stops); ArrayList<Passenger> list = nextStop.getPassengerByDest(stops);
for (Passenger p : list) { for (Passenger p : list) {
Thread.sleep(timeEmbark() * 1000); waitEmbark();
if (currentCapacity >= maxCapacity) if (currentCapacity >= maxCapacity)
continue; continue;
passengers.add(p); passengers.add(p);
@ -124,40 +171,46 @@ public class Bus extends Thread {
currentCapacity++; currentCapacity++;
Logger.getInstance().print(id, Logger.getInstance().print(id,
"\t[EMBARK] " + p.getName() + " at " + nextStop.getName()); "\t[EMBARK] " + p.getName() + " at " + nextStop.getStopName());
} }
} catch (IndexOutOfBoundsException | InterruptedException e) { } catch (IndexOutOfBoundsException e) {
Logger.getInstance().print(id, "Exception: " + e.getMessage()); Logger.getInstance().print(id, "Exception: " + e.getMessage());
} }
Logger.getInstance().print(id, "[BUS] release mutex for " + nextStop.getName());
} }
} }
// pretty print!
public void printDetails(int indent) { public void printDetails(int indent) {
Logger.getInstance().print(id, "\t".repeat(indent) + "---".repeat(3) + " Bus details " + "---".repeat(3)); Logger.getInstance().print(id, "\t".repeat(indent) + "---".repeat(3) + " Bus details " + "---".repeat(3));
Logger.getInstance().print(id, "\t".repeat(indent) + name); Logger.getInstance().print(id, "\t".repeat(indent) + name);
Logger.getInstance().print(id, "\t".repeat(indent) + "time between stops: " + timeBetweenStops); Logger.getInstance().print(id, "\t".repeat(indent) + "time between stops: " + timeBetweenStops);
Logger.getInstance().print(id, "\t".repeat(indent) + "max capacity: " + maxCapacity); Logger.getInstance().print(id, "\t".repeat(indent) + "max capacity: " + maxCapacity);
Logger.getInstance().print(id, "\t".repeat(indent) + "current stop: " + nextStop.getName()); Logger.getInstance().print(id, "\t".repeat(indent) + "current stop: " + nextStop.getStopName());
Logger.getInstance().print(id, "\t".repeat(indent) + "stops: "); Logger.getInstance().print(id, "\t".repeat(indent) + "stops: ");
nextStop.printDetails(id, indent + 1);
if (nextStop != stops.get(0))
nextStop.printDetails(id, indent + 1);
for (Stop s : stops) { for (Stop s : stops) {
s.printDetails(id, indent + 1); s.printDetails(id, indent + 1);
} }
} }
// pretty print w/ 0 indent
public void printDetails() { public void printDetails() {
printDetails(0); printDetails(0);
} }
// return stop list
public ArrayList<Stop> getStops() { public ArrayList<Stop> getStops() {
ArrayList<Stop> res = new ArrayList<Stop>(stops); ArrayList<Stop> res = new ArrayList<Stop>(stops);
res.add(0, nextStop); if (nextStop != stops.get(0))
res.add(0, nextStop);
return res; return res;
} }
// return last stop
public Stop getTerminus() { public Stop getTerminus() {
return stops.get(stops.size() - 1); return stops.get(stops.size() - 1);
} }
@ -165,4 +218,8 @@ public class Bus extends Thread {
public String getNameBus() { public String getNameBus() {
return name; return name;
} }
public void setBlockingQueue(BlockingQueue<BusInformationMessage> q) {
blockingQueue = q;
}
} }

View file

@ -0,0 +1,39 @@
package usherbrooke.ift630;
class BusInformationMessage {
private Bus bus; // origin
private Stop stop; //dest
private int timeToStop; // info
public BusInformationMessage(Bus b, Stop s, int time) {
this.bus = b;
this.stop = s;
this.timeToStop = time;
}
// throws unauth if stop shouldt see
public Bus getBus(Stop s) throws UnauthorizedException {
if (s != stop) {
throw new UnauthorizedException("UnauthorizedException");
}
return bus;
}
// throws unauth if stop shouldt see
public Stop getStop(Stop s) throws UnauthorizedException {
if (s != stop) {
throw new UnauthorizedException("UnauthorizedException");
}
return stop;
}
// throws unauth if stop shouldt see
public int getTimeToStop(Stop s) throws UnauthorizedException {
if (s != stop) {
throw new UnauthorizedException("UnauthorizedException");
}
return timeToStop;
}
}

View file

@ -0,0 +1,9 @@
package usherbrooke.ift630;
public class ExitException extends Exception {
ExitException() {}
public ExitException(String message) {
super(message);
}
}

View file

@ -1,10 +0,0 @@
package usherbrooke.ift630;
public class NotFoundException extends Exception {
NotFoundException() {}
public NotFoundException(String message)
{
super(message);
}
}

View file

@ -1,10 +0,0 @@
package usherbrooke.ift630;
public class OverCapacityException extends Exception {
OverCapacityException() {}
public OverCapacityException(String message)
{
super(message);
}
}

View file

@ -14,8 +14,10 @@ public class Passenger {
this.start = start; this.start = start;
} }
// unused, *yet*
public synchronized void embark() {} public synchronized void embark() {}
// unused, *yet*
public synchronized void disembark() {} public synchronized void disembark() {}
public Stop getStart() { public Stop getStart() {
@ -30,16 +32,19 @@ public class Passenger {
return name; return name;
} }
// Pretty print, with color!
public void printDetails(int color, int indent) { public void printDetails(int color, int indent) {
Logger.getInstance().print(color, "\t".repeat(indent) + "---".repeat(3) + " Passenger details " + "---".repeat(3)); Logger.getInstance().print(color, "\t".repeat(indent) + "---".repeat(3) + " Passenger details " + "---".repeat(3));
Logger.getInstance().print(color, "\t".repeat(indent) + name + " start: " + start.getName() + " dest: " + dest.getName()); Logger.getInstance().print(color, "\t".repeat(indent) + name + " start: " + start.getStopName() + " dest: " + dest.getStopName());
} }
// Pretty print, with indent!
public void printDetails(int indent) { public void printDetails(int indent) {
Logger.getInstance().print(id, "\t".repeat(indent) + "---".repeat(3) + " Passenger details " + "---".repeat(3)); Logger.getInstance().print(id, "\t".repeat(indent) + "---".repeat(3) + " Passenger details " + "---".repeat(3));
Logger.getInstance().print(id, "\t".repeat(indent) + name + " start: " + start.getName() + " dest: " + dest.getName()); Logger.getInstance().print(id, "\t".repeat(indent) + name + " start: " + start.getStopName() + " dest: " + dest.getStopName());
} }
// Pretty print!
public void printDetails() { public void printDetails() {
printDetails(0); printDetails(0);
} }

View file

@ -1,37 +1,66 @@
package usherbrooke.ift630; package usherbrooke.ift630;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.locks.Lock; import java.util.concurrent.BlockingQueue;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.ExecutorService;
public class Stop implements Runnable { public class Stop extends Thread {
private String name; private String name;
private int id; private int id;
private int maxCapacity; private int maxCapacity;
private ArrayList<Passenger> passengers; private int indent = 0;
private Lock mutex = new ReentrantLock(); private ArrayList<Passenger> passengers;
private ExecutorService threads;
private BlockingQueue<BusInformationMessage> blockingQueue;
// print bus info times.
private void displayInfos(int indent) throws InterruptedException, ExitException{
BusInformationMessage info = null;
// lock queue, get head
synchronized (blockingQueue) {
info = blockingQueue.poll();
}
// get infos
try {
try {
int time = info.getTimeToStop(this) / 1000;
// if everything is alright, print
Logger.getInstance().print(id,
"\t".repeat(indent) + "[STOP] " + info.getBus(this).getNameBus() + " arrives in " + time + "s");
// else, catch
} catch (NullPointerException e) {}
} catch (UnauthorizedException e) {
// if unauth, requeue message.
synchronized (blockingQueue) {
blockingQueue.add(info);
}
}
// loop, and ensure that every stop is reactive
synchronized (this) {
threads.submit(this);
sleep(10);
throw new ExitException();
}
}
// TODO lock mutex when stop is used
@Override @Override
public void run() { public void run() {
// xd do nothing for now try {
//for (Passenger p : passengers) { for (;;) {
// // displayInfos(indent);
//} }
} catch (InterruptedException | ExitException e) {}
} }
// run once. Then, queue the same thread, and exit.
// Message a bus their current number of passenger ; and the
// number of passenger willing to take this bus.
Stop(int id, int maxCapacity) { Stop(int id, int maxCapacity) {
this.id = id; this.id = id;
this.name = "Stop " + id; this.name = "Stop " + id;
this.passengers = new ArrayList<Passenger>(); this.passengers = new ArrayList<Passenger>();
this.maxCapacity = maxCapacity; this.maxCapacity = maxCapacity;
}
public Passenger getPassenger() {
return passengers.get(0);
} }
public void addPassenger(Passenger p) { public void addPassenger(Passenger p) {
@ -40,10 +69,12 @@ public class Stop implements Runnable {
// return all passenger that stops at a stop in the list // return all passenger that stops at a stop in the list
public ArrayList<Passenger> getPassengerByDest(ArrayList<Stop> list) { public ArrayList<Passenger> getPassengerByDest(ArrayList<Stop> list) {
// res for a little cleaner code
ArrayList<Passenger> res = new ArrayList<Passenger>(); ArrayList<Passenger> res = new ArrayList<Passenger>();
// for all given stop
for (Stop s : list) { for (Stop s : list) {
// for all passenger at this stop
for (Passenger p : passengers) { for (Passenger p : passengers) {
// if they stop at this stop, add to res
if (p.getDest() == s) { if (p.getDest() == s) {
res.add(p); res.add(p);
} }
@ -53,7 +84,7 @@ public class Stop implements Runnable {
return res; return res;
} }
public String getName() { public String getStopName() {
return name; return name;
} }
@ -65,10 +96,6 @@ public class Stop implements Runnable {
return maxCapacity; return maxCapacity;
} }
public Lock getMutex() {
return mutex;
}
public void printDetails(int color, int indent) { public void printDetails(int color, int indent) {
Logger.getInstance().print(color, "\t".repeat(indent) + "---".repeat(3) + " Stop details " + "---".repeat(3)); Logger.getInstance().print(color, "\t".repeat(indent) + "---".repeat(3) + " Stop details " + "---".repeat(3));
Logger.getInstance().print(color, "\t".repeat(indent) + name); Logger.getInstance().print(color, "\t".repeat(indent) + name);
@ -91,24 +118,23 @@ public class Stop implements Runnable {
printDetails(0); printDetails(0);
} }
public void busArrive() {
synchronized(this) {
mutex.lock();
}
}
public void busLeave() {
synchronized(this) {
mutex.unlock();
}
}
public void removePassenger(Passenger p) { public void removePassenger(Passenger p) {
// Logger.getInstance().print(id, "Passenger " + p.getName() + " left " + name);
try { try {
passengers.remove(p); passengers.remove(p);
} catch(Exception e) { } catch (Exception e) {
System.out.println("exception" + e.getMessage()); System.out.println("exception" + e.getMessage());
} }
} }
public void setIndent(int indent) {
this.indent = indent;
}
public void setBlockingQueue(BlockingQueue<BusInformationMessage> q) {
blockingQueue = q;
}
public void setThreadPool(ExecutorService threads) {
this.threads = threads;
}
} }

View file

@ -0,0 +1,9 @@
package usherbrooke.ift630;
public class UnauthorizedException extends Exception {
public UnauthorizedException() {}
public UnauthorizedException(String message) {
super(message);
}
}