├── .gitignore ├── src ├── spec.rs ├── start.rs ├── lib.rs └── supervisor.rs ├── .github └── workflows │ ├── security_audit.yml │ └── push_ci.yml ├── Cargo.toml ├── Cargo.lock ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /src/spec.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::time::Duration; 3 | 4 | -------------------------------------------------------------------------------- /.github/workflows/security_audit.yml: -------------------------------------------------------------------------------- 1 | name: Security audit 2 | on: 3 | schedule: 4 | - cron: '0 0 * * *' 5 | jobs: 6 | audit: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions-rs/audit-check@v1 11 | with: 12 | token: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/push_ci.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Continuous integration 4 | 5 | jobs: 6 | ci: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | rust: 11 | - stable 12 | - beta 13 | - nightly 14 | - 1.45.0 # MSRV 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | 19 | - uses: actions-rs/toolchain@v1 20 | with: 21 | profile: minimal 22 | toolchain: ${{ matrix.rust }} 23 | override: true 24 | components: rustfmt, clippy 25 | 26 | - name: cargo build 27 | uses: actions-rs/cargo@v1 28 | with: 29 | command: build 30 | 31 | - name: cargo test 32 | uses: actions-rs/cargo@v1 33 | with: 34 | command: test 35 | 36 | - name: cargo clippy 37 | uses: actions-rs/cargo@v1 38 | with: 39 | command: clippy 40 | args: -- -D warnings 41 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-supervisor" 3 | version = "0.1.0" 4 | description = "Simple erlang-style supervision for rust+async-backplane." 5 | keywords = ["supervisor", "supervision", "erlang", "reliability", "backplane"] 6 | categories = ["asynchronous", "concurrency"] 7 | homepage = "https://github.com/irrustible/async-supervisor" 8 | repository = "https://github.com/irrustible/async-supervisor" 9 | documentation = "https://docs.rs/async-supervisor" 10 | license = "MPL-2.0" 11 | authors = ["James Laver "] 12 | edition = "2018" 13 | readme = "README.md" 14 | 15 | [dependencies] 16 | async-backplane = "0.1" 17 | async-io = "0.1.*" 18 | futures-lite = "0.1.*" 19 | futures-micro = "0.2" 20 | simple-rate-limit = { git = "https://github.com/irrustible/simple-rate-limit", branch = "main" } 21 | # simple-rate-limit = { path = "../simple-rate-limit" } 22 | # rle-bitset = "0.1" 23 | # rle-bitset = { path = "../rle-bitset" } 24 | # futures-many = "0.1" 25 | futures-many = { git = "https://github.com/irrustible/futures-many", branch = "main" } 26 | # futures-many = { path = "../futures-many" } -------------------------------------------------------------------------------- /src/start.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use async_io::Timer; 3 | 4 | /// A boxed function that spawns a Future using the provided Device 5 | /// and returns a [`Starting`] the supervisor can await to confirm the 6 | /// process has started up. 7 | pub type StartFn = Box Starting>; 8 | 9 | /// Type of the boxed Future returned by [`StartFn`]. 10 | pub type Starting = Box> + Unpin>; 11 | 12 | pub enum StartError { 13 | /// The task failed on its own terms. 14 | Fault(Fault), 15 | /// The grace period was exceeded. 16 | Timeout, 17 | } 18 | 19 | /// Describes how a supervisor should start a task. The major 20 | /// component is a boxed function ([`Init`]), 21 | pub struct Start { 22 | pub fun: StartFn, 23 | /// How much of a hurry we are in to get onto starting the next task. 24 | pub haste: Haste, 25 | } 26 | 27 | impl Start { 28 | /// Creates a new [`Start`] from a [`Fn`] closure. 29 | pub fn new(fun: StartFn) -> Self { 30 | Start { 31 | fun, 32 | haste: Haste::Gracefully(Grace::Fixed(Duration::from_secs(5))), 33 | } 34 | } 35 | 36 | /// Sets the inner function to the provided value. 37 | pub fn set_fn(mut self, fun: StartFn) -> Self { 38 | self.fun = fun; 39 | self 40 | } 41 | 42 | /// Sets the grace period to the provided value. 43 | pub fn set_haste(mut self, haste: Haste) -> Self { 44 | self.haste = haste; 45 | self 46 | } 47 | 48 | /// Starts a process, giving it the appropriate grace period to start up 49 | pub async fn start(&self, device: Device) -> Result { 50 | match self.haste { 51 | Haste::Gracefully(Grace::Forever) => { 52 | (self.fun)(device).await.map_err(StartError::Fault) 53 | } 54 | Haste::Gracefully(Grace::Fixed(duration)) => { 55 | async { (self.fun)(device).await.map_err(StartError::Fault) } 56 | .or(async { 57 | Timer::new(duration).await; 58 | Err(StartError::Timeout) 59 | }) 60 | .await 61 | } 62 | Haste::Quickly => unimplemented!(), 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod start; 2 | pub use start::{Start, StartError, StartFn, Starting}; 3 | 4 | mod supervisor; 5 | pub use supervisor::Supervisor; 6 | 7 | // pub mod rest_for_one; 8 | // pub mod one_for_one; 9 | 10 | pub use simple_rate_limit::RateLimit; 11 | 12 | use async_backplane::{Device, DeviceID, Fault}; 13 | use futures_lite::FutureExt; 14 | use std::future::Future; 15 | use std::time::Duration; 16 | 17 | /// A logic for determining which other tasks to restart when one fails. 18 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 19 | pub enum RecoveryLogic { 20 | /// No other tasks will be restarted. 21 | Isolated, 22 | /// Tasks started after this one will be restarted. 23 | CascadeNewer, 24 | /// All tasks will be restarted. 25 | CascadeAll, 26 | } 27 | 28 | /// A period of time permitted for a startup/shutdown to occur. 29 | pub enum Grace { 30 | /// A fixed period of time. 31 | Fixed(Duration), 32 | /// As long as needed. Mainly for supervisors. Be very careful! 33 | Forever, 34 | } 35 | 36 | /// How patient should we be shutting down a task? 37 | pub enum Haste { 38 | /// We will wait for it to end before we continue. 39 | Gracefully(Grace), 40 | /// We will assume it to have disconnected and continue our work. 41 | Quickly, 42 | } 43 | 44 | /// When a service completes its startup phase successfully. Notifies 45 | /// the supervisor of whether it's finished or continuing to run. 46 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 47 | pub enum Started { 48 | /// We've done our work and we don't need to keep running. 49 | Completed, 50 | /// We've started up successfully. 51 | Running, 52 | } 53 | 54 | /// When should a supervisor restart a child? 55 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 56 | pub enum Restart { 57 | /// Do not restart: this is only supposed to run once. 58 | Never, 59 | /// We won't restart it if it succeeds. 60 | Failed, 61 | /// Restart even if it succeeds. 62 | Always, 63 | } 64 | 65 | /// A structure describing how the supervisor starts and manages a task. 66 | pub struct Spec { 67 | pub start: Start, 68 | pub restart: Restart, 69 | pub shutdown: Haste, 70 | } 71 | 72 | impl Spec { 73 | /// Creates a new Spec with the provided [`Start`]. 74 | pub fn new(start: Start) -> Self { 75 | Spec { 76 | start, 77 | restart: Restart::Always, 78 | shutdown: Haste::Gracefully(Grace::Fixed(Duration::from_secs(5))), 79 | } 80 | } 81 | 82 | /// Replaces [`start`] with a new [`Start`] 83 | pub fn set_start(mut self, start: Start) -> Self { 84 | self.start = start; 85 | self 86 | } 87 | 88 | /// Replaces [`restart`] with a new [`Restart`] 89 | pub fn set_restart(mut self, restart: Restart) -> Self { 90 | self.restart = restart; 91 | self 92 | } 93 | 94 | /// Replaces [`shutdown`] with a new [`Shutdown`] 95 | pub fn set_shutdown(mut self, shutdown: Haste) -> Self { 96 | self.shutdown = shutdown; 97 | self 98 | } 99 | } 100 | 101 | /// The Supervisor failed - why? 102 | pub enum SupervisionError { 103 | /// We were asked to shutdown, presumably by our own supervisor. 104 | Shutdown(DeviceID), 105 | /// We didn't manage to initialise the spec with the given index. 106 | StartupFailed(usize, StartError), 107 | /// Exceeded its restart rate limit. 108 | Throttled, 109 | } 110 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "async-backplane" 5 | version = "0.1.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "a9a516d4104b6588c490dc3a656b889eb5583652c16928d6bc2e546a61b6cc2d" 8 | dependencies = [ 9 | "atomic-waker", 10 | "concurrent-queue", 11 | "futures-lite", 12 | "maybe-unwind", 13 | "pin-project-lite", 14 | "waker-queue", 15 | ] 16 | 17 | [[package]] 18 | name = "async-io" 19 | version = "0.1.8" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "ebcd87dcd83e016eeda9d68c54d339668a87689f435e1c5784f9f3e308065d6b" 22 | dependencies = [ 23 | "cfg-if", 24 | "concurrent-queue", 25 | "futures-lite", 26 | "libc", 27 | "once_cell", 28 | "parking", 29 | "socket2", 30 | "vec-arena", 31 | "wepoll-sys-stjepang", 32 | "winapi", 33 | ] 34 | 35 | [[package]] 36 | name = "async-supervisor" 37 | version = "0.1.0" 38 | dependencies = [ 39 | "async-backplane", 40 | "async-io", 41 | "futures-lite", 42 | "futures-many", 43 | "futures-micro", 44 | "simple-rate-limit", 45 | ] 46 | 47 | [[package]] 48 | name = "atomic-waker" 49 | version = "1.0.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" 52 | 53 | [[package]] 54 | name = "cache-padded" 55 | version = "1.1.1" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" 58 | 59 | [[package]] 60 | name = "cc" 61 | version = "1.0.58" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "f9a06fb2e53271d7c279ec1efea6ab691c35a2ae67ec0d91d7acec0caf13b518" 64 | 65 | [[package]] 66 | name = "cfg-if" 67 | version = "0.1.10" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 70 | 71 | [[package]] 72 | name = "concurrent-queue" 73 | version = "1.1.2" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "1582139bb74d97ef232c30bc236646017db06f13ee7cc01fa24c9e55640f86d4" 76 | dependencies = [ 77 | "cache-padded", 78 | ] 79 | 80 | [[package]] 81 | name = "fastrand" 82 | version = "1.3.3" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "36a9cb09840f81cd211e435d00a4e487edd263dc3c8ff815c32dd76ad668ebed" 85 | 86 | [[package]] 87 | name = "futures-core" 88 | version = "0.3.5" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "59f5fff90fd5d971f936ad674802482ba441b6f09ba5e15fd8b39145582ca399" 91 | 92 | [[package]] 93 | name = "futures-io" 94 | version = "0.3.5" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "de27142b013a8e869c14957e6d2edeef89e97c289e69d042ee3a49acd8b51789" 97 | 98 | [[package]] 99 | name = "futures-lite" 100 | version = "0.1.10" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "bbe71459749b2e8e66fb95df721b22fa08661ad384a0c5b519e11d3893b4692a" 103 | dependencies = [ 104 | "fastrand", 105 | "futures-core", 106 | "futures-io", 107 | "memchr", 108 | "parking", 109 | "pin-project-lite", 110 | "waker-fn", 111 | ] 112 | 113 | [[package]] 114 | name = "futures-many" 115 | version = "0.1.0" 116 | source = "git+https://github.com/irrustible/futures-many?branch=main#540c986c0c6b0447ba5337a253f41b9a55dc7820" 117 | dependencies = [ 118 | "futures-core", 119 | "futures-micro", 120 | "primitive_traits 0.2.0", 121 | "rle-bitset", 122 | ] 123 | 124 | [[package]] 125 | name = "futures-micro" 126 | version = "0.2.0" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "765775ce35a7751af578419776aca3235658be6d89849c178c1b556d6db7aa54" 129 | 130 | [[package]] 131 | name = "libc" 132 | version = "0.2.74" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" 135 | 136 | [[package]] 137 | name = "maybe-unwind" 138 | version = "0.3.1" 139 | source = "registry+https://github.com/rust-lang/crates.io-index" 140 | checksum = "8f8f264861cca5d0943a472622980bf8624cc7ec2087a3c8d26b837ae92613cd" 141 | dependencies = [ 142 | "futures-core", 143 | ] 144 | 145 | [[package]] 146 | name = "memchr" 147 | version = "2.3.3" 148 | source = "registry+https://github.com/rust-lang/crates.io-index" 149 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 150 | 151 | [[package]] 152 | name = "once_cell" 153 | version = "1.4.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "0b631f7e854af39a1739f401cf34a8a013dfe09eac4fa4dba91e9768bd28168d" 156 | 157 | [[package]] 158 | name = "parking" 159 | version = "1.0.6" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "6cb300f271742d4a2a66c01b6b2fa0c83dfebd2e0bf11addb879a3547b4ed87c" 162 | 163 | [[package]] 164 | name = "pin-project-lite" 165 | version = "0.1.7" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715" 168 | 169 | [[package]] 170 | name = "primitive_traits" 171 | version = "0.1.0" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "8f04c8d9cad48dd24ba746a3a9e0aa7dc80199eecf78e5d930917eb23d29023d" 174 | 175 | [[package]] 176 | name = "primitive_traits" 177 | version = "0.2.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "ff1fce8f7e64aa6d866737c60f38b47dfc5863303bf7e2f68748db118d820eb3" 180 | 181 | [[package]] 182 | name = "redox_syscall" 183 | version = "0.1.57" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 186 | 187 | [[package]] 188 | name = "ring-vec" 189 | version = "0.1.0" 190 | source = "git+https://github.com/irrustible/ring-vec?branch=main#74379581dd5bebcfb2b89ff83265e6225fe61791" 191 | 192 | [[package]] 193 | name = "rle-bitset" 194 | version = "0.1.0" 195 | source = "registry+https://github.com/rust-lang/crates.io-index" 196 | checksum = "2e1a58810d7d14aa63589fb56bb35dea4f571357d9f8855a15daba781865b195" 197 | dependencies = [ 198 | "primitive_traits 0.1.0", 199 | ] 200 | 201 | [[package]] 202 | name = "simple-rate-limit" 203 | version = "0.1.0" 204 | source = "git+https://github.com/irrustible/simple-rate-limit?branch=main#3e1b0c527f676510cc39131a12a5a1a89f1674a2" 205 | dependencies = [ 206 | "ring-vec", 207 | ] 208 | 209 | [[package]] 210 | name = "socket2" 211 | version = "0.3.12" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" 214 | dependencies = [ 215 | "cfg-if", 216 | "libc", 217 | "redox_syscall", 218 | "winapi", 219 | ] 220 | 221 | [[package]] 222 | name = "vec-arena" 223 | version = "0.5.0" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "17dfb54bf57c9043f4616cb03dab30eff012cc26631b797d8354b916708db919" 226 | 227 | [[package]] 228 | name = "waker-fn" 229 | version = "1.0.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "9571542c2ce85ce642e6b58b3364da2fb53526360dfb7c211add4f5c23105ff7" 232 | 233 | [[package]] 234 | name = "waker-queue" 235 | version = "0.1.0" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "ab74d1a40ba941c730e4dbc71ae1be4a61df69669b5a3a876913fd9a1c1aceb5" 238 | dependencies = [ 239 | "atomic-waker", 240 | "concurrent-queue", 241 | "pin-project-lite", 242 | ] 243 | 244 | [[package]] 245 | name = "wepoll-sys-stjepang" 246 | version = "1.0.6" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "6fd319e971980166b53e17b1026812ad66c6b54063be879eb182342b55284694" 249 | dependencies = [ 250 | "cc", 251 | ] 252 | 253 | [[package]] 254 | name = "winapi" 255 | version = "0.3.9" 256 | source = "registry+https://github.com/rust-lang/crates.io-index" 257 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 258 | dependencies = [ 259 | "winapi-i686-pc-windows-gnu", 260 | "winapi-x86_64-pc-windows-gnu", 261 | ] 262 | 263 | [[package]] 264 | name = "winapi-i686-pc-windows-gnu" 265 | version = "0.4.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 268 | 269 | [[package]] 270 | name = "winapi-x86_64-pc-windows-gnu" 271 | version = "0.4.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 274 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-supervisor 2 | 3 | 4 | 5 | 6 | 7 | Erlang-style Supervisors for 8 | [async-backplane](https://github.com/irrustible/async-backplane). 9 | 10 | ## Status: alpha 11 | 12 | It's built on battle tested principles, but the implementation is 13 | brand new and currently without tests. 14 | 15 | Currently unimplemented (contributions welcome!): 16 | 17 | * Startup with `Haste::Quickly` - panics. 18 | * The entire test suite, lol. 19 | 20 | ## Guide 21 | 22 | I wanted to bring erlang style reliability to rust, so I wrote 23 | [async-backplane](https://github.com/irrustible/async-backplane), a 24 | fabulous way of building reliable systems in the erlang style. But 25 | it was only the building blocks, not the full package. 26 | 27 | To build erlang style systems needs supervisors. These are my 28 | backplane adaptations of the best erlang/elixir ones. 29 | 30 | If you haven't read the 31 | [async-backplane](https://github.com/irrustible/async-backplane) 32 | README, you will want to do that before you continue! 33 | 34 | ### What is a Supervisor, anyway? 35 | 36 | A `Supervisor` is a Future responsible for starting and managing tasks 37 | (Device-holding Futures spawned on an executor). It starts up all of 38 | its tasks and attempts to recover from the failure of one of them by 39 | restarting it and potentially its peers, according to the provided 40 | configuration. We control which tasks are restarted by selecting a 41 | `RecoveryLogic`, reproduced below: 42 | 43 | ```rust 44 | pub enum RecoveryLogic { 45 | /// No other tasks will be restarted. 46 | Isolated, 47 | /// Tasks started after this one will be restarted. 48 | CascadeNewer, 49 | /// All tasks will be restarted. 50 | CascadeAll, 51 | } 52 | ``` 53 | 54 | We then choose a `RateLimit` for how often restarts are allowed to 55 | happen before the `Supervisor` gives up trying to restart things and 56 | disconnects itself. We create a supervisor thus: 57 | 58 | ```rust 59 | use async_supervisor::{RateLimit, RecoveryLogic, Supervisor}; 60 | 61 | fn my_sup() -> Supervisor { 62 | // One task failing does not affect any others. 63 | Supervisor::new(RecoveryLogic::Isolated) 64 | } 65 | ``` 66 | 67 | The supervisor defaults to a `restart_rate` of 5 restarts within 5 68 | seconds. This means that on the sixth restart within 5 seconds, the 69 | supervisor will abort trying to restart tasks and will 70 | disconnect. This can be customed by providing a new `RateLimit` to 71 | `Supervisor.set_restart_rate()`. 72 | 73 | A supervisor with no tasks isn't much use, however. We describe tasks 74 | by creating a `Spec`, a pairing of a boxed function to spawn it with 75 | some configuration about how to manage it. We'll cover configuration 76 | in a minute, but first let's explain that boxed function. 77 | 78 | If we're going to support restarting tasks, we need to have some 79 | concept of a lifecycle those tasks must obey. Ours is very simple - it 80 | first performs startup work and then it runs. We separate things into 81 | two phases because supervisors have the option to perform an orderly 82 | startup, where we wait for each task to start up before going on to 83 | start the next task. 84 | 85 | In the event that during startup, one of the tasks fails to start, the 86 | supervisor will shut down with a success status. Its supervisor will 87 | then restart it only if it is configured to `Always` restart it. 88 | 89 | Now come some rather wordy types that are actually quite simple: 90 | 91 | ```rust 92 | pub type StartFn = Box Starting>; 93 | pub type Starting = Box> + Unpin>; 94 | ``` 95 | 96 | `StartFn` is a boxed function from `Device` to `Starting`. It's boxed 97 | so we can start different tasks under the same `Supervisor`. 98 | `Starting` is mostly wordy because we're specifying the `Future`'s 99 | output. It's also boxed, for the same reason. 100 | 101 | If we ignore the boxing for a moment, this would be a suitable start 102 | function: 103 | 104 | ```rust 105 | async fn start_fn(device: Device) -> Result { ... } 106 | ``` 107 | 108 | The reason is that `async fn` is just syntax sugar over a `Fn` 109 | returning a `Future`. The type of this function would be this, if we 110 | could write it this way: 111 | 112 | ```rust 113 | Fn(Device) -> impl Future> + Unpin 114 | ``` 115 | 116 | So ours is just the version of that with the added boxes. The future 117 | that is returned should complete when the task has successfully 118 | completed its startup work. It should return a `Started`: 119 | 120 | ```rust 121 | pub enum Started { 122 | /// We've done our work and we don't need to keep running. 123 | Completed, 124 | /// We've started up successfully. 125 | Running, 126 | } 127 | ``` 128 | 129 | Let's write a simple start fn that doesn't need to do anything to 130 | start up: 131 | 132 | ```rust 133 | use async_backplane::Device; 134 | use async_supervisor::{StartFn, Starting}; 135 | use smol::Task; // A simple futures executor. 136 | use futures_micro::ready; 137 | 138 | fn start(device: Device) -> Starting { 139 | // Start the task. 140 | Task::spawn(async { // How you spawn in smol. 141 | // Go straight into managed mode, in this case 142 | // just completing successfully. 143 | device.manage(|| Ok(())) 144 | }).detach(); 145 | // Return the future for the supervisor to wait on. `ready()` 146 | // just immediately succeeds with the provided value 147 | Box::new(ready(Started::Running)) // Not for very long, ha! 148 | } 149 | 150 | ``` 151 | 152 | And here's one that has a startup phase: 153 | 154 | ```rust 155 | use async_backplane::Device; 156 | use async_supervisor::{StartFn, Starting}; 157 | use smol::Task; 158 | use async_oneshot::oneshot; // A simple oneshot channel. 159 | 160 | fn start(device: Device) -> Starting { 161 | // Create a channel for the device to signal us on. 162 | let (send, recv) = oneshot(); 163 | Task::spawn(async { 164 | // ... startup work goes here ... 165 | // Announce we're all good. 166 | send.send(Ok(Started::Running)).unwrap(); 167 | // Now go into managed mode. You should probably unwrap 168 | // the result it returns instead of ignoring it. 169 | device.manage(|| Ok(())).await; 170 | }).detach(); 171 | Box::new(recv) 172 | } 173 | ``` 174 | 175 | Now let's tie everything together - creating a supervisor with a 176 | single task and running it: 177 | 178 | ```rust 179 | use async_supervisor::{RateLimit, RecoveryLogic, Spec, Supervisor}; 180 | use smol::Task; // A simple Futures executor. 181 | use async_oneshot::oneshot; 182 | 183 | async fn my_sup(device: Device) { 184 | // This is the code from the last example. 185 | let limit = RateLimit::new(5, Duration::from_secs(5)); 186 | let mut sup = Supervisor::new(RecoveryLogic::Isolated, limit); 187 | sup.add_task(Spec::new(Start::new(Box::new(start)))); // function from last example. 188 | sup.supervise().await; // you should check the result. 189 | } 190 | ``` 191 | 192 | We didn't change any of the default options here, but we should cover 193 | what they are. Firstly the `Start` object we create has the option to 194 | set a grace period for startup other than the default (5 seconds) with 195 | the `set_grace` method. Most of the options are on the `Spec` though: 196 | 197 | ```rust 198 | pub struct Spec { 199 | pub start: Start, 200 | pub restart: Restart, 201 | pub shutdown: Haste, 202 | } 203 | ``` 204 | 205 | `Restart` is a simple enum that tells the supervisor when to restart 206 | this task: 207 | 208 | ```rust 209 | /// When should a supervisor restart a child? 210 | pub enum Restart { 211 | /// Do not restart: this is only supposed to run once. 212 | Never, 213 | /// We won't restart it if it succeeds. 214 | Failed, 215 | /// Restart even if it succeeds. 216 | Always, 217 | } 218 | ``` 219 | 220 | `Haste` describes how much time we give a task to start up or shut 221 | down.We can either wait for it for some (potentially infinite) grace 222 | period or we can just assume it succeeded and carry on: 223 | 224 | ```rust 225 | /// How should a task be restarted? 226 | pub enum Haste { 227 | /// We will wait for it to end before we continue. 228 | Gracefully(Grace), 229 | /// We will assume it to have disconnected and continue our work. 230 | Quickly, 231 | } 232 | 233 | /// A period of time permitted for a startup/shutdown to occur. 234 | pub enum Grace { 235 | /// A fixed period of time. 236 | Fixed(Duration), 237 | /// As long as needed. Mainly for supervisors. Be very careful! 238 | Forever, 239 | } 240 | ``` 241 | 242 | You can set `restart` and `shutdown` with the `set_restart` and 243 | `set_shutdown` methods on `Spec`. 244 | 245 | 246 | 247 | 248 | TODO: describe interactions. 249 | 250 | ## Differences from Erlang Supervisors 251 | 252 | Obviously, being built in rust, we already have to diverge somewhat 253 | from Erlang. We rely on a rust adaptation of the erlang principles, 254 | [async-backplane](https://github.com/irrustible/async-backplane), 255 | which loosely resembles the basic erlang environment. 256 | 257 | The obvious difference, therefore, is types. We have rearranged the 258 | structure of things to feel more natural in rust. In particular we do 259 | not distinguish between 'worker' and 'supervisor' processes - the user 260 | simply configures appropriate grace periods for their tasks. 261 | 262 | Because we don't control task spawning, we don't maintain the ability 263 | to terminate a task. We therefore rely on spawned tasks to obey a 264 | contract in order to guarantee we work correctly. 265 | 266 | ## Copyright and License 267 | 268 | Copyright (c) 2020 James Laver, async-supervisor Contributors 269 | 270 | This Source Code Form is subject to the terms of the Mozilla Public 271 | License, v. 2.0. If a copy of the MPL was not distributed with this 272 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 273 | 274 | -------------------------------------------------------------------------------- /src/supervisor.rs: -------------------------------------------------------------------------------- 1 | //! A Supervisor where any process being restarted causes all the 2 | //! others to restart. 3 | use crate::*; 4 | use async_backplane::prelude::*; 5 | use async_io::Timer; 6 | use futures_lite::*; 7 | use futures_many::Many; 8 | use simple_rate_limit::{RateLimit, RateLimiter}; 9 | 10 | /// A one-for-one Supervisor 11 | pub struct Supervisor { 12 | pub logic: RecoveryLogic, 13 | pub restart_rate: RateLimit, 14 | specs: Vec, 15 | states: Vec>, 16 | } 17 | 18 | impl Supervisor { 19 | pub fn new(logic: RecoveryLogic) -> Supervisor { 20 | Supervisor { 21 | logic, 22 | restart_rate: RateLimit::new(5, Duration::from_secs(5)).unwrap(), 23 | specs: Vec::new(), 24 | states: Vec::new(), 25 | } 26 | } 27 | 28 | pub fn set_restart_rate(mut self, restart_rate: RateLimit) -> Self { 29 | self.restart_rate = restart_rate; 30 | self 31 | } 32 | 33 | pub fn add_task(&mut self, spec: Spec) { 34 | self.specs.push(spec); 35 | } 36 | 37 | pub async fn supervise(mut self, mut device: Device) -> Result<(), Crash> { 38 | if let Err(crash) = self.start_up(&mut device, 0).await { 39 | self.shut_down(&mut device, 0).await; 40 | device.disconnect(None); 41 | Err(crash) 42 | } else { 43 | self.watch(device).await.map(|_| ()) 44 | } 45 | } 46 | 47 | async fn start_up( 48 | &mut self, 49 | device: &mut Device, 50 | start_index: usize, 51 | ) -> Result<(), Crash> { 52 | for index in start_index..self.specs.len() { 53 | match self.start_link(device, index).await { 54 | Ok(line) => { 55 | self.states.push(line); 56 | } 57 | Err(error) => { 58 | return Err(error); 59 | } 60 | } 61 | } 62 | Ok(()) 63 | } 64 | 65 | async fn start_link( 66 | &mut self, 67 | device: &mut Device, 68 | index: usize, 69 | ) -> Result, Crash> { 70 | let d = Device::new(); 71 | device.link(&d, LinkMode::Monitor); 72 | let line = d.line(); 73 | self.specs[index] 74 | .start 75 | .start(d) 76 | .await 77 | .map_err(|e| Crash::Error(SupervisionError::StartupFailed(index, e))) 78 | .map(|s| match s { 79 | Started::Completed => None, 80 | Started::Running => Some(line), 81 | }) 82 | } 83 | 84 | async fn watch(&mut self, device: Device) -> Result<(), Crash> { 85 | let mut limiter = RateLimiter::new(self.restart_rate); 86 | let mut device = device; 87 | while let Some(message) = device.next().await { 88 | match message { 89 | Shutdown(id) => { 90 | device.disconnect(None); 91 | return Err(Crash::PowerOff(id)); 92 | } 93 | Disconnected(id, result) => { 94 | let ret = self 95 | .disconnected(&mut device, id, result, &mut limiter) 96 | .await; 97 | if let Err(crash) = ret { 98 | return Err(crash); 99 | } 100 | } 101 | } 102 | } 103 | Ok(()) // Not found 104 | } 105 | 106 | async fn disconnected( 107 | &mut self, 108 | device: &mut Device, 109 | id: DeviceID, 110 | result: Option, 111 | limiter: &mut RateLimiter, 112 | ) -> Result<(), Crash> { 113 | for index in 0..self.states.len() { 114 | let state = &self.states[index]; 115 | if let Some(running) = state { 116 | if running.device_id() == id { 117 | return self.handle_restart(device, index, result, limiter).await; 118 | } 119 | } 120 | } 121 | Ok(()) 122 | } 123 | 124 | async fn handle_restart( 125 | &mut self, 126 | device: &mut Device, 127 | index: usize, 128 | result: Option, 129 | limiter: &mut RateLimiter, 130 | ) -> Result<(), Crash> { 131 | self.states[index].take().unwrap(); 132 | match self.specs[index].restart { 133 | Restart::Never => Ok(()), 134 | Restart::Always => self.restart(device, index, limiter).await, 135 | Restart::Failed => { 136 | if result.is_some() { 137 | self.restart(device, index, limiter).await 138 | } else { 139 | Ok(()) 140 | } 141 | } 142 | } 143 | } 144 | 145 | async fn restart( 146 | &mut self, 147 | device: &mut Device, 148 | index: usize, 149 | limiter: &mut RateLimiter, 150 | ) -> Result<(), Crash> { 151 | if limiter.check() { 152 | match self.logic { 153 | RecoveryLogic::Isolated => match self.start_link(device, index).await { 154 | Ok(line) => { 155 | self.states[index] = line; 156 | Ok(()) 157 | } 158 | Err(crash) => { 159 | self.shut_down(device, 0).await; 160 | Err(crash) 161 | } 162 | }, 163 | RecoveryLogic::CascadeNewer => { 164 | self.shut_down(device, index + 1).await; 165 | self.start_up(device, index).await 166 | } 167 | RecoveryLogic::CascadeAll => { 168 | self.shut_down(device, 0).await; 169 | self.start_up(device, 0).await 170 | } 171 | } 172 | } else { 173 | Err(Crash::Error(SupervisionError::Throttled)) 174 | } 175 | } 176 | 177 | async fn shut_down(&mut self, device: &mut Device, start_index: usize) { 178 | let mut waiting: Vec> = Vec::new(); 179 | let mut timers = Many::new(); 180 | self.start_shut_down(device.device_id(), start_index, &mut waiting, &mut timers) 181 | .await; 182 | let mut needed = waiting.len(); 183 | while needed > 0 { 184 | match self.next_shutdown_message(device, &mut timers).await { 185 | ShuttingDown::Remove(id) => { 186 | for x in waiting.iter_mut() { 187 | if let Some(y) = x { 188 | if *y == id { 189 | *x = None; 190 | needed -= 1; 191 | } 192 | } 193 | } 194 | // not found, ignore 195 | } 196 | ShuttingDown::DoneWaiting => { 197 | break; 198 | } 199 | ShuttingDown::Done => { 200 | return; 201 | } 202 | } 203 | } 204 | while needed > 0 { 205 | if let Some(message) = device.next().await { 206 | if let Message::Disconnected(id, _) = message { 207 | for x in waiting.iter_mut() { 208 | if let Some(y) = x { 209 | if *y == id { 210 | *x = None; 211 | needed -= 1; 212 | } 213 | } 214 | } 215 | } //ignore shutdown requests 216 | } else { 217 | return; 218 | } // Well the Device things there are no more left, what to do? unreachable? 219 | } 220 | } 221 | 222 | async fn start_shut_down( 223 | &mut self, 224 | my_id: DeviceID, 225 | start_index: usize, 226 | waiting: &mut Vec>, 227 | timers: &mut Many>, 228 | ) { 229 | for (i, state) in self.states.drain(start_index..).enumerate().rev() { 230 | let index = i + start_index; 231 | if let Some(line) = state { 232 | let id = line.device_id(); 233 | #[allow(unused_must_use)] 234 | match self.specs[index].shutdown { 235 | Haste::Quickly => { 236 | line.send(Shutdown(my_id)); 237 | } 238 | Haste::Gracefully(Grace::Forever) => { 239 | waiting.push(Some(line.device_id())); 240 | line.send(Shutdown(my_id)); 241 | } 242 | Haste::Gracefully(Grace::Fixed(when)) => { 243 | waiting.push(Some(line.device_id())); 244 | timers.push(timer(when, id).boxed()); 245 | line.send(Shutdown(my_id)); 246 | } 247 | } 248 | } 249 | } 250 | } 251 | 252 | async fn next_shutdown_message( 253 | &mut self, 254 | device: &mut Device, 255 | timers: &mut Many>, 256 | ) -> ShuttingDown { 257 | loop { 258 | let ret = async { Ok(device.next().await) } 259 | .or(async { Err(timers.next().await) }) 260 | .await; 261 | match ret { 262 | Ok(Some(Message::Disconnected(id, _))) => { 263 | return ShuttingDown::Remove(id); 264 | } 265 | Err(Some(id)) => { 266 | return ShuttingDown::Remove(id); 267 | } 268 | Ok(None) => { 269 | return ShuttingDown::Done; 270 | } 271 | Err(None) => { 272 | return ShuttingDown::DoneWaiting; 273 | } 274 | _ => (), // ignore, carry on 275 | } 276 | } 277 | } 278 | } 279 | 280 | enum ShuttingDown { 281 | Remove(DeviceID), 282 | DoneWaiting, 283 | Done, 284 | } 285 | 286 | async fn timer(when: Duration, id: DeviceID) -> DeviceID { 287 | Timer::new(when).await; 288 | id 289 | } 290 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | --------------------------------------------------------------------------------