lab1 code

This commit is contained in:
2025-04-22 02:05:21 +08:00
parent 7868ccecdd
commit 4707552388
8 changed files with 629 additions and 33 deletions

208
.clang-format Normal file
View File

@@ -0,0 +1,208 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: true
AlignConsecutiveBitFields:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveDeclarations:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignConsecutiveMacros:
Enabled: false
AcrossEmptyLines: false
AcrossComments: false
AlignCompound: false
PadOperators: false
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: true
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: false
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
AttributeMacros:
- __capability
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: true
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeConceptDeclarations: Always
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
QualifierAlignment: Leave
CompactNamespaces: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
EmptyLineAfterAccessModifier: Never
EmptyLineBeforeAccessModifier: LogicalBlock
ExperimentalAutoDetectBinPacking: false
PackConstructorInitializers: BinPack
BasedOnStyle: ''
ConstructorInitializerAllOnOneLineOrOnePerLine: false
AllowAllConstructorInitializersOnNextLine: true
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IfMacros:
- KJ_IF_MAYBE
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
CaseSensitive: false
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
CaseSensitive: false
- Regex: '.*'
Priority: 1
SortPriority: 0
CaseSensitive: false
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentAccessModifiers: false
IndentCaseLabels: false
IndentCaseBlocks: false
IndentGotoLabels: true
IndentPPDirectives: None
IndentExternBlock: AfterExternBlock
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: false
InsertBraces: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
LambdaBodyIndentation: Signature
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakOpenParenthesis: 0
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PenaltyIndentedWhitespace: 0
PointerAlignment: Right
PPIndentWidth: -1
ReferenceAlignment: Pointer
ReflowComments: true
RemoveBracesLLVM: false
RequiresClausePosition: OwnLine
SeparateDefinitionBlocks: Leave
ShortNamespaceLines: 1
SortIncludes: CaseSensitive
SortJavaStaticImport: Before
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeParensOptions:
AfterControlStatements: true
AfterForeachMacros: true
AfterFunctionDefinitionName: false
AfterFunctionDeclarationName: false
AfterIfMacros: true
AfterOverloadedOperator: false
AfterRequiresInClause: false
AfterRequiresInExpression: false
BeforeNonEmptyParentheses: false
SpaceAroundPointerQualifiers: Default
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
BitFieldColonSpacing: Both
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 4
UseCRLF: false
UseTab: Never

60
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,60 @@
{
"files.associations": {
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"random": "cpp",
"ratio": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"initializer_list": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"mutex": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"semaphore": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"thread": "cpp",
"typeinfo": "cpp",
"syncstream": "cpp",
"csignal": "cpp",
"sstream": "cpp",
"fstream": "cpp"
}
}

1
lab1/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
build/

View File

@@ -3,7 +3,7 @@ OUT_BIN := $(OUT_DIR)/banker
$(OUT_BIN): banker.cpp
mkdir -p $(OUT_DIR)
g++ -pthread banker.cpp -o $(OUT_BIN)
g++ -pthread --std=c++20 banker.cpp -o $(OUT_BIN)
.PHONY: run
run: $(OUT_BIN)

View File

@@ -1,22 +1,19 @@
#include <iostream>
#include <thread>
#include <deque>
#include <vector>
#include <chrono>
#include <atomic>
#include <chrono>
#include <csignal>
#include <deque>
#include <fstream>
#include <iostream>
#include <mutex>
#include <semaphore>
#include <sstream>
#include <syncstream>
#include <thread>
#include <vector>
#define TELLER_NUM 5
#define TELLER_NUM 3
#define TIME_GRAN_MSEC 200
std::atomic<bool> bank_open = false;
std::mutex custom_q_mtx;
std::counting_semaphore custom_num_sem;
std::deque<Customer> customer_q;
struct CustomerInfo {
unsigned int id;
unsigned int arrival_time;
@@ -28,42 +25,164 @@ struct Customer {
unsigned int service_time;
};
void teller_thread(int id, std::chrono::time_point<std::chrono::steady_clock> start_tick) {
std::cout << "Teller id " << id << " started\n";
std::atomic<bool> bank_open{false};
std::mutex customer_q_mtx;
std::counting_semaphore<100> customer_num_sem{0};
std::deque<Customer> customer_q;
void signal_handler(int signum) {
// std::cout << "\nReceived signal " << signum << ", stopping...\n";
write(STDOUT_FILENO, "\nCaught SIGINT, shutting down...\n", 33);
bank_open = false;
}
void teller_thread(
int id, std::chrono::time_point<std::chrono::steady_clock> 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<std::chrono::steady_clock> start_serving_time;
struct Customer curr_customer;
while (bank_open) {
if (is_serving && ((curr_tick - start_serving_time) / TIME_GRAN_MSEC) >= curr_customer.service_time) {
if (is_serving) {
if ((std::chrono::duration_cast<std::chrono::milliseconds>(
(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;
}
}
std::cout << "Teller id " << id << " waiting request\n";
// Try to acquire a new customer
if (customer_num_sem.try_acquire()) {
// Successfully acquired a customer
std::lock_guard<std::mutex> 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::cout << "Teller id " << id << " closing\n";
std::osyncstream(std::cout)
<< "Teller id " << id << " closing" << std::endl;
}
int main() {
bank_open = true;
void customer_thread(
CustomerInfo info,
std::chrono::time_point<std::chrono::steady_clock> start_tick) {
std::vector<std::thread> tellers;
for (int i = 0; i < TELLER_NUM; ++i) {
try {
tellers.emplace_back(teller_thread, i);
} catch (const std::system_error& e) {
std::cerr << "Failed to create thread, i = " << i << ": " << e.what() << '\n';
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<std::mutex> lock(customer_q_mtx);
customer_q.push_back(cus);
}
customer_num_sem.release();
}
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<CustomerInfo> 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;
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(5 * TIME_GRAN_MSEC));
std::cout << "Closing all child\n";
bank_open = false;
file.close();
for (auto& t : tellers) {
bank_open = true;
std::chrono::time_point start_tick = std::chrono::steady_clock::now();
// Set up tellers
std::vector<std::thread> 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<std::thread> 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();
}

71
lab1/test_data/1_out.txt Normal file
View File

@@ -0,0 +1,71 @@
Teller id 0 started
Teller id 1 started
Teller id 1 is idle at tick 0
Teller id 0 is idle at tick 0
Teller id 2 started
Teller id 2 is idle at tick 0
Teller id 0 is idle at tick 1
Teller id 2 is idle at tick 1
Teller id 1 is idle at tick 1
Customer id 1 arrived at tick 1
Teller id 0 is now serving customer 1 for 10 ticks at tick 2
Teller id 1 is idle at tick 2
Teller id 2 is idle at tick 2
Teller id 0 is serving customer 1 at tick 3
Teller id 1 is idle at tick 3
Teller id 2 is idle at tick 3
Teller id 0 is serving customer 1 at tick 4
Teller id 1 is idle at tick 4
Teller id 2 is idle at tick 4
Teller id 0 is serving customer 1 at tick 5
Customer id 2 arrived at tick 5
Teller id 2 is idle at tick 5
Teller id 1 is idle at tick 5
Teller id 0 is serving customer 1 at tick 6
Teller id 2 is now serving customer 2 for 2 ticks at tick 6
Customer id 3 arrived at tick 6
Teller id 1 is idle at tick 6
Teller id 0 is serving customer 1 at tick 7
Teller id 1 is now serving customer 3 for 3 ticks at tick 7
Teller id 2 is serving customer 2 at tick 7
Teller id 0 is serving customer 1 at tick 8
Teller id 1 is serving customer 3 at tick 8
Teller id 2 is serving customer 2 at tick 8
Teller id 0 is serving customer 1 at tick 9
Teller id 1 is serving customer 3 at tick 9
Teller id 2 finishied serving customer 2 at tick 9
Teller id 2 is idle at tick 9
Teller id 1 is serving customer 3 at tick 10
Teller id 0 is serving customer 1 at tick 10
Teller id 2 is idle at tick 10
Teller id 0 is serving customer 1 at tick 11
Teller id 1 finishied serving customer 3 at tick 11
Teller id 2 is idle at tick 11
Teller id 1 is idle at tick 11
Teller id 1 is idle at tick 12
Teller id 2 is idle at tick 12
Teller id 0 is serving customer 1 at tick 12
Teller id 0 finishied serving customer 1 at tick 13
Teller id 0 is idle at tick 13
Teller id 2 is idle at tick 13
Teller id 1 is idle at tick 13
Teller id 0 is idle at tick 14
Teller id 1 is idle at tick 14
Teller id 2 is idle at tick 14
Teller id 0 is idle at tick 15
Teller id 2 is idle at tick 15
Teller id 1 is idle at tick 15
Teller id 0 is idle at tick 16
Teller id 1 is idle at tick 16
Teller id 2 is idle at tick 16
Teller id 2 is idle at tick 17
Teller id 0 is idle at tick 17
Teller id 1 is idle at tick 17
Teller id 0 is idle at tick 18
Teller id 1 is idle at tick 18
Teller id 2 is idle at tick 18
^C
Caught SIGINT, shutting down...
Teller id 0 closing
Teller id 1 closing
Teller id 2 closing

7
lab1/test_data/2.txt Normal file
View File

@@ -0,0 +1,7 @@
1 1 10
2 2 10
3 3 10
4 4 10
5 5 5
6 6 7
8 7 10

130
lab1/test_data/2_out.txt Normal file
View File

@@ -0,0 +1,130 @@
Teller id 1 started
Teller id 0 started
Teller id 2 started
Teller id 1 is idle at tick 0
Teller id 2 is idle at tick 0
Teller id 0 is idle at tick 0
Teller id 0 is idle at tick 1
Teller id 2 is idle at tick 1
Teller id 1 is idle at tick 1
Customer id 1 arrived at tick 1
Teller id 0 is now serving customer 1 for 10 ticks at tick 2
Teller id 1 is idle at tick 2
Teller id 2 is idle at tick 2
Customer id 2 arrived at tick 2
Teller id 1 is now serving customer 2 for 10 ticks at tick 3
Customer id 3 arrived at tick 3
Teller id 0 is serving customer 1 at tick 3
Teller id 2 is idle at tick 3
Teller id 0 is serving customer 1 at tick 4
Teller id 2 is now serving customer 3 for 10 ticks at tick 4
Teller id 1 is serving customer 2 at tick 4
Customer id 4 arrived at tick 4
Teller id 0 is serving customer 1 at tick 5
Teller id 2 is serving customer 3 at tick 5
Teller id 1 is serving customer 2 at tick 5
Customer id 5 arrived at tick 5
Customer id 6 arrived at tick 6
Teller id 1 is serving customer 2 at tick 6
Teller id 0 is serving customer 1 at tick 6
Teller id 2 is serving customer 3 at tick 6
Teller id 1 is serving customer 2 at tick 7
Teller id 0 is serving customer 1 at tick 7
Customer id 8 arrived at tick 7
Teller id 2 is serving customer 3 at tick 7
Teller id 1 is serving customer 2 at tick 8
Teller id 2 is serving customer 3 at tick 8
Teller id 0 is serving customer 1 at tick 8
Teller id 2 is serving customer 3 at tick 9
Teller id 1 is serving customer 2 at tick 9
Teller id 0 is serving customer 1 at tick 9
Teller id 0 is serving customer 1 at tick 10
Teller id 2 is serving customer 3 at tick 10
Teller id 1 is serving customer 2 at tick 10
Teller id 2 is serving customer 3 at tick 11
Teller id 1 is serving customer 2 at tick 11
Teller id 0 is serving customer 1 at tick 11
Teller id 1 is serving customer 2 at tick 12
Teller id 2 is serving customer 3 at tick 12
Teller id 0 is serving customer 1 at tick 12
Teller id 2 is serving customer 3 at tick 13
Teller id 1 is serving customer 2 at tick 13
Teller id 0 finishied serving customer 1 at tick 13
Teller id 0 is now serving customer 4 for 10 ticks at tick 13
Teller id 2 is serving customer 3 at tick 14
Teller id 1 finishied serving customer 2 at tick 14
Teller id 1 is now serving customer 5 for 5 ticks at tick 14
Teller id 0 is serving customer 4 at tick 14
Teller id 2 finishied serving customer 3 at tick 15
Teller id 2 is now serving customer 6 for 7 ticks at tick 15
Teller id 1 is serving customer 5 at tick 15
Teller id 0 is serving customer 4 at tick 15
Teller id 2 is serving customer 6 at tick 16
Teller id 1 is serving customer 5 at tick 16
Teller id 0 is serving customer 4 at tick 16
Teller id 2 is serving customer 6 at tick 17
Teller id 0 is serving customer 4 at tick 17
Teller id 1 is serving customer 5 at tick 17
Teller id 2 is serving customer 6 at tick 18
Teller id 1 is serving customer 5 at tick 18
Teller id 0 is serving customer 4 at tick 18
Teller id 2 is serving customer 6 at tick 19
Teller id 0 is serving customer 4 at tick 19
Teller id 1 is serving customer 5 at tick 19
Teller id 0 is serving customer 4 at tick 20
Teller id 1 finishied serving customer 5 at tick 20
Teller id 1 is now serving customer 8 for 10 ticks at tick 20
Teller id 2 is serving customer 6 at tick 20
Teller id 1 is serving customer 8 at tick 21
Teller id 0 is serving customer 4 at tick 21
Teller id 2 is serving customer 6 at tick 21
Teller id 2 is serving customer 6 at tick 22
Teller id 1 is serving customer 8 at tick 22
Teller id 0 is serving customer 4 at tick 22
Teller id 1 is serving customer 8 at tick 23
Teller id 2 finishied serving customer 6 at tick 23
Teller id 2 is idle at tick 23
Teller id 0 is serving customer 4 at tick 23
Teller id 1 is serving customer 8 at tick 24
Teller id 0 finishied serving customer 4 at tick 24
Teller id 2 is idle at tick 24
Teller id 0 is idle at tick 24
Teller id 1 is serving customer 8 at tick 25
Teller id 2 is idle at tick 25
Teller id 0 is idle at tick 25
Teller id 1 is serving customer 8 at tick 26
Teller id 2 is idle at tick 26
Teller id 0 is idle at tick 26
Teller id 1 is serving customer 8 at tick 27
Teller id 0 is idle at tick 27
Teller id 2 is idle at tick 27
Teller id 1 is serving customer 8 at tick 28
Teller id 2 is idle at tick 28
Teller id 0 is idle at tick 28
Teller id 1 is serving customer 8 at tick 29
Teller id 2 is idle at tick 29
Teller id 0 is idle at tick 29
Teller id 1 is serving customer 8 at tick 30
Teller id 0 is idle at tick 30
Teller id 2 is idle at tick 30
Teller id 1 finishied serving customer 8 at tick 31
Teller id 1 is idle at tick 31
Teller id 0 is idle at tick 31
Teller id 2 is idle at tick 31
Teller id 1 is idle at tick 32
Teller id 2 is idle at tick 32
Teller id 0 is idle at tick 32
Teller id 1 is idle at tick 33
Teller id 2 is idle at tick 33
Teller id 0 is idle at tick 33
Teller id 0 is idle at tick 34
Teller id 1 is idle at tick 34
Teller id 2 is idle at tick 34
Teller id 0 is idle at tick 35
Teller id 1 is idle at tick 35
Teller id 2 is idle at tick 35
^C
Caught SIGINT, shutting down...
Teller id 1 closing
Teller id 0 closing
Teller id 2 closing