Skip to content

Commit aa24d24

Browse files
committed
Use SyncUnsafeCell to replace use of static mut
1 parent 0735f79 commit aa24d24

19 files changed

+489
-346
lines changed

examples/dma.rs

Lines changed: 40 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
use core::mem::MaybeUninit;
88

9+
// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439
10+
use utilities::sync_unsafe_cell::SyncUnsafeCell;
11+
912
use cortex_m_rt::entry;
1013
#[macro_use]
1114
mod utilities;
@@ -24,9 +27,11 @@ use log::info;
2427
//
2528
// The runtime does not initialise these SRAM banks.
2629
#[link_section = ".sram4.buffers"]
27-
static mut SOURCE_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit();
30+
static SOURCE_BUFFER: MaybeUninit<SyncUnsafeCell<[u32; 20]>> =
31+
MaybeUninit::uninit();
2832
#[link_section = ".axisram.buffers"]
29-
static mut TARGET_BUFFER: MaybeUninit<[u32; 20]> = MaybeUninit::uninit();
33+
static TARGET_BUFFER: MaybeUninit<SyncUnsafeCell<[u32; 20]>> =
34+
MaybeUninit::uninit();
3035

3136
#[entry]
3237
fn main() -> ! {
@@ -53,29 +58,37 @@ fn main() -> ! {
5358
info!("stm32h7xx-hal example - Memory to Memory DMA");
5459
info!("");
5560

56-
// Initialise the source buffer with truly random data, without taking any
57-
// references to uninitialisated memory
58-
let source_buffer: &'static mut [u32; 20] = {
59-
let buf: &mut [MaybeUninit<u32>; 20] = unsafe {
60-
&mut *(core::ptr::addr_of_mut!(SOURCE_BUFFER)
61-
as *mut [MaybeUninit<u32>; 20])
62-
};
63-
64-
for value in buf.iter_mut() {
65-
unsafe {
66-
value.as_mut_ptr().write(rng.gen().unwrap());
67-
}
68-
}
69-
#[allow(static_mut_refs)] // TODO: Fix this
70-
unsafe {
71-
SOURCE_BUFFER.assume_init_mut()
61+
// SOURCE_BUFFER is located in .axisram.buffers, which is not initialized by
62+
// the runtime. We must manually initialize it to a valid value, without
63+
// taking a reference to the uninitialized value
64+
unsafe {
65+
let cell = SOURCE_BUFFER.as_ptr();
66+
for i in 0..20 {
67+
core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i])
68+
.write(rng.gen().unwrap());
7269
}
73-
};
70+
}
71+
// Now we can take a mutable reference to SOURCE_BUFFER. To avoid aliasing,
72+
// this reference must only be taken once
73+
let source_buffer =
74+
unsafe { &mut *SyncUnsafeCell::raw_get(SOURCE_BUFFER.as_ptr()) };
7475
// Save a copy on the stack so we can check it later
7576
let source_buffer_cloned = *source_buffer;
7677

77-
// NOTE(unsafe): TARGET_BUFFER must also be initialised to prevent undefined
78-
// behaviour (taking a mutable reference to uninitialised memory)
78+
// TARGET_BUFFER is located in .axisram.buffers, which is not initialized by
79+
// the runtime. We must manually initialize it to a valid value, without
80+
// taking a reference to the uninitialized value
81+
unsafe {
82+
let cell = TARGET_BUFFER.as_ptr();
83+
for i in 0..20 {
84+
core::ptr::addr_of_mut!((*SyncUnsafeCell::raw_get(cell))[i])
85+
.write(0);
86+
}
87+
}
88+
// Now we can take a mutable reference to TARGET_BUFFER. To avoid aliasing,
89+
// this reference must only be taken once
90+
let target_buffer =
91+
unsafe { &mut *SyncUnsafeCell::raw_get(TARGET_BUFFER.as_ptr()) };
7992

8093
// Setup DMA
8194
//
@@ -92,9 +105,7 @@ fn main() -> ! {
92105
Transfer::init(
93106
streams.4,
94107
MemoryToMemory::new(),
95-
unsafe {
96-
(*core::ptr::addr_of_mut!(TARGET_BUFFER)).assume_init_mut()
97-
}, // Uninitialised memory
108+
target_buffer,
98109
Some(source_buffer),
99110
config,
100111
);
@@ -104,10 +115,11 @@ fn main() -> ! {
104115
// Wait for transfer to complete
105116
while !transfer.get_transfer_complete_flag() {}
106117

107-
// Now the target memory is actually initialised
108-
#[allow(static_mut_refs)] // TODO: Fix this
109-
let target_buffer: &'static mut [u32; 20] =
110-
unsafe { TARGET_BUFFER.assume_init_mut() };
118+
// Take a second(!) reference to the target buffer. Because the transfer has
119+
// stopped, we can reason that the first reference is no longer
120+
// active. Therefore we can take another reference without causing aliasing
121+
let target_buffer: &[u32; 20] =
122+
unsafe { &*SyncUnsafeCell::raw_get(TARGET_BUFFER.as_ptr()) };
111123

112124
// Comparison check
113125
assert_eq!(&source_buffer_cloned, target_buffer);

examples/ethernet-nucleo-h743zi2.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ use core::mem::MaybeUninit;
1717
use core::sync::atomic::{AtomicU32, Ordering};
1818
use rt::{entry, exception};
1919

20+
// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439
21+
use utilities::sync_unsafe_cell::SyncUnsafeCell;
22+
2023
extern crate cortex_m;
2124

2225
#[macro_use]
@@ -52,7 +55,7 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44];
5255

5356
/// Ethernet descriptor rings are a global singleton
5457
#[link_section = ".sram3.eth"]
55-
static mut DES_RING: MaybeUninit<ethernet::DesRing<4, 4>> =
58+
static DES_RING: MaybeUninit<SyncUnsafeCell<ethernet::DesRing<4, 4>>> =
5659
MaybeUninit::uninit();
5760

5861
// the program entry point
@@ -113,9 +116,18 @@ fn main() -> ! {
113116
assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz
114117

115118
let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS);
116-
let (_eth_dma, eth_mac) = unsafe {
117-
#[allow(static_mut_refs)] // TODO: Fix this
118-
DES_RING.write(ethernet::DesRing::new());
119+
let (_eth_dma, eth_mac) = {
120+
// DES_RING is located in .axisram.eth, which is not initialized by
121+
// the runtime. We must manually initialize it to a valid value,
122+
// without taking a reference to the uninitialized value
123+
unsafe {
124+
SyncUnsafeCell::raw_get(DES_RING.as_ptr())
125+
.write(ethernet::DesRing::new());
126+
}
127+
// Now we can take a mutable reference to DES_RING. To avoid
128+
// aliasing, this reference must only be taken once
129+
let des_ring =
130+
unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) };
119131

120132
ethernet::new(
121133
dp.ETHERNET_MAC,
@@ -132,8 +144,7 @@ fn main() -> ! {
132144
rmii_txd0,
133145
rmii_txd1,
134146
),
135-
#[allow(static_mut_refs)] // TODO: Fix this
136-
DES_RING.assume_init_mut(),
147+
des_ring,
137148
mac_addr,
138149
ccdr.peripheral.ETH1MAC,
139150
&ccdr.clocks,

examples/ethernet-rtic-nucleo-h723zg.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
mod utilities;
2121

2222
use core::mem::MaybeUninit;
23-
use core::ptr::addr_of_mut;
2423
use core::sync::atomic::AtomicU32;
2524

25+
// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439
26+
use utilities::sync_unsafe_cell::SyncUnsafeCell;
27+
2628
use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage};
2729
use smoltcp::time::Instant;
2830
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};
@@ -49,17 +51,19 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44];
4951

5052
/// Ethernet descriptor rings are a global singleton
5153
#[link_section = ".axisram.eth"]
52-
static mut DES_RING: MaybeUninit<ethernet::DesRing<4, 4>> =
54+
static DES_RING: MaybeUninit<SyncUnsafeCell<ethernet::DesRing<4, 4>>> =
5355
MaybeUninit::uninit();
5456

5557
/// Net storage with static initialisation - another global singleton
58+
#[derive(Default)]
5659
pub struct NetStorageStatic<'a> {
5760
socket_storage: [SocketStorage<'a>; 8],
5861
}
5962

6063
// MaybeUninit allows us write code that is correct even if STORE is not
6164
// initialised by the runtime
62-
static mut STORE: MaybeUninit<NetStorageStatic> = MaybeUninit::uninit();
65+
static STORE: MaybeUninit<SyncUnsafeCell<NetStorageStatic>> =
66+
MaybeUninit::uninit();
6367

6468
pub struct Net<'a> {
6569
iface: Interface,
@@ -163,9 +167,18 @@ mod app {
163167
assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz
164168

165169
let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS);
166-
let (eth_dma, eth_mac) = unsafe {
167-
#[allow(static_mut_refs)] // TODO: Fix this
168-
DES_RING.write(ethernet::DesRing::new());
170+
let (eth_dma, eth_mac) = {
171+
// DES_RING is located in .axisram.eth, which is not initialized by
172+
// the runtime. We must manually initialize it to a valid value,
173+
// without taking a reference to the uninitialized value
174+
unsafe {
175+
SyncUnsafeCell::raw_get(DES_RING.as_ptr())
176+
.write(ethernet::DesRing::new());
177+
}
178+
// Now we can take a mutable reference to DES_RING. To avoid
179+
// aliasing, this reference must only be taken once
180+
let des_ring =
181+
unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) };
169182

170183
ethernet::new(
171184
ctx.device.ETHERNET_MAC,
@@ -182,8 +195,7 @@ mod app {
182195
rmii_txd0,
183196
rmii_txd1,
184197
),
185-
#[allow(static_mut_refs)] // TODO: Fix this
186-
DES_RING.assume_init_mut(),
198+
des_ring,
187199
mac_addr,
188200
ccdr.peripheral.ETH1MAC,
189201
&ccdr.clocks,
@@ -198,22 +210,14 @@ mod app {
198210

199211
unsafe { ethernet::enable_interrupt() };
200212

201-
// unsafe: mutable reference to static storage, we only do this once
202-
let store = unsafe {
203-
#[allow(static_mut_refs)] // TODO: Fix this
204-
let store_ptr = STORE.as_mut_ptr();
205-
206-
// Initialise the socket_storage field. Using `write` instead of
207-
// assignment via `=` to not call `drop` on the old, uninitialised
208-
// value
209-
addr_of_mut!((*store_ptr).socket_storage)
210-
.write([SocketStorage::EMPTY; 8]);
211-
212-
// Now that all fields are initialised we can safely use
213-
// assume_init_mut to return a mutable reference to STORE
214-
#[allow(static_mut_refs)] // TODO: Fix this
215-
STORE.assume_init_mut()
216-
};
213+
// Initialize STORE
214+
unsafe {
215+
SyncUnsafeCell::raw_get(STORE.as_ptr())
216+
.write(NetStorageStatic::default());
217+
}
218+
// Now we can take a mutable reference to STORE. To avoid aliasing, this
219+
// reference must only be taken once
220+
let store = unsafe { &mut *SyncUnsafeCell::raw_get(STORE.as_ptr()) };
217221

218222
let net = Net::new(store, eth_dma, mac_addr.into());
219223

examples/ethernet-rtic-stm32h735g-dk.rs

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
mod utilities;
1717

1818
use core::mem::MaybeUninit;
19-
use core::ptr::addr_of_mut;
2019
use core::sync::atomic::AtomicU32;
2120

21+
// TODO: use core::cell::SyncUnsafeCell when stabilized rust-lang/rust#95439
22+
use utilities::sync_unsafe_cell::SyncUnsafeCell;
23+
2224
use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage};
2325
use smoltcp::time::Instant;
2426
use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr};
@@ -45,16 +47,19 @@ const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44];
4547

4648
/// Ethernet descriptor rings are a global singleton
4749
#[link_section = ".axisram.eth"]
48-
static mut DES_RING: MaybeUninit<ethernet::DesRing<4, 4>> =
50+
static DES_RING: MaybeUninit<SyncUnsafeCell<ethernet::DesRing<4, 4>>> =
4951
MaybeUninit::uninit();
5052

5153
// This data will be held by Net through a mutable reference
54+
#[derive(Default)]
5255
pub struct NetStorageStatic<'a> {
5356
socket_storage: [SocketStorage<'a>; 8],
5457
}
58+
5559
// MaybeUninit allows us write code that is correct even if STORE is not
5660
// initialised by the runtime
57-
static mut STORE: MaybeUninit<NetStorageStatic> = MaybeUninit::uninit();
61+
static STORE: MaybeUninit<SyncUnsafeCell<NetStorageStatic>> =
62+
MaybeUninit::uninit();
5863

5964
pub struct Net<'a> {
6065
iface: Interface,
@@ -157,9 +162,18 @@ mod app {
157162
assert_eq!(ccdr.clocks.pclk4().raw(), 100_000_000); // PCLK 100MHz
158163

159164
let mac_addr = smoltcp::wire::EthernetAddress::from_bytes(&MAC_ADDRESS);
160-
let (eth_dma, eth_mac) = unsafe {
161-
#[allow(static_mut_refs)] // TODO: Fix this
162-
DES_RING.write(ethernet::DesRing::new());
165+
let (eth_dma, eth_mac) = {
166+
// DES_RING is located in .axisram.eth, which is not initialized by
167+
// the runtime. We must manually initialize it to a valid value,
168+
// without taking a reference to the uninitialized value
169+
unsafe {
170+
SyncUnsafeCell::raw_get(DES_RING.as_ptr())
171+
.write(ethernet::DesRing::new());
172+
}
173+
// Now we can take a mutable reference to DES_RING. To avoid
174+
// aliasing, this reference must only be taken once
175+
let des_ring =
176+
unsafe { &mut *SyncUnsafeCell::raw_get(DES_RING.as_ptr()) };
163177

164178
ethernet::new(
165179
ctx.device.ETHERNET_MAC,
@@ -176,8 +190,7 @@ mod app {
176190
rmii_txd0,
177191
rmii_txd1,
178192
),
179-
#[allow(static_mut_refs)] // TODO: Fix this
180-
DES_RING.assume_init_mut(),
193+
des_ring,
181194
mac_addr,
182195
ccdr.peripheral.ETH1MAC,
183196
&ccdr.clocks,
@@ -192,22 +205,14 @@ mod app {
192205

193206
unsafe { ethernet::enable_interrupt() };
194207

195-
// unsafe: mutable reference to static storage, we only do this once
196-
let store = unsafe {
197-
#[allow(static_mut_refs)] // TODO: Fix this
198-
let store_ptr = STORE.as_mut_ptr();
199-
200-
// Initialise the socket_storage field. Using `write` instead of
201-
// assignment via `=` to not call `drop` on the old, uninitialised
202-
// value
203-
addr_of_mut!((*store_ptr).socket_storage)
204-
.write([SocketStorage::EMPTY; 8]);
205-
206-
// Now that all fields are initialised we can safely use
207-
// assume_init_mut to return a mutable reference to STORE
208-
#[allow(static_mut_refs)] // TODO: Fix this
209-
STORE.assume_init_mut()
210-
};
208+
// Initialize STORE
209+
unsafe {
210+
SyncUnsafeCell::raw_get(STORE.as_ptr())
211+
.write(NetStorageStatic::default());
212+
}
213+
// Now we can take a mutable reference to STORE. To avoid aliasing, this
214+
// reference must only be taken once
215+
let store = unsafe { &mut *SyncUnsafeCell::raw_get(STORE.as_ptr()) };
211216

212217
let net = Net::new(store, eth_dma, mac_addr.into(), Instant::ZERO);
213218

0 commit comments

Comments
 (0)