With this assignment you will demonstrate:
Please:
Work in teams of 1 to 4 students.
Continue working on the repository you forked in Homework #1. Please remember that you should have only one official (graded) forked repository per group.
There are no unit tests supplied for this assignment, but we will run your submissions and eyeball the results for correctness. As specified in the README file for the homework template repository linked above, your grade depends not only on (1) all tests passing, but a significant portion will be based on (2) following instructions, (3) maintaining a clean repository, (4) code hygiene, and (5) following all formatting, indentation, naming, and styling conventions of the programming language.
To submit your work, choose one and only one team member to submit to BrightSpace a single text file containing (1) the names of all team members and (2) the URL of your private forked repository. Your homework submission will be the state of your repository on your main branch, at 18:00 in the America/Los Angeles time zone on the due date.
Implement the following simulation in Go (that’s right—there’s only one language for this assignment): Ten customers (Ani, Bai, Cat, Dao, Eve, Fay, Gus, Hua, Iza, and Jai) make several visits to a restaurant with three cooks (Remy, Colette, and Linguini) and only one waiter. As each customer arrives, they try to place an order with the busy waiter, who can only hold 3 outstanding orders at a time. The waiter takes the order to a free cook (if any) who prepares the meal, which takes a random amount of time (5000-10000 ms). The cook personally delivers the meal to the customer. If it takes too long (7000 ms) for a customer to get their order placed (after all, the waiter can’t hold that many orders at a time), they leave the restaurant and come back later (between 2500 and 5000 ms). It takes between 1000 and 2000 ms to eat a meal. When a customer eats five meals, they go home. Only after all customers have gone home, the restaurant shuts down.
Because it may help a bit, here is a solution in Java:
import java.util.List;
import java.time.LocalDateTime;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
public class Restaurant {
// Java's built-in logging utility is a bit of overkill for this example.
private static void log(Object... args) {
var strings = List.of(args).stream().map(Object::toString).toList();
System.out.println(LocalDateTime.now() + " " + String.join(" ", strings));
}
// A little utility that simulates performing a task for a random duration.
// For example, calling doAction(10, "Remy", "is cooking") will compute a
// random number of millisecs between 5000 and 10000, log "Remy is cooking",
// and sleep the current goroutine for that much time.
private static void doAction(int seconds, Object... action) {
log(action);
try {
var randomMillis = 500 * seconds +
ThreadLocalRandom.current().nextInt(500 * seconds);
Thread.sleep(randomMillis);
} catch (InterruptedException e) {
// Restore interrupted status and continue
Thread.currentThread().interrupt();
}
}
// An order for a meal is placed by a customer and is taken by a cook.
// When the meal is finished, the cook will send the finished meal through
// the reply channel. Each order has a unique id, safely incremented using
// an atomic counter.
static class Order {
static final AtomicLong nextId = new AtomicLong(0);
final long id;
final String customer;
final CompletableFuture<Order> reply;
String preparedBy;
Order(String customer) {
this.id = nextId.incrementAndGet();
this.customer = customer;
this.reply = new CompletableFuture<>();
}
}
// The waiter is represented by a channel of orders, which in Java is
// represented as a BlockingQueue. The waiter will take orders from
// customers and send them to the cook. The cook will then send the
// prepared meal back to the waiter. To simulate a waiter being busy,
// the waiter channel has a buffer capacity of 3 orders.
private static final BlockingQueue<Order> waiter = new LinkedBlockingQueue<>(3);
// A cook spends their time fetching orders from the order channel,
// cooking the requested meal, and sending the meal back through the
// order's reply channel. The cook function is designed to be run on a
// Java virtual thread.
public static void cook(String name) {
log(name, "starting work");
while (true) {
try {
// We want to get the order with a blocking call
var order = waiter.take();
doAction(10, name, "cooking order", order.id, "for", order.customer);
order.preparedBy = name;
// Completing the future sends the cooked order back to the customer
order.reply.complete(order);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}
// A customer eats five meals and then goes home. Each time they enter the
// restaurant, they place an order with the waiter. If the waiter is too
// busy, the customer will wait for 5 seconds before abandoning the order.
// If the order does get placed, then they will wait as long as necessary
// for the meal to be cooked and delivered.
public static void customer(String name, CountDownLatch latch) {
try {
for (var mealsEaten = 0; mealsEaten < 5; ) {
var order = new Order(name);
log(name, "placed order", order.id);
try {
// Wait 7 seconds for the waiter to take the order
if (waiter.offer(order, 7, TimeUnit.SECONDS)) {
// Wait indefinitely for the meal to be cooked
var meal = order.reply.get();
// Eat for up to 2 seconds
doAction(2, name, "eating cooked order", meal.id,
"prepared by", meal.preparedBy);
mealsEaten++;
} else {
doAction(5, name, "waiting too long, abandoning order", order.id);
}
} catch (InterruptedException | ExecutionException e) {
Thread.currentThread().interrupt();
break;
}
}
log(name, "going home");
} finally {
latch.countDown();
}
}
public static void main(String[] args) throws InterruptedException {
var customers = List.of(
"Ani", "Bai", "Cat", "Dao", "Eve", "Fay", "Gus", "Hua", "Iza", "Jai");
// All threads will be virtual threads
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
executor.execute(() -> cook("Remy"));
executor.execute(() -> cook("Colette"));
executor.execute(() -> cook("Linguini"));
// Need a latch in order to wait for all customers to finish
CountDownLatch latch = new CountDownLatch(customers.size());
for (String customer : customers) {
executor.execute(() -> customer(customer, latch));
}
latch.await();
// Cleanup nicely
log("Restaurant closing");
executor.shutdown();
}
}
Here are your requirements for translating to Go:
*Order
objects (pointers to orders).atomic.Uint64
.Waiter
channel and writing to the order.reply
channel.select
statement with a 7 second timeout that tries to give the order to the waiter.main
function, create a waitGroup
to make sure the main goroutine does not exit until all customers have finished. This means that each customer goroutine will need to know its wait group, and will decrement the wait group count when it is done. Do this with the defer
construct in the customer function.log.Println
, which gives you the date and time for free.Here is an example output when using log.Println
in Go:
2024/08/24 22:41:21 Linguini starting work 2024/08/24 22:41:21 Bai placed order 1 2024/08/24 22:41:21 Cat placed order 3 2024/08/24 22:41:21 Linguini cooking order 1 for Bai 2024/08/24 22:41:21 Iza placed order 4 2024/08/24 22:41:21 Ani placed order 2 2024/08/24 22:41:21 Remy starting work 2024/08/24 22:41:21 Remy cooking order 3 for Cat 2024/08/24 22:41:21 Gus placed order 6 2024/08/24 22:41:21 Fay placed order 5 2024/08/24 22:41:21 Jai placed order 7 2024/08/24 22:41:21 Hua placed order 8 2024/08/24 22:41:21 Colette starting work 2024/08/24 22:41:21 Colette cooking order 4 for Iza 2024/08/24 22:41:21 Eve placed order 10 2024/08/24 22:41:21 Dao placed order 9 2024/08/24 22:41:27 Colette cooking order 2 for Ani 2024/08/24 22:41:27 Iza eating cooked order 4 prepared by Colette 2024/08/24 22:41:28 Iza placed order 11 2024/08/24 22:41:28 Dao waiting too long, abandoning order 9 2024/08/24 22:41:28 Hua waiting too long, abandoning order 8 2024/08/24 22:41:28 Eve waiting too long, abandoning order 10 2024/08/24 22:41:28 Linguini cooking order 6 for Gus 2024/08/24 22:41:28 Bai eating cooked order 1 prepared by Linguini 2024/08/24 22:41:30 Bai placed order 12 2024/08/24 22:41:30 Remy cooking order 5 for Fay 2024/08/24 22:41:30 Cat eating cooked order 3 prepared by Remy 2024/08/24 22:41:31 Eve placed order 13 2024/08/24 22:41:32 Cat placed order 14 2024/08/24 22:41:33 Dao placed order 15 2024/08/24 22:41:33 Hua placed order 16 2024/08/24 22:41:34 Colette cooking order 7 for Jai 2024/08/24 22:41:34 Ani eating cooked order 2 prepared by Colette 2024/08/24 22:41:36 Ani placed order 17 2024/08/24 22:41:36 Linguini cooking order 11 for Iza 2024/08/24 22:41:36 Gus eating cooked order 6 prepared by Linguini 2024/08/24 22:41:37 Remy cooking order 12 for Bai 2024/08/24 22:41:37 Fay eating cooked order 5 prepared by Remy 2024/08/24 22:41:38 Gus placed order 18 2024/08/24 22:41:39 Fay placed order 19 2024/08/24 22:41:40 Hua waiting too long, abandoning order 16 2024/08/24 22:41:41 Colette cooking order 13 for Eve 2024/08/24 22:41:41 Jai eating cooked order 7 prepared by Colette 2024/08/24 22:41:42 Jai placed order 20 2024/08/24 22:41:42 Linguini cooking order 14 for Cat 2024/08/24 22:41:42 Iza eating cooked order 11 prepared by Linguini 2024/08/24 22:41:43 Iza placed order 21 2024/08/24 22:41:44 Hua placed order 22 2024/08/24 22:41:46 Remy cooking order 15 for Dao 2024/08/24 22:41:46 Bai eating cooked order 12 prepared by Remy 2024/08/24 22:41:46 Colette cooking order 17 for Ani 2024/08/24 22:41:46 Eve eating cooked order 13 prepared by Colette 2024/08/24 22:41:47 Bai placed order 23 2024/08/24 22:41:48 Eve placed order 24 2024/08/24 22:41:50 Iza waiting too long, abandoning order 21 2024/08/24 22:41:51 Hua waiting too long, abandoning order 22 2024/08/24 22:41:52 Linguini cooking order 18 for Gus 2024/08/24 22:41:52 Cat eating cooked order 14 prepared by Linguini 2024/08/24 22:41:53 Cat placed order 25 2024/08/24 22:41:54 Remy cooking order 19 for Fay 2024/08/24 22:41:54 Dao eating cooked order 15 prepared by Remy 2024/08/24 22:41:54 Hua placed order 26 2024/08/24 22:41:54 Iza placed order 27 2024/08/24 22:41:55 Colette cooking order 20 for Jai 2024/08/24 22:41:55 Ani eating cooked order 17 prepared by Colette 2024/08/24 22:41:55 Dao placed order 28 2024/08/24 22:41:57 Ani placed order 29 2024/08/24 22:41:58 Linguini cooking order 23 for Bai 2024/08/24 22:41:58 Gus eating cooked order 18 prepared by Linguini 2024/08/24 22:41:59 Gus placed order 30 2024/08/24 22:42:01 Iza waiting too long, abandoning order 27 2024/08/24 22:42:02 Remy cooking order 24 for Eve 2024/08/24 22:42:02 Fay eating cooked order 19 prepared by Remy 2024/08/24 22:42:03 Fay placed order 31 2024/08/24 22:42:03 Colette cooking order 25 for Cat 2024/08/24 22:42:03 Jai eating cooked order 20 prepared by Colette 2024/08/24 22:42:04 Iza placed order 32 2024/08/24 22:42:05 Jai placed order 33 2024/08/24 22:42:06 Gus waiting too long, abandoning order 30 2024/08/24 22:42:07 Remy cooking order 26 for Hua 2024/08/24 22:42:07 Eve eating cooked order 24 prepared by Remy 2024/08/24 22:42:08 Linguini cooking order 28 for Dao 2024/08/24 22:42:08 Bai eating cooked order 23 prepared by Linguini 2024/08/24 22:42:08 Eve placed order 34 2024/08/24 22:42:09 Bai placed order 35 2024/08/24 22:42:10 Gus placed order 36 2024/08/24 22:42:12 Jai waiting too long, abandoning order 33 2024/08/24 22:42:12 Colette cooking order 29 for Ani 2024/08/24 22:42:12 Cat eating cooked order 25 prepared by Colette 2024/08/24 22:42:14 Linguini cooking order 31 for Fay 2024/08/24 22:42:14 Dao eating cooked order 28 prepared by Linguini 2024/08/24 22:42:14 Cat placed order 37 2024/08/24 22:42:15 Jai placed order 38 2024/08/24 22:42:15 Dao placed order 39 2024/08/24 22:42:15 Remy cooking order 32 for Iza 2024/08/24 22:42:15 Hua eating cooked order 26 prepared by Remy 2024/08/24 22:42:17 Hua placed order 40 2024/08/24 22:42:20 Colette cooking order 34 for Eve 2024/08/24 22:42:20 Ani eating cooked order 29 prepared by Colette 2024/08/24 22:42:21 Ani placed order 41 2024/08/24 22:42:21 Linguini cooking order 35 for Bai 2024/08/24 22:42:21 Fay eating cooked order 31 prepared by Linguini 2024/08/24 22:42:22 Dao waiting too long, abandoning order 39 2024/08/24 22:42:23 Fay placed order 42 2024/08/24 22:42:24 Hua waiting too long, abandoning order 40 2024/08/24 22:42:25 Remy cooking order 36 for Gus 2024/08/24 22:42:25 Iza eating cooked order 32 prepared by Remy 2024/08/24 22:42:26 Iza placed order 43 2024/08/24 22:42:27 Dao placed order 44 2024/08/24 22:42:27 Hua placed order 45 2024/08/24 22:42:30 Colette cooking order 37 for Cat 2024/08/24 22:42:30 Eve eating cooked order 34 prepared by Colette 2024/08/24 22:42:31 Eve placed order 46 2024/08/24 22:42:31 Linguini cooking order 38 for Jai 2024/08/24 22:42:31 Bai eating cooked order 35 prepared by Linguini 2024/08/24 22:42:33 Bai placed order 47 2024/08/24 22:42:34 Dao waiting too long, abandoning order 44 2024/08/24 22:42:34 Hua waiting too long, abandoning order 45 2024/08/24 22:42:34 Remy cooking order 41 for Ani 2024/08/24 22:42:34 Gus eating cooked order 36 prepared by Remy 2024/08/24 22:42:35 Gus placed order 48 2024/08/24 22:42:37 Colette cooking order 42 for Fay 2024/08/24 22:42:37 Cat eating cooked order 37 prepared by Colette 2024/08/24 22:42:38 Cat placed order 49 2024/08/24 22:42:38 Dao placed order 50 2024/08/24 22:42:39 Hua placed order 51 2024/08/24 22:42:40 Remy cooking order 43 for Iza 2024/08/24 22:42:40 Ani eating cooked order 41 prepared by Remy 2024/08/24 22:42:40 Linguini cooking order 46 for Eve 2024/08/24 22:42:40 Jai eating cooked order 38 prepared by Linguini 2024/08/24 22:42:41 Ani placed order 52 2024/08/24 22:42:41 Jai placed order 53 2024/08/24 22:42:44 Colette cooking order 47 for Bai 2024/08/24 22:42:44 Fay eating cooked order 42 prepared by Colette 2024/08/24 22:42:46 Fay placed order 54 2024/08/24 22:42:46 Hua waiting too long, abandoning order 51 2024/08/24 22:42:48 Linguini cooking order 48 for Gus 2024/08/24 22:42:48 Eve eating cooked order 46 prepared by Linguini 2024/08/24 22:42:48 Jai waiting too long, abandoning order 53 2024/08/24 22:42:49 Remy cooking order 49 for Cat 2024/08/24 22:42:49 Iza eating cooked order 43 prepared by Remy 2024/08/24 22:42:49 Hua placed order 55 2024/08/24 22:42:50 Eve placed order 56 2024/08/24 22:42:50 Iza placed order 57 2024/08/24 22:42:51 Jai placed order 58 2024/08/24 22:42:51 Colette cooking order 50 for Dao 2024/08/24 22:42:51 Bai eating cooked order 47 prepared by Colette 2024/08/24 22:42:52 Bai going home 2024/08/24 22:42:53 Linguini cooking order 52 for Ani 2024/08/24 22:42:53 Gus eating cooked order 48 prepared by Linguini 2024/08/24 22:42:54 Remy cooking order 54 for Fay 2024/08/24 22:42:54 Cat eating cooked order 49 prepared by Remy 2024/08/24 22:42:55 Gus placed order 59 2024/08/24 22:42:56 Cat going home 2024/08/24 22:42:58 Jai waiting too long, abandoning order 58 2024/08/24 22:43:01 Linguini cooking order 55 for Hua 2024/08/24 22:43:01 Ani eating cooked order 52 prepared by Linguini 2024/08/24 22:43:01 Jai placed order 60 2024/08/24 22:43:01 Colette cooking order 56 for Eve 2024/08/24 22:43:01 Dao eating cooked order 50 prepared by Colette 2024/08/24 22:43:02 Remy cooking order 57 for Iza 2024/08/24 22:43:02 Fay eating cooked order 54 prepared by Remy 2024/08/24 22:43:02 Dao placed order 61 2024/08/24 22:43:03 Ani going home 2024/08/24 22:43:03 Fay going home 2024/08/24 22:43:06 Colette cooking order 59 for Gus 2024/08/24 22:43:06 Eve eating cooked order 56 prepared by Colette 2024/08/24 22:43:07 Linguini cooking order 60 for Jai 2024/08/24 22:43:07 Hua eating cooked order 55 prepared by Linguini 2024/08/24 22:43:08 Eve going home 2024/08/24 22:43:09 Hua placed order 62 2024/08/24 22:43:10 Remy cooking order 61 for Dao 2024/08/24 22:43:10 Iza eating cooked order 57 prepared by Remy 2024/08/24 22:43:12 Colette cooking order 62 for Hua 2024/08/24 22:43:12 Gus eating cooked order 59 prepared by Colette 2024/08/24 22:43:12 Iza going home 2024/08/24 22:43:13 Gus going home 2024/08/24 22:43:14 Jai eating cooked order 60 prepared by Linguini 2024/08/24 22:43:16 Jai placed order 63 2024/08/24 22:43:16 Linguini cooking order 63 for Jai 2024/08/24 22:43:18 Dao eating cooked order 61 prepared by Remy 2024/08/24 22:43:20 Dao placed order 64 2024/08/24 22:43:20 Remy cooking order 64 for Dao 2024/08/24 22:43:21 Hua eating cooked order 62 prepared by Colette 2024/08/24 22:43:22 Jai eating cooked order 63 prepared by Linguini 2024/08/24 22:43:23 Hua placed order 65 2024/08/24 22:43:23 Colette cooking order 65 for Hua 2024/08/24 22:43:24 Jai going home 2024/08/24 22:43:26 Dao eating cooked order 64 prepared by Remy 2024/08/24 22:43:28 Dao going home 2024/08/24 22:43:31 Hua eating cooked order 65 prepared by Colette 2024/08/24 22:43:32 Hua placed order 66 2024/08/24 22:43:32 Linguini cooking order 66 for Hua 2024/08/24 22:43:38 Hua eating cooked order 66 prepared by Linguini 2024/08/24 22:43:39 Hua going home 2024/08/24 22:43:39 Restaurant closing
You will be awarded up to 75 points for correctness, that is, (1) each customer eats exactly five meals, (2) the simulation is free of deadlocks, livelocks, and race conditions, (3) the simulation is accurate in that a customer is not eating two meals at a time, etc., and (4) everything shuts down normally (i.e., there are no resource leaks and no premature termination). The other 25 points are earned by precisely following all instructions, writing the logged messages with the format shown above, writing code that is built according to each language’s conventions, keeping a clean repository, etc. Your feedback will be in the form of deductions for such things as:
This list is not exhaustive, but should help in getting you used to paying attention to your submissions and taking pride in your work. Note that most code editors have plugins that will auto-format your code and even look for suspicious coding practices. You are strongly encouraged to use them.