#include #include #include #include #include #include #include #include #include #include #include #include #define TELLER_NUM 3 #define TIME_GRAN_MSEC 200 struct CustomerInfo { unsigned int id; unsigned int arrival_time; unsigned int service_time; }; struct Customer { unsigned int id; unsigned int service_time; }; std::atomic bank_open{false}; // BEGIN_LST_CUS_Q std::mutex customer_q_mtx; std::deque customer_q; // END_LST_CUS_Q // BEGIN_LST_COUNT_SEM std::counting_semaphore<100> customer_num_sem{0}; // END_LST_COUNT_SEM void signal_handler(int signum) { // std::cout << "\nReceived signal " << signum << ", stopping...\n"; write(STDOUT_FILENO, "\nCaught SIGINT, shutting down...\n", 33); bank_open = false; } // BEGIN_LST_TELLER void teller_thread( int id, std::chrono::time_point start_tick) { std::osyncstream(std::cout) << "Teller id " << id << " started" << std::endl; std::chrono::time_point curr_tick = start_tick; unsigned int tick_count = 0; bool is_serving = false; std::chrono::time_point start_serving_time; struct Customer curr_customer; while (bank_open) { if (is_serving) { if ((std::chrono::duration_cast( (curr_tick - start_serving_time) / TIME_GRAN_MSEC)) .count() <= curr_customer.service_time) { // We are still serving, do nothing std::osyncstream(std::cout) << "Teller id " << id << " is serving customer " << curr_customer.id << " at tick " << tick_count << std::endl; goto next_tick; } else { // We have just finished serving. std::osyncstream(std::cout) << "Teller id " << id << " finishied serving customer " << curr_customer.id << " at tick " << tick_count << std::endl; is_serving = false; } } // Try to acquire a new customer if (customer_num_sem.try_acquire()) { // Successfully acquired a customer std::lock_guard lock(customer_q_mtx); if (!customer_q.empty()) { curr_customer = customer_q.front(); customer_q.pop_front(); std::osyncstream(std::cout) << "Teller id " << id << " is now serving customer " << curr_customer.id << " for " << curr_customer.service_time << " ticks at tick " << tick_count << std::endl; is_serving = true; start_serving_time = curr_tick; } } else { std::osyncstream(std::cout) << "Teller id " << id << " is idle at tick " << tick_count << std::endl; } next_tick: ++tick_count; curr_tick += std::chrono::milliseconds(TIME_GRAN_MSEC); std::this_thread::sleep_until(curr_tick); } std::osyncstream(std::cout) << "Teller id " << id << " closing" << std::endl; } // END_LST_TELLER // BEGIN_LST_CUSTOMER void customer_thread( CustomerInfo info, std::chrono::time_point start_tick) { Customer cus{.id = info.id, .service_time = info.service_time}; std::chrono::time_point curr_tick = start_tick; unsigned int tick_count = 0; std::chrono::time_point target_tick = start_tick + std::chrono::milliseconds(TIME_GRAN_MSEC) * info.arrival_time; while (curr_tick < target_tick) { if (!bank_open) { return; } ++tick_count; curr_tick += std::chrono::milliseconds(TIME_GRAN_MSEC); std::this_thread::sleep_until(curr_tick); } // Wake up and push self into queue, then V(customer_num_sem) std::osyncstream(std::cout) << "Customer id " << info.id << " arrived at tick " << tick_count << std::endl; { std::lock_guard lock(customer_q_mtx); customer_q.push_back(cus); } customer_num_sem.release(); } // END_LST_CUSTOMER // BEGIN_LST_MAIN int main(int argc, char *argv[]) { if (argc != 2) { std::cout << "Invalid commandline arg, expected filename"; return 1; } std::signal(SIGINT, signal_handler); // Read config from file std::vector customer_infos; std::ifstream file(argv[1]); if (!file.is_open()) { std::cerr << "Cannot open file: " << argv[1] << std::endl; return 1; } std::string line; while (std::getline(file, line)) { std::istringstream iss(line); CustomerInfo cus; if (iss >> cus.id >> cus.arrival_time >> cus.service_time) { customer_infos.push_back(cus); } else { std::cerr << "Invalid line: " << line << std::endl; } } file.close(); bank_open = true; std::chrono::time_point start_tick = std::chrono::steady_clock::now(); // Set up tellers std::vector tellers; for (int i = 0; i < TELLER_NUM; ++i) { try { tellers.emplace_back(teller_thread, i, start_tick); } catch (const std::system_error &e) { std::cerr << "Failed to create teller thread, i = " << i << ": " << e.what() << std::endl; } } // Set up customers std::vector customers; for (auto cu : customer_infos) { try { customers.emplace_back(customer_thread, cu, start_tick); } catch (const std::system_error &e) { std::cerr << "Failed to create customer thread, id = " << cu.id << ": " << e.what() << std::endl; } } // Wait for all children to shutdown (by ctrl-c handler) for (auto &t : customers) { if (t.joinable()) { t.join(); } } for (auto &t : tellers) { if (t.joinable()) { t.join(); } } return 0; } // END_LST_MAIN