|
What you want sounds similar to Rust's Mutex design pattern. In most languages, there's no link between a mutex and the resources it's protecting, but in the Rust standard library, a Mutex<T> contains the resources (the T), so that the only way to get access to these resources is to lock the mutex, and the borrow checker will not allow accessing them after the mutex is unlocked again. In your case, you could implement something like an InterruptDisabler<T>, and use it to wrap an object which has the methods to access these registers. When you wanted to access these registers, you would do something like "my_interrupt_disabler.disable_interrupts()", which would disable interrupts and return an InterruptDisablerGuard<T>, which then would you to access the T. Once that InterruptDisablerGuard<T> gets out of scope, the compiler automatically calls the drop() method from the Drop trait on it, and that method could enable interrupts again. You cannot access the inner object without going through the guard, and you cannot get the guard without disabling interrupts. And that's only one possible implementation. Another one would be to have something like an "interrupts disabled" token which is returned by a "disable interrupts" function, which is consumed (that is, destroyed) by an "enable interrupts" function, and which is !Send and !Sync (so it cannot be passed to another thread). The methods which access these registers would require you to give them a reference to the token, so they could only be called if you somehow obtained the token, which could only be done by disabling interrupts in the current thread. And for when you're within the interrupt handler itself, the low-level code which calls it could manufacture one of these tokens and pass a reference to it to the interrupt handler, so it would be able to access the registers without calling the "disable interrupts" function. Of course, both of these ideas still require you to decide which registers are safe to call with interrupts enabled, and which registers are not, when creating the objects which represent the register blocks. Rust can help the developer, but it's not a panacea. |
Say `some_sfr = 1` is safe but `some_sfr = (some_sfr & 0xf0) | 0x3` is not.
Token thing seems interesting, but generally, no code calls the interrupt handler, HW does that by itself (outside of the control of the language), so there would be no way to pass the token to the handler, it would have to be manufactured in the handler out of thin air (without disabling interrupts, because those are disabled automatically by HW). And I'd have to wrap all the non-atomic language constructs in some functions that would require the token, to have some enforced safety.
I guess the fearless concurrency thing is only for cases where the language controls the creation of threads.