Skip to content

Commit 163b3e3

Browse files
committed
Implement a mutex.
Removes a bunch of static mut stuff. Does require the BIOS to implement compare-and-swap for us, because the CPU might not have it. Ran out of code space so I had to make the memory allocations larger. I think we now no-longer fit on a Neotron 32.
1 parent 3dbfca6 commit 163b3e3

File tree

10 files changed

+201
-33
lines changed

10 files changed

+201
-33
lines changed

Cargo.lock

Lines changed: 2 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ panic = "abort"
4141
panic = "abort"
4242

4343
[dependencies]
44-
neotron-common-bios = "0.10"
44+
neotron-common-bios = { version = "0.11.0-alpha", git = "https://github.com/neotron-compute/neotron-common-bios", branch = "extra-functions" }
4545
pc-keyboard = "0.7"
4646
r0 = "1.0"
4747
heapless = "0.7"

neotron-flash-0002.ld

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
MEMORY
2323
{
2424
/* The first 128 KiB is for the BIOS. We get the rest. */
25-
FLASH (rx) : ORIGIN = 0x00020000, LENGTH = 128K
25+
FLASH (rx) : ORIGIN = 0x00020000, LENGTH = 256K
2626
/*
2727
* We get the bottom 4KB of RAM. Anything above that is for applications
2828
* (up to wherever the BIOS tells us we can use.)

neotron-flash-0802.ld

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
MEMORY
2323
{
2424
/* The first 128 KiB is for the BIOS. We get the rest. */
25-
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 128K
25+
FLASH (rx) : ORIGIN = 0x08020000, LENGTH = 256K
2626
/*
2727
* We get the bottom 4KB of RAM. Anything above that is for applications
2828
* (up to wherever the BIOS tells us we can use.)

neotron-flash-1002.ld

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
MEMORY
2323
{
2424
/* The first 128 KiB is for the BIOS. We get the rest. */
25-
FLASH (rx) : ORIGIN = 0x10020000, LENGTH = 128K
25+
FLASH (rx) : ORIGIN = 0x10020000, LENGTH = 256K
2626

2727
/*
2828
* The RAM reserved for the OS. Above this is the Transient Program Area.

src/commands/input.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub static KBTEST_ITEM: menu::Item<Ctx> = menu::Item {
1515
fn kbtest(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
1616
osprintln!("Press ESC to quit");
1717
loop {
18-
if let Some(ev) = unsafe { crate::STD_INPUT.get_raw() } {
18+
if let Some(ev) = crate::STD_INPUT.lock().get_raw() {
1919
osprintln!("Event: {ev:?}");
2020
if ev == pc_keyboard::DecodedKey::RawKey(pc_keyboard::KeyCode::Escape)
2121
|| ev == pc_keyboard::DecodedKey::Unicode('\u{001b}')

src/commands/screen.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,16 @@ pub static MANDEL_ITEM: menu::Item<Ctx> = menu::Item {
4242

4343
/// Called when the "clear" command is executed.
4444
fn clear(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
45-
if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } {
46-
console.clear();
45+
let mut guard = VGA_CONSOLE.try_lock().unwrap();
46+
if let Some(vga_console) = guard.as_mut() {
47+
vga_console.clear();
4748
}
4849
}
4950

5051
/// Called when the "fill" command is executed.
5152
fn fill(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
52-
if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } {
53+
let mut guard = VGA_CONSOLE.try_lock().unwrap();
54+
if let Some(console) = guard.as_mut() {
5355
console.clear();
5456
let api = API.get();
5557
let mode = (api.video_get_mode)();
@@ -91,7 +93,8 @@ fn fill(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx:
9193
/// Called when the "bench" command is executed.
9294
fn bench(_menu: &menu::Menu<Ctx>, _item: &menu::Item<Ctx>, _args: &[&str], _ctx: &mut Ctx) {
9395
const NUM_CHARS: u64 = 1_000_000;
94-
if let Some(ref mut console) = unsafe { &mut VGA_CONSOLE } {
96+
let mut guard = VGA_CONSOLE.try_lock().unwrap();
97+
if let Some(console) = guard.as_mut() {
9598
let api = API.get();
9699
let start = (api.time_ticks_get)();
97100
console.clear();

src/lib.rs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,18 @@
1313
// ===========================================================================
1414

1515
use core::sync::atomic::{AtomicBool, Ordering};
16+
1617
use neotron_common_bios as bios;
1718

1819
mod commands;
1920
mod config;
2021
mod fs;
2122
mod program;
23+
mod refcell;
2224
mod vgaconsole;
2325

2426
pub use config::Config as OsConfig;
27+
use refcell::CsRefCell;
2528

2629
// ===========================================================================
2730
// Global Variables
@@ -37,18 +40,18 @@ const SECONDS_BETWEEN_UNIX_AND_NEOTRON_EPOCH: i64 = 946684800;
3740
static API: Api = Api::new();
3841

3942
/// We store our VGA console here.
40-
static mut VGA_CONSOLE: Option<vgaconsole::VgaConsole> = None;
43+
static VGA_CONSOLE: CsRefCell<Option<vgaconsole::VgaConsole>> = CsRefCell::new(None);
4144

4245
/// We store our VGA console here.
43-
static mut SERIAL_CONSOLE: Option<SerialConsole> = None;
46+
static SERIAL_CONSOLE: CsRefCell<Option<SerialConsole>> = CsRefCell::new(None);
4447

4548
/// Note if we are panicking right now.
4649
///
4750
/// If so, don't panic if a serial write fails.
4851
static IS_PANIC: AtomicBool = AtomicBool::new(false);
4952

5053
/// Our keyboard controller
51-
static mut STD_INPUT: StdInput = StdInput::new();
54+
static STD_INPUT: CsRefCell<StdInput> = CsRefCell::new(StdInput::new());
5255

5356
struct StdInput {
5457
keyboard: pc_keyboard::EventDecoder<pc_keyboard::layouts::AnyLayout>,
@@ -170,16 +173,20 @@ impl StdInput {
170173
#[macro_export]
171174
macro_rules! osprint {
172175
($($arg:tt)*) => {
173-
if let Some(ref mut console) = unsafe { &mut $crate::VGA_CONSOLE } {
174-
#[allow(unused)]
175-
use core::fmt::Write as _;
176-
write!(console, $($arg)*).unwrap();
177-
}
178-
if let Some(ref mut console) = unsafe { &mut $crate::SERIAL_CONSOLE } {
179-
#[allow(unused)]
180-
use core::fmt::Write as _;
181-
write!(console, $($arg)*).unwrap();
182-
}
176+
crate::VGA_CONSOLE.with(|guard| {
177+
if let Some(console) = guard.as_mut() {
178+
#[allow(unused)]
179+
use core::fmt::Write as _;
180+
write!(console, $($arg)*).unwrap();
181+
}
182+
}).unwrap();
183+
crate::SERIAL_CONSOLE.with(|guard| {
184+
if let Some(console) = guard.as_mut() {
185+
#[allow(unused)]
186+
use core::fmt::Write as _;
187+
write!(console, $($arg)*).unwrap();
188+
}
189+
}).unwrap();
183190
};
184191
}
185192

@@ -366,16 +373,17 @@ pub extern "C" fn os_main(api: &bios::Api) -> ! {
366373
height as isize,
367374
);
368375
vga.clear();
369-
unsafe {
370-
VGA_CONSOLE = Some(vga);
371-
}
376+
let mut guard = VGA_CONSOLE.try_lock().unwrap();
377+
*guard = Some(vga);
378+
drop(guard);
372379
osprintln!("\u{001b}[0mConfigured VGA console {}x{}", width, height);
373380
}
374381
}
375382

376383
if let Some((idx, serial_config)) = config.get_serial_console() {
377384
let _ignored = (api.serial_configure)(idx, serial_config);
378-
unsafe { SERIAL_CONSOLE = Some(SerialConsole(idx)) };
385+
let mut guard = SERIAL_CONSOLE.try_lock().unwrap();
386+
*guard = Some(SerialConsole(idx));
379387
osprintln!("Configured Serial console on Serial {}", idx);
380388
}
381389

@@ -421,7 +429,7 @@ pub extern "C" fn os_main(api: &bios::Api) -> ! {
421429

422430
loop {
423431
let mut buffer = [0u8; 16];
424-
let count = unsafe { STD_INPUT.get_data(&mut buffer) };
432+
let count = { STD_INPUT.lock().get_data(&mut buffer) };
425433
for b in &buffer[0..count] {
426434
menu.input_byte(*b);
427435
}

src/program.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,12 @@ extern "C" fn api_write(
319319
buffer: neotron_api::FfiByteSlice,
320320
) -> neotron_api::Result<()> {
321321
if fd == neotron_api::file::Handle::new_stdout() {
322-
if let Some(ref mut console) = unsafe { &mut crate::VGA_CONSOLE } {
322+
let mut guard = crate::VGA_CONSOLE.try_lock().unwrap();
323+
if let Some(console) = guard.as_mut() {
323324
console.write_bstr(buffer.as_slice());
324325
}
325-
if let Some(ref mut console) = unsafe { &mut crate::SERIAL_CONSOLE } {
326+
let mut guard = crate::SERIAL_CONSOLE.try_lock().unwrap();
327+
if let Some(console) = guard.as_mut() {
326328
if let Err(_e) = console.write_bstr(buffer.as_slice()) {
327329
return neotron_api::Result::Err(neotron_api::Error::DeviceSpecific);
328330
}
@@ -342,7 +344,7 @@ extern "C" fn api_read(
342344
) -> neotron_api::Result<usize> {
343345
if fd == neotron_api::file::Handle::new_stdin() {
344346
if let Some(buffer) = buffer.as_mut_slice() {
345-
let count = unsafe { crate::STD_INPUT.get_data(buffer) };
347+
let count = { crate::STD_INPUT.lock().get_data(buffer) };
346348
Ok(count).into()
347349
} else {
348350
neotron_api::Result::Err(neotron_api::Error::DeviceSpecific)

src/refcell.rs

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
//! # RefCells for Neotron OS.
2+
//!
3+
//! Like the `RefCell` in the standard library, except that it's thread-safe
4+
//! and uses the BIOS critical section to make it so.
5+
6+
// ===========================================================================
7+
// Modules and Imports
8+
// ===========================================================================
9+
10+
use core::{
11+
cell::UnsafeCell,
12+
ops::{Deref, DerefMut},
13+
sync::atomic::{AtomicBool, Ordering},
14+
};
15+
16+
// ===========================================================================
17+
// Global Variables
18+
// ===========================================================================
19+
20+
// None
21+
22+
// ===========================================================================
23+
// Macros
24+
// ===========================================================================
25+
26+
// None
27+
28+
// ===========================================================================
29+
// Public types
30+
// ===========================================================================
31+
32+
/// Indicates a failure to lock the refcell because it was already locked.
33+
#[derive(Debug)]
34+
pub struct LockError;
35+
36+
/// A cell that gives you references, and is thread-safe.
37+
///
38+
/// Uses the BIOS to ensure thread-safety whilst checking if the lock is taken
39+
/// or not.
40+
pub struct CsRefCell<T> {
41+
inner: UnsafeCell<T>,
42+
locked: AtomicBool,
43+
}
44+
45+
impl<T> CsRefCell<T> {
46+
/// Create a new cell.
47+
pub const fn new(value: T) -> CsRefCell<T> {
48+
CsRefCell {
49+
inner: UnsafeCell::new(value),
50+
locked: AtomicBool::new(false),
51+
}
52+
}
53+
54+
/// Try and do something with the lock.
55+
pub fn with<F, U>(&self, f: F) -> Result<U, LockError>
56+
where
57+
F: FnOnce(&mut CsRefCellGuard<T>) -> U,
58+
{
59+
let mut guard = self.try_lock()?;
60+
let result = f(&mut guard);
61+
drop(guard);
62+
Ok(result)
63+
}
64+
65+
/// Lock the cell.
66+
///
67+
/// If you can't lock it (because it is already locked), this function will panic.
68+
pub fn lock(&self) -> CsRefCellGuard<T> {
69+
self.try_lock().unwrap()
70+
}
71+
72+
/// Try and grab the lock.
73+
///
74+
/// It'll fail if it's already been taken.
75+
///
76+
/// It'll panic if the global lock is in a bad state, or you try and
77+
/// re-enter this function from an interrupt whilst the global lock is held.
78+
/// Don't do that.
79+
pub fn try_lock(&self) -> Result<CsRefCellGuard<T>, LockError> {
80+
let api = crate::API.get();
81+
82+
if (api.compare_and_swap_bool)(&self.locked, false, true) {
83+
// succesfully swapped `false` for `true`
84+
core::sync::atomic::fence(Ordering::Acquire);
85+
Ok(CsRefCellGuard { parent: self })
86+
} else {
87+
// cell is already locked
88+
Err(LockError)
89+
}
90+
}
91+
}
92+
93+
/// Mark our type as thread-safe.
94+
///
95+
/// # Safety
96+
///
97+
/// We use the BIOS critical sections to control access to the global lock, and
98+
/// refcell locks are only tested whilst holding the global lock. Thus it is now
99+
/// thread-safe.
100+
unsafe impl<T> Sync for CsRefCell<T> {}
101+
102+
/// Represents an active borrow of a [`CsRefCell`].
103+
pub struct CsRefCellGuard<'a, T> {
104+
parent: &'a CsRefCell<T>,
105+
}
106+
107+
impl<'a, T> Deref for CsRefCellGuard<'a, T> {
108+
type Target = T;
109+
110+
fn deref(&self) -> &Self::Target {
111+
let ptr = self.parent.inner.get();
112+
unsafe { &*ptr }
113+
}
114+
}
115+
116+
impl<'a, T> DerefMut for CsRefCellGuard<'a, T> {
117+
fn deref_mut(&mut self) -> &mut Self::Target {
118+
let ptr = self.parent.inner.get();
119+
unsafe { &mut *ptr }
120+
}
121+
}
122+
123+
impl<'a, T> Drop for CsRefCellGuard<'a, T> {
124+
fn drop(&mut self) {
125+
// We hold this refcell guard exclusively, so this can't race
126+
self.parent.locked.store(false, Ordering::Release);
127+
}
128+
}
129+
130+
// ===========================================================================
131+
// Private types
132+
// ===========================================================================
133+
134+
// None
135+
136+
// ===========================================================================
137+
// Private functions
138+
// ===========================================================================
139+
140+
// None
141+
142+
// ===========================================================================
143+
// Public functions
144+
// ===========================================================================
145+
146+
// None
147+
148+
// ===========================================================================
149+
// Tests
150+
// ===========================================================================
151+
152+
// None
153+
154+
// ===========================================================================
155+
// End of file
156+
// ===========================================================================

0 commit comments

Comments
 (0)