diff --git a/src/concurrency/index.md b/src/concurrency/index.md index 54c00fa0..ef6f7a17 100644 --- a/src/concurrency/index.md +++ b/src/concurrency/index.md @@ -42,7 +42,7 @@ you can get away with such a simple approach this can be a great solution. Unlike non-embedded Rust, we will not usually have the luxury of creating heap allocations and passing references to that data into a newly-created -thread. Instead our interrupt handlers might be called at any time and must +thread. Instead, our interrupt handlers might be called at any time and must know how to access whatever shared memory we are using. At the lowest level, this means we must have _statically allocated_ mutable memory, which both the interrupt handler and the main code can refer to. @@ -124,7 +124,7 @@ fn timer() { } ``` -In this example we use `cortex_m::interrupt::free`, but other platforms will +In this example, we use `cortex_m::interrupt::free`, but other platforms will have similar mechanisms for executing code in a critical section. This is also the same as disabling interrupts, running some code, and then re-enabling interrupts. @@ -138,7 +138,7 @@ for two reasons: If `COUNTER` was being shared by multiple interrupt handlers that might _preempt_ each other, then each one might require a critical section as well. -This solves our immediate problem, but we're still left writing a lot of unsafe code which we need to carefully reason about, and we might be using critical sections needlessly. Since each critical section temporarily pauses interrupt processing, there is an associated cost of some extra code size and higher interrupt latency and jitter (interrupts may take longer to be processed, and the time until they are processed will be more variable). Whether this is a problem depends on your system, but in general we'd like to avoid it. +This solves our immediate problem, but we're still left writing a lot of unsafe code which we need to carefully reason about, and we might be using critical sections needlessly. Since each critical section temporarily pauses interrupt processing, there is an associated cost of some extra code size and higher interrupt latency and jitter (interrupts may take longer to be processed, and the time until they are processed will be more variable). Whether this is a problem depends on your system, but in general, we'd like to avoid it. It's worth noting that while a critical section guarantees no interrupts will fire, it does not provide an exclusivity guarantee on multi-core systems! The @@ -209,7 +209,7 @@ blocks which must be very carefully checked and are not ergonomic. Surely we can do better in Rust! We can abstract our counter into a safe interface which can be safely used -anywhere else in our code. For this example we'll use the critical-section +anywhere else in our code. For this example, we'll use the critical-section counter, but you could do something very similar with atomics. ```rust,ignore @@ -274,7 +274,7 @@ fn timer() { We've moved our `unsafe` code to inside our carefully-planned abstraction, and now our application code does not contain any `unsafe` blocks. -This design requires the application pass a `CriticalSection` token in: +This design requires that the application pass a `CriticalSection` token in: these tokens are only safely generated by `interrupt::free`, so by requiring one be passed in, we ensure we are operating inside a critical section, without having to actually do the lock ourselves. This guarantee is provided statically @@ -302,7 +302,7 @@ variables _must_ be Sync, since they can be accessed by multiple threads. To tell the compiler we have taken care that the `CSCounter` is in fact safe to share between threads, we implement the Sync trait explicitly. As with the previous use of critical sections, this is only safe on single-core platforms: -with multiple cores you would need to go to greater lengths to ensure safety. +with multiple cores, you would need to go to greater lengths to ensure safety. ## Mutexes @@ -383,8 +383,8 @@ to get a safe counter with no unsafe code at all! This is great for simple types like the `u32` of our counter, but what about more complex types which are not Copy? An extremely common example in an -embedded context is a peripheral struct, which generally are not Copy. -For that we can turn to `RefCell`. +embedded context is a peripheral struct, which generally is not Copy. +For that, we can turn to `RefCell`. ## Sharing Peripherals @@ -499,7 +499,7 @@ interrupt::free(|cs| { }); ``` -Finally we use `MY_GPIO` in a safe and concurrent fashion. The critical section +Finally, we use `MY_GPIO` in a safe and concurrent fashion. The critical section prevents the interrupt firing as usual, and lets us borrow the mutex. The `RefCell` then gives us an `&Option`, and tracks how long it remains borrowed - once that reference goes out of scope, the `RefCell` will be updated @@ -509,7 +509,7 @@ Since we can't move the `GPIOA` out of the `&Option`, we need to convert it to an `&Option<&GPIOA>` with `as_ref()`, which we can finally `unwrap()` to obtain the `&GPIOA` which lets us modify the peripheral. -If we need a mutable references to shared resources, then `borrow_mut` and `deref_mut` +If we need a mutable reference to a shared resource, then `borrow_mut` and `deref_mut` should be used instead. The following code shows an example using the TIM2 timer. ```rust,ignore @@ -587,7 +587,7 @@ primitives, and often interoperate with hardware features such as DMA engines. [FreeRTOS]: https://freertos.org/ [ChibiOS]: http://chibios.org/ -At the time of writing there are not many Rust RTOS examples to point to, +At the time of writing, there are not many Rust RTOS examples to point to, but it's an interesting area so watch this space! ## Multiple Cores