This repository was archived by the owner on Jan 20, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
m-tse/mustached-lana
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
################
# Team Info
################
Name1: Andrew Shim
NetId1: aks35
Name2: Matthew Tse
NetId2: mst13
Name3: Afanasiy Yermakov
NetId3: asy9
###############
# Time spent
###############
40 hours [combined]
################
# Files to submit
################
*.java #Including the test case files; Please do not submit tar or zip files
README #This file filled with the lab implementation details
Elevator.log #The name of the log file you generate should be Elevator.log.
# You can submit the log file along with your source code. However,
# it should be generated automatically when the Elevator is executed.
####################################
# Implementation details
####################################
______________________________________________________________________
PROMPT
This section should contain the implementation details and a overview of the
results.
You are required to provide a good README document along with the
implementation details. In particular, you can pseudocode to describe your
implementation details where necessary. However that does not mean to
copy/paste your Java code. Rather, provide clear and concise text/pseudocode
describing the primary algorithms (for e.g., scheduling choices you used) and
any special data structures in your implementation. We expect the design and
implementation details to be at most 2-3 pages. A plain text file is encouraged.
However, a pdf is acceptable. No other forms are permitted.
In case of lab is limited in some functionality, you should provide
the details to maximize your partial credit.
______________________________________________________________________
Part 1. Event Barrier
Our implementation of EventBarrier uses the following instance variables:
- boolean signaled
- int counter
We will discuss the implementation of the EventBarrier methods using these
variables.
void hold()
The hold() method makes use of the signaled boolean and counter.
Anytime a thread (i.e. minstrel) calls the hold() method, the
counter is incremented, and this operation is made atomic by using
the "synchronized" key word; the counter allows the EventBarrier to know how many
"waiters" it has. While the signaled boolean is false (i.e. the EventBarrier
has not been signaled), the thread is forced to wait through the
wait() method. Once the EventBarrier becomes signaled and the thread
is notified, the thread will be allowed to move on.
void signal()
The signal() method makes use of the signaled boolean and the counter.
Once a "gatekeeper" thread calls signal, the EventBarrier's signaled
boolean gets set to true and the EventBarrier calls notifyAll(), thereby
waking up all threads that were waiting on the EventBarrier. Then the
thread is forced to wait() while the number of waiters is greater then 0.
This simulates the notion of the "gatekeeper" waiting for all of the
"minstrels" to cross the bridge. Once the last thread is finished
"crossing the bridge", the "gatekeeper" thread breaks out of the wait
because it is notified by the waiters as they call complete() (discussed below).
The EventBarrier is then set to unsignaled.
void complete()
The complete() method uses the counter. When a thread calls
complete(), which means the thread has finished "crossing the bridge",
the EventBarrier decrements its counter (done atomically through "synchronized")
because there is one less waiter waiting. Then, the calling thread
notifies all of the waiters. This serves two functions: one, it wakes
up the signaler, and two it wakes up any other threads that were waiting
on the calling thread. Basically, when a thread completes, if the number of
waiters is greater than 0 that thread will wait() until there are no more
waiters. Thus, it is up to every completing thread to notifyAll() because
there may be threads waiting on the calling thread's completion. This
simulates the "minstrels" waiting for all of the other "minstrels" to
finish crossing the bridge before moving on.
int waiters()
This method simply returns the current count of the waiters.
Our EventBarrier also comes with logging and debug toggle functionality
but this functionality was more for our benefit and was outside the
scope of the lab requirements.
Testing EventBarrier
To test our EventBarrier we created TestThread.java, TestGateKeeper.java,
and EventSimulator.java. EventSimulator takes a text file as input
where each line of the file contains a single integer. That integer
represents how many TestThreads are to be run. Upon initialization,
the EventSimulator creates a MyEventBarrier and TestGateKeeper.
Reading the file one line at a time, the EventSimulator creates the requested
number of TestThreads, where all of the created threads and the TestGateKeeper
share the same MyEventBarrier. It then launches the TestThreads,
sleeps for a moment, and then has the TestGateKeeper signal the
EventBarrier. Note, the TestThreads simply call hold() and
complete() on the MyEventBarrier.
The sample output for running the EventSimulator on "Part1_test.txt" was
logged to "Event.log".
______________________________________________________________________
Part 2. Elevator
Overview of Elevator System
We assume that there is exactly one building that can contain multiple
elevators and support multiple riders. We have the following three classes:
- Building
- Elevator extends Thread
- Rider extends Thread
The Building instance variables:
- int floors
- ArrayList<Elevator> elevators
- ArrayList<EventBarrier> enterBarriers
Elevator instance variables:
- Building myBuilding
- ArrayList<EventBarrier> ridingBarriers
- Queue<Integer> floorRequests
- Direction direction /* this is an enum defined in Elevator
can be UP, DOWN, or STATIONARY */
- int myId
- int riders
- int currentFloor
- int currentRequest
- int capacity
Rider instance variables:
- Building myBuilding
- int myId
Note: Just the pertinent instance variables are listed. By pertinent we
mean those that have to do with functionality listed in the handout.
Description of Functionality
The main method must start by initializing the Building.
While the Building initializes, it creates and launches its Elevator threads,
and creates an EventBarrier for each of its floors.
Then, the main creates as many Rider threads as is specified
by an input. Each of those Rider threads can make requests to the Building
to travel to a different floor. Based on where a Rider is, the Building will
send an Elevator to that Rider by requesting that the Elevator move to the
current floor of the Rider. Elevators handle floor requests by adding the
requests to their queues. The Rider must then wait for however long it takes
the assigned Elevator to reach the Rider's current floor. Once the Elevator
arrives at the Rider's floor, it opens its doors. If there are other Riders
currently in the Elevator, those Riders will exit while the Riders that have
been waiting on that floor for an Elevator enter. Entry will only be permitted
if the Elevator has not reached max capacity, otherwise the Rider will have
to wait for another Elevator. Once a Rider enters the Elevator, the Elevator
will proceed in the Direction it was traveling, unless the only floor
request in the Elevator's queue is that of the current Rider, then the Elevator
will take the Rider to the requested destination. As a Rider travels to
the destination, the Elevator will pick up and drop off
other Riders along the way. The Elevator will not, however, override the
"current" Rider's request (meaning that it will not go in the opposite direction
of where the "current" Rider wants to go), so it will always, although not
completely fairly, service the Rider's request. In summary, the Elevator scheduling
operates according to a SCAN methodology.
Implementation Specifics
We created an ElevatorSimulator.java that reads an input text file (as specified
in post 191 on Piazza) and runs the Elevators and Riders accordingly. The Riders
know which floors they are currently on and which floors they want to go to, thus
they also know in which direction they need to travel. They
pass this knowledge onto the building through their goToFloor() method, which eventually
calls the Building's CallUp/CallDown and AwaitUp/AwaitDown methods. The
Building's AwaitUp/Down methods are essentially wrappers that include logging
functionality with the CallUp/Down methods.
CallUp/Down methods find the closest Elevator (algorithm for that discussed
below) and then calls the RequestFor() method on that Elevator. The RequestFor() method
adds the requested floor to the Elevator's queue of requests.
Once the Elevator has been called, the rider will wait for the Elevator to get to its
floor by calling hold() on the Elevator's ridingBarrier (i.e. MyEventBarrier) for that floor.
We guarantee that the Elevators will service the Riders because the Elevators operate
in a loop where they call their VisitFloor() methods for the currentRequest; then
once they get to the floor of the current request, they take another request off of
their queues and proceed until there are no more requests.
Once an Elevator reaches the Rider's floor, the Elevator will call its OpenDoors() method
and signal() the EventBarrier of that floor. This will cause the requesting Riders
of that floor to wake up, after which they will call the Enter() method of the
Elevator. The Elevator will increment its count of riders for every call to Enter(),
and once its number of riders equals its capacity, the Elevator will stop allowing
Rides to enter.
In addition to signaling the riders to board, the OpenDoors() method removes
the current floor from the Elevator's queue of requests; this is because
everyone who was in the Elevator that wanted to go to the current floor will
have exited while those who want to get on the Elevator either have boarded
or cannot due to capacity constraints. At the end of OpenDoors(),
the Elevator calls CloseDoors() and resumes its travels.
Once an Elevator is finished visiting a floor (all Riders that wanted
to have entered (or capacity has been reached) and exited, it will set its current floor
request equal to -1. In an Elevator's main while loop, the loop conditional
checks whether the current floor request is equal to -1 or not. If it is,
the Elevator will fetch the next request from its queue unless the queue
is empty, in which case the current floor request will remain -1.
In the first situation, the Elevator has fetched the next request
and will make its way to the requested floor, otherwise the Elevator will
make its way to the center floor while maintaining that it is STATIONARY.
We make it move to the center floor because that minimizes the expected
amount of work the Elevator will need to carry out for the next request
(how many floors the Elevator will have to move), and we say that it is
STATIONARY because we only want to assign the Elevator's Direction when
it has received a request, otherwise we would indicate that
the Elevator is busier than it actually is (i.e. our algorithm to find
the "closest" Elevator would be affected).
Now that Riders have entered the Elevator, they can RequestFloor() and
travel to their destination. After making the request, the Riders will
call hold() on the Elevator's ridingBarrier of the destination floor.
When the Elevator reaches the destination, it will OpenDoors() as
before and the Riders will be allowing to exit while new ones
can enter. Note that Riders call complete() on the Elevator's
ridingBarriers at the end of every Enter() and Exit().
Algorithm for Finding the Closest Elevator
The method findClosestElevator() is in Building.
We base our definition of "closeness" on the proximity of the Elevator
to the requested floor and the Elevator's direction. We do not give
specific scores for these characteristics, rather we simply define
that going in the proper direction grants a significantly higher score
than being in close proximity (i.e. an Elevator A on floor 1 that is going
UP is closer to floor 4 than is an Elevator B on floor 5 that is going UP).
If two Elevators are going in the proper directions, then proximity is
taken into account (i.e. if Elevator B was on floor 5 and going DOWN then
it is closer to floor 4 than Elevator A is).
The algorithm prioritizes direction and proximity simply by checking
that the Elevator is going in the proper direction first. If the Elevator
is going the right way, the algorithm calculates the difference of
that Elevator's current floor with the desired floor and maintains
that difference. If the difference is the smallest the algorithm has
seen at that point, the algorithm will set that difference as the
smallest difference seen and set the Elevator as the return value.
The algorithm loops through all of the buildings Elevators, and by the
end of the loop, if there is a "closest" Elevator, that Elevator will
be returned.
However, if none of the Elevators are going in the proper direction,
the algorithm will not have found an Elevator to return. In this case,
the algorithm finds the Elevator with the lowest number of requests,
(the logic being that more requests means that an Elevator is more
likely to reach capacity and will thus be more likely to not
service the Rider by the time the Elevator gets to the Rider).
We realize that there are a number of metrics we are not considering
and that are important to a real-world model such as the time it
takes for an Elevator to service a request and how many Riders
the Elevator can satisfy in a given amount of time. In order
to model these, we could have timers on each of the Riders
and associate costs with not being fair or taking too much time.
It would then be the system's job to attempt to minimize these
costs.
Testing the Elevator System
As mentioned earlier, we came up with the ElevatorSimulator.java as a means
to test our implementation. The simulator takes a text file as input. The
text file MUST have the following information or the program will break:
F E R T C
r s d
.
.
.
The first line must contain 5 integers:
F = Number of floors in the Building
E = Number of Elevators in the Building
R = Number of Riders
T = Number of Rider threads
C = Capacity of the Elevators
After the first line there can be any number of following lines that
must contain three integers:
r = The Rider number
s = The starting floor
d = The destination floor
Upon completion of the simulation, the program will have produced
"Elevator.log", which captures all of the pertinent thread actions.
Two sample input text files have been provided:
"Part2_Sub1_test.txt"
"Part2_Sub2_test.txt"
"Part2_Sub3_test.txt"
A sample "Elevator.log" has also been provided; the log is a result of
running the simulator on "Part2_Sub2.txt".
In order to make sure that our Elevators and Riders are behaving as
expected, we also created SimulationGrader.java. The grader takes
the output file ("Elevator.log") as input and determines whether
or not the sequence of actions is in a proper order (e.g. the Rider has
to have called on an Elevator before it can start waiting on the Elevator).
If it finds out of order actions, it increments a score counter. When it
has finished reading through the input file, if the score is anything
greater than 0 then the grader determines that the simulation has
failed.
Note: The grader still has a few bugs in it, but it is a decent
is a good test for correct functionality.
####################################
# Feedback on the lab
####################################
How did you find the lab?
The lab was good in that it allowed us to explore multi-threaded programming
but the timeline for the release of guidelines lead to a bit of struggle, although
the extension did help to mitigate that. Basically,
when the lab was first assigned, we only had the handout to go off of, and the
handout mentioned nothing of the I/O testing guidelines (mentioned later on Piazza).
Because of this, we attacked this lab in a particular way and ended up having
to change a good amount of the code in order to follow the I/O guidelines.
For one thing, in order to log the particular information (e.g. "E1 on F3 closes")
certain methods had to get passed certain parameters. While this wasn't a very
significant mental challenge, it was rather tedious. Also, we found that we
spent a good amount of time on creating the test cases, whereas we feel that
it would have been better had we spent all our time dealing with threads.
##################################
# Additional comments
##################################
Anything else you would like to convey.
About
No description, website, or topics provided.
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published