Skip to content

Commit c3292a8

Browse files
authored
Merge pull request #164 from enigbe/refactor-use-listen-and-trigger-universally
refactor: use listen and trigger universally
2 parents b66a84a + 5aa5f65 commit c3292a8

File tree

3 files changed

+298
-223
lines changed

3 files changed

+298
-223
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ node_modules
33
*.json
44
activity-generator/releases/*
55
.DS_Store
6-
/results
6+
/results

docs/ARCHITECTURE.md

Lines changed: 32 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,25 @@
22

33
## Simulation Stages
44

5-
Simulation activities are broken down into four stages:
6-
1. Simulation Event: an action that the simulator should execute,
7-
produced from the user-provided description of desired activities.
5+
Simulation activities are broken down into four stages:
6+
7+
1. Simulation Event: an action that the simulator should execute,
8+
produced from the user-provided description of desired activities.
89
2. Simulation Output: the output of executing an simulation event. Note
9-
that this is not the final outcome of the event, as the event itself
10+
that this is not the final outcome of the event, as the event itself
1011
may take a non-trivial amount of time to complete.
11-
3. Result Tracking: per-output tracking of the final outcome of the
12+
3. Result Tracking: per-output tracking of the final outcome of the
1213
simulation event.
13-
4. Simulation Result: recording of the final result as provided by
14+
4. Simulation Result: recording of the final result as provided by
1415
result tracking, which is a final result of the simulation.
1516

1617
## Activity Generation
18+
1719
The simulator uses tokio's asynchronous multi-producer, single-consumer
18-
channels to implement a consume/producer architecture to produce,
19-
execute and track simulation events. Stages of the simulation
20-
communicate by passing the _receiver_ for the next stage to the
21-
consumer from the previous stage, allowing each stage to pass it's
20+
channels to implement a consume/producer architecture to produce,
21+
execute and track simulation events. Stages of the simulation
22+
communicate by passing the _receiver_ for the next stage to the
23+
consumer from the previous stage, allowing each stage to pass it's
2224
output on to the next one.
2325

2426
```
@@ -52,38 +54,25 @@ output on to the next one.
5254
```
5355

5456
### Shutdown
55-
To ensure that all tasks shutdown on completion (or failure) of the
56-
simulation, we relay on the following:
57-
1. [Triggered](https://docs.rs/triggered/latest/triggered): a `Trigger`
58-
that can be used to inform threads that it's time to shut down, and
59-
a `Listener` that propagates this signal.
60-
2. Channel mechanics: if all of the senders for a channel have been
61-
dropped (in our context, all of the producers have exited) then the
62-
corresponding receiving channel will error out on receiving. Likewise,
63-
if the receiving channel is dropped, the sending channels will error
64-
out.
65-
66-
All events are handled in a `tokio::select` to allow waiting on
67-
multiple asynchronous tasks at once. These selects should be `biased`
68-
on the exit case (ie, the `Listener` being triggered) so that we
69-
prioritize exit above generating more events.
7057

71-
Practically, this means that we're handling the various shutdown
72-
scenarios in the following way:
73-
74-
1. Consumer error:
75-
- Receiving channel is dropped on consumer exit.
76-
- Sending channels used by producers will error out.
77-
- Producers will trigger shutdown.
78-
- Listening producers will exit.
79-
80-
2. Producer error:
81-
- Producers will trigger shutdown.
82-
- Listening producers will exit.
83-
- Once all producers have exited, the consuming channel's receiver
84-
will error out causing it to exit.
85-
86-
3. Miscellaneous tasks:
87-
- Trigger shutdown on exit.
88-
- Listen for shutdown from other errors.
58+
To ensure that all tasks shutdown on completion (or failure) of the
59+
simulation, we relay on the following:
8960

61+
1. [Triggered](https://docs.rs/triggered/latest/triggered): a `Trigger`
62+
that can be used to inform threads/tasks that it's time to shut down,
63+
and a `Listener` that propagates this signal.
64+
2. The (`Trigger`, `Listener`) pair are used with channels: if a channel
65+
errors out across `send()` or `recv()`, shutdown is triggered. There is
66+
no reliance on channel mechanics, i.e. errors generated when all senders
67+
are and/or a receiver is dropped.
68+
3. All events are handled in a `tokio::select` to allow waiting on
69+
multiple asynchronous tasks at once. These selects should be `biased`
70+
on the exit case (ie, the `Listener` being triggered) so that we
71+
prioritize exit above generating more events.
72+
4. Additionally, we `select!` on shutdown signal on `send()`/`recv()`
73+
for all channels to guarantee this:
74+
- A task's receiver exiting while one or more corresponding senders
75+
(in different tasks) are actively sending, doesn't result in the
76+
sending tasks erroring due to channel `SendError`. Any sender's
77+
inability to `send()` due to a dropped receiver triggers a clean
78+
shutdown across all listening tasks.

0 commit comments

Comments
 (0)