I will like to understand why is it unsafe to use unsafe code in rust for accessing a shared resource/varibale in rust if my program runs on a single core computer. ChatGPT and ClaudeAI both say this code is unsafe even if running on a single core computer:
use std::collections::HashMap;
static mut UNSAFE_MAP: Option<HashMap<u32, u32>> = None;
#[tokio::main(flavor = "current_thread")]
async fn main() {
// Initialize map
unsafe { UNSAFE_MAP = Some(HashMap::new()); }
let task1 = tokio::spawn(async {
for i in 0..10_000_000u32 {
unsafe {
if let Some(map) = &mut UNSAFE_MAP {
map.insert(i, i);
}
}
}
});
let task2 = tokio::spawn(async {
for i in 0..10_000_000u32 {
unsafe {
if let Some(map) = &mut UNSAFE_MAP {
map.insert(i + 10_000_000, i);
}
}
}
});
// Wait for both tasks
let _ = tokio::join!(task1, task2);
// Check final state
unsafe {
if let Some(map) = &UNSAFE_MAP {
println!("Final map size: {}", map.len());
// Should be 20 million if everything worked
}
}
}
I understant that the OS can stop my program at any given time. But once it resumes it should continue running on the same order it was supposed to no? I also undertand that if both tokio threads write to the same file or do any OS related stuff without synchronization/locks there could be problems. I have a single core computer hosted on AWS and I will like to use unsafe code to avoid having locks. I am actually building an RTP server so having no locks will be nice to have. I have run this code for one hour and havent been able to make it fail.
This code is just an example in my real program I plan to have a pointer to a struct that holds all my shared resources. I will be reading most of the time and ocasionally writing to hashmaps. Since I am building an RTP server even if I use a RwLock it takes a long time if I plan on haing 100 concurrent calls.
Edit
And lastly what is safe? for example using a i32 instead of an Atomic32 would be safe on a one single core CPU correct? But a hashmap is not safe correct? So if I never plan on having more than 100 concurrent calls I could use an cicrular array safely? If I have 100 concurrent calls that means I have to send an audio packet every 20 ms and once for each channels so I belive I have a high chance of encountering a contended lock.
I understant that the OS can stop my program at any given time.
It can also change which thread is running at any time. Suppose your Task1 thread is in the middle of inserting a value into the map, and has just chosen a bucket to use and verified that it isn’t full. Then the OS decides to switch to your Task2 thread, which inserts a value into that same bucket. Then the first threat starts running again, and overwrites the value the second thread inserted (because it thought the bucket was still available). In fact, there’s a pretty good chance that something like that is already happening, even if the size (which is tracked separately) appears to be correct.
Thread preemption is more commonly unsafe than process preemption.
Incidentally, grabbing and releasing a non-contended lock, and in particular doing it repeatedly to the same lock on the same core, is very very fast. If you’re seeing slowness in this situation it’s because the lock is contended (as it definitely is here).
P.S. Although they happened to be right here, chat bots like ChatGPT and Claude constantly make mistakes and should never be trusted, even if they claim to cite sources. Don’t ask them things if you don’t already know the answer.