This repository has been 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