I'm looking for something like this (below is C)
void MyFunction() {
//Do work
static int serial;
++serial;
printf("Function run #%d\n", serial);
//Do more work
}
As Ice Giant mentions in their answer, you can use static mut to solve this. However, it is unsafe for a good reason: if you use it, you are responsible for making sure that the code is thread safe.
It's very easy to misuse and introduce unsound code and undefined behavior into your Rust program, so what I would recommend that you do instead is to use a type that provides safe interior mutability, such as Mutex, RwLock, or in the case of integers, atomics like AtomicU32:
use std::sync::atomic::{AtomicU32, Ordering};
fn my_function() {
// NOTE: AtomicU32 has safe interior mutability, so we don't need `mut` here
static SERIAL: AtomicU32 = AtomicU32::new(0);
// fetch and increment SERIAL in a single atomic operation
let serial = SERIAL.fetch_add(1, Ordering::Relaxed);
// do something with `serial`...
println!("function run #{}", serial);
}
Playground example
Because this program does not contain any unsafe code, we can be certain that the program does not contain the same data race bugs that can be caused by naïve use of static mut (or the equivalent use of static in the C code).
Related
I've created rust bindings to a C library and currently writing safe wrappers around it.
The question is about C functions which take in C function pointers which are not able to take in any custom user data.
It is easier to explain it with an example,
C Library:
// The function pointer I need to pass,
typedef void (*t_function_pointer_library_wants)(const char *argument);
// The function which I need to pass the function pointer to,
void register_hook(const t_function_pointer_library_wants hook);
Bindings:
// For the function pointer type
pub type t_function_pointer_library_wants = ::std::option::Option<unsafe extern "C" fn(argument: *const ::std::os::raw::c_char)>;
// For the function which accepts the pointer
extern "C" {
pub fn register_hook(hook: t_function_pointer_library_wants);
}
It would have been very nice if I could expose an api to the user like the following,
// Let's assume my safe wrapper is named on_something
// ..
on_something(|argument|{
// Do something with the argument..
});
// ..
although according to the sources below, the lack of ability to hand over the management of the part of memory which would store my closure's state to C, prevents me to create this sort of API. Because the function pointer in C is stateless and does not take in any user data of some sort. (Please correct me if I'm wrong.)
I've come to this conclusion by reading these sources and similar ones:
Trampoline Technique
Similar Trampoline Technique
Hacky Thread Local Technique
Sources in Shepmaster's answer
As a fallback, I maybe can imagine an API like this where I pass a function pointer instead.
fn handler(argument: &str) {
// Do something with the argument..
}
//..
on_something(handler);
//..
But I am a little confused about converting an fn(&str),
to an unsafe extern "C" fn(argument: *const std::os::raw::c_char)..
I'd be very glad if you could point me to the right direction.
* The actual library in focus is libpd and there is an issue I've created related to this.
Thanks a lot.
First off, this is a pretty hard problem to solve. Obviously, you need some way to pass data into a function outside of its arguments. However, pretty any method of doing that via a static could easily result in race conditions or worse, depending on what the c library does and how the library is used. The other option is to JIT some glue code that calls your closure. At first glance, that seems even worse, but libffi abstracts most of that away. A wrapper using the libffi crate would like this:
use std::ffi::CStr;
use libffi::high::Closure1;
fn on_something<F: Fn(&str) + Send + Sync + 'static>(f: F) {
let closure: &'static _ = Box::leak(Box::new(move |string: *const c_char| {
let string = unsafe { CStr::from_ptr(string).to_str().unwrap() };
f(string);
}));
let callback = Closure1::new(closure);
let &code = callback.code_ptr();
let ptr:unsafe extern "C" fn (*const c_char) = unsafe { std::mem::transmute(code) };
std::mem::forget(callback);
unsafe { register_handler(Some(ptr)) };
}
I don't have a playground link, but it worked fine when I tested it locally. There are two important things to note with this code:
It's maximally pessimistic about what the c code does, assuming the function is repeatedly called from multiple threads for the entire duration of the program. You may be able to get away with fewer restrictions, depending on what libpd does.
It leaks memory to ensure the callback is valid for the life of the program. This is probably fine since callbacks are typically only set once. There is no way to safely recover this memory without keeping around a pointer to the callback that was registered.
It's also worth noting that the libffi::high::ClosureMutN structs are unsound, as they permit aliasing mutable references to the passed wrapped closure. There is a PR to fix that waiting to be merged though.
lazy_static doesn't work because I need to assign to this variable at runtime after some user interaction. thread_local doesn't work because I need to read this variable across threads.
From a system perspective, I think what I'm trying to do should be simple. At the beginning of execution I'm single threaded, I initialize some things, and then I tokio::spawn some tasks which only need to read those things.
I can get past the problem by using a mutex, but I don't really see why I should need to use a mutex when I can guarantee that no tasks will ever try to get mutable access other than at the very beginning of runtime when I'm still in a single thread. Is there a better way that using a mutex?
This is what I have so far, in case anyone is curious:
lazy_static! {
pub static ref KEYPAIRSTORE_GLOBAL: Mutex<KeypairStore> = Mutex::new(KeypairStore::new());
}
// ...
// at top of main:
let mut keypairstore = KEYPAIRSTORE_GLOBAL.lock().unwrap();
*keypairstore = KeypairStore::new_from_password();
// somewhere later in a tokio::spawn:
let keypair_store = KEYPAIRSTORE_GLOBAL.lock().unwrap();
let keypair = keypair_store.get_keypair();
println!("{}", keypair.address());
I don't see why I need to use this mutex... I'd be happy to use unsafe during assignment, but I'd rather not have to use it every time I want to read.
As written, you need the Mutex because you are mutating it after it is initialised. Instead, do the mutation during the initialisation:
lazy_static! {
pub static ref KEYPAIRSTORE_GLOBAL: KeypairStore = {
let mut keystore = KeypairStore::new_from_password();
// ... more initialisation here...
keystore
}
}
// somewhere later in a tokio::spawn:
let keypair = KEYPAIRSTORE_GLOBAL.get_keypair();
println!("{}", keypair.address());
This is assuming that the signature of get_keypair is:
pub fn get_keypair(&self) -> Keypair;
On page 465 of Programming Rust you can find the code and explanation (emphasis added by me)
use std::sync::Arc;
fn process_files_in_parallel(filenames: Vec<String>,
glossary: Arc<GigabyteMap>)
-> io::Result<()>
{
...
for worklist in worklists {
// This call to .clone() only clones the Arc and bumps the
// reference count. It does not clone the GigabyteMap.
let glossary_for_child = glossary.clone();
thread_handles.push(
spawn(move || process_files(worklist, &glossary_for_child))
);
}
...
}
We have changed the type of glossary: to run the analysis in parallel, the caller must pass in an Arc<GigabyteMap>, a smart pointer to a GigabyteMap that’s been moved into the heap, by doing Arc::new(giga_map). When we call glossary.clone(), we are making a copy of the Arc smart pointer, not the whole GigabyteMap. This amounts to incrementing a reference count. With this change, the program compiles and runs, because it no longer depends on reference lifetimes. As long as any thread owns an Arc<GigabyteMap>, it will keep the map alive, even if the parent thread bails out early. There won’t be any data races, because data in an Arc is immutable.
In the next section they show this rewritten with Rayon,
extern crate rayon;
use rayon::prelude::*;
fn process_files_in_parallel(filenames: Vec<String>, glossary: &GigabyteMap)
-> io::Result<()>
{
filenames.par_iter()
.map(|filename| process_file(filename, glossary))
.reduce_with(|r1, r2| {
if r1.is_err() { r1 } else { r2 }
})
.unwrap_or(Ok(()))
}
You can see in the section rewritten to use Rayon that it accepts &GigabyteMap rather than Arc<GigabyteMap>. They don't explain how this works though. Why doesn't Rayon require Arc<GigabyteMap>? How does Rayon get away with accepting a direct reference?
Rayon can guarantee that the iterator does not outlive the current stack frame, unlike what I assume is thread::spawn in the first code example. Specifically, par_iter under the hood uses something like Rayon's scope function, which allows one to spawn a unit of work that's "attached" to the stack and will join before the stack ends.
Because Rayon can guarantee (via lifetime bounds, from the user's perspective) that the tasks/threads are joined before the function calling par_iter exits, it can provide this API which is more ergonomic to use than the standard library's thread::spawn.
Rayon expands on this in the scope function's documentation.
I have a struct that is not Send because it contains Rc. Lets say that Arc has too big overhead, so I want to keep using Rc. I would still like to occasionally Send this struct between threads, but only when I can verify that the Rc has strong_count 1 and weak_count 0.
Here is (hopefully safe) abstraction that I have in mind:
mod my_struct {
use std::rc::Rc;
#[derive(Debug)]
pub struct MyStruct {
reference_counted: Rc<String>,
// more fields...
}
impl MyStruct {
pub fn new() -> Self {
MyStruct {
reference_counted: Rc::new("test".to_string())
}
}
pub fn pack_for_sending(self) -> Result<Sendable, Self> {
if Rc::strong_count(&self.reference_counted) == 1 &&
Rc::weak_count(&self.reference_counted) == 0
{
Ok(Sendable(self))
} else {
Err(self)
}
}
// There are more methods, some may clone the `Rc`!
}
/// `Send`able wrapper for `MyStruct` that does not allow you to access it,
/// only unpack it.
pub struct Sendable(MyStruct);
// Safety: `MyStruct` is not `Send` because of `Rc`. `Sendable` can be
// only created when the `Rc` has strong count 1 and weak count 0.
unsafe impl Send for Sendable {}
impl Sendable {
/// Retrieve the inner `MyStruct`, making it not-sendable again.
pub fn unpack(self) -> MyStruct {
self.0
}
}
}
use crate::my_struct::MyStruct;
fn main() {
let handle = std::thread::spawn(|| {
let my_struct = MyStruct::new();
dbg!(&my_struct);
// Do something with `my_struct`, but at the end the inner `Rc` should
// not be shared with anybody.
my_struct.pack_for_sending().expect("Some Rc was still shared!")
});
let my_struct = handle.join().unwrap().unpack();
dbg!(&my_struct);
}
I did a demo on the Rust playground.
It works. My question is, is it actually safe?
I know that the Rc is owned only by a single onwer and nobody can change that under my hands, because it can't be accessed by other threads and we wrap it into Sendable which does not allow access to the contained value.
But in some crazy world Rc could for example internally use thread local storage and this would not be safe... So is there some guarantee that I can do this?
I know that I must be extremely careful to not introduce some additional reason for the MyStruct to not be Send.
No.
There are multiple points that need to be verified to be able to send Rc across threads:
There can be no other handle (Rc or Weak) sharing ownership.
The content of Rc must be Send.
The implementation of Rc must use a thread-safe strategy.
Let's review them in order!
Guaranteeing the absence of aliasing
While your algorithm -- checking the counts yourself -- works for now, it would be better to simply ask Rc whether it is aliased or not.
fn is_aliased<T>(t: &mut Rc<T>) -> bool { Rc::get_mut(t).is_some() }
The implementation of get_mut will be adjusted should the implementation of Rc change in ways you have not foreseen.
Sendable content
While your implementation of MyStruct currently puts String (which is Send) into Rc, it could tomorrow change to Rc<str>, and then all bets are off.
Therefore, the sendable check needs to be implemented at the Rc level itself, otherwise you need to audit any change to whatever Rc holds.
fn sendable<T: Send>(mut t: Rc<T>) -> Result<Rc<T>, ...> {
if !is_aliased(&mut t) {
Ok(t)
} else {
...
}
}
Thread-safe Rc internals
And that... cannot be guaranteed.
Since Rc is not Send, its implementation can be optimized in a variety of ways:
The entire memory could be allocated using a thread-local arena.
The counters could be allocated using a thread-local arena, separately, so as to seamlessly convert to/from Box.
...
This is not the case at the moment, AFAIK, however the API allows it, so the next release could definitely take advantage of this.
What should you do?
You could make pack_for_sending unsafe, and dutifully document all assumptions that are counted on -- I suggest using get_mut to remove one of them. Then, on each new release of Rust, you'd have to double-check each assumption to ensure that your usage if still safe.
Or, if you do not mind making an allocation, you could write a conversion to Arc<T> yourself (see Playground):
fn into_arc<T>(this: Rc<T>) -> Result<Arc<T>, Rc<T>> {
Rc::try_unwrap(this).map(|t| Arc::new(t))
}
Or, you could write a RFC proposing a Rc <-> Arc conversion!
The API would be:
fn Rc<T: Send>::into_arc(this: Self) -> Result<Arc<T>, Rc<T>>
fn Arc<T>::into_rc(this: Self) -> Result<Rc<T>, Arc<T>>
This could be made very efficiently inside std, and could be of use to others.
Then, you'd convert from MyStruct to MySendableStruct, just moving the fields and converting Rc to Arc as you go, send to another thread, then convert back to MyStruct.
And you would not need any unsafe...
The only difference between Arc and Rc is that Arc uses atomic counters. The counters are only accessed when the pointer is cloned or dropped, so the difference between the two is negligible in applications which just share pointers between long-lived threads.
If you have never cloned the Rc, it is safe to send between threads. However, if you can guarantee that the pointer is unique then you can make the same guarantee about a raw value, without using a smart pointer at all!
This all seems quite fragile, for little benefit; future changes to the code might not meet your assumptions, and you will end up with Undefined Behaviour. I suggest that you at least try making some benchmarks with Arc. Only consider approaches like this when you measure a performance problem.
You might also consider using the archery crate, which provides a reference-counted pointer that abstracts over atomicity.
I'm new to Rust and threading and I'm trying to print out a number while adding to it in another thread. How can I accomplish this?
use std::thread;
use std::time::Duration;
fn main() {
let mut num = 5;
thread::spawn(move || {
loop {
num += 1;
thread::sleep(Duration::from_secs(10));
}
});
output(num);
}
fn output(num: i32) {
loop {
println!("{:?}", num);
thread::sleep(Duration::from_secs(5));
}
}
The above code doesn't work: it always prints 5 as if the number is never incremented.
Please read the "Shared-State Concurrency" chapter of The Rust Book, it explains how to do this in detail.
In short:
Your program does not work because num is copied, so output() and the thread operate on different copies of the number. The Rust compiler will fail to compile with an error if num is not copyable.
Since you need to share the same variable between multiple threads, you need to wrap it in an Arc (atomic reference-counted variable)
Since you need to modify the variable inside the Arc, you need to put it in a Mutex or RwLock. You use the .lock() method to obtain a mutable reference out of a Mutex. The method will ensure exclusive access across the whole process during the lifetime of that mutable reference.
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
fn main() {
let num = Arc::new(Mutex::new(5));
// allow `num` to be shared across threads (Arc) and modified
// (Mutex) safely without a data race.
let num_clone = num.clone();
// create a cloned reference before moving `num` into the thread.
thread::spawn(move || {
loop {
*num.lock().unwrap() += 1;
// modify the number.
thread::sleep(Duration::from_secs(10));
}
});
output(num_clone);
}
fn output(num: Arc<Mutex<i32>>) {
loop {
println!("{:?}", *num.lock().unwrap());
// read the number.
// - lock(): obtains a mutable reference; may fail,
// thus return a Result
// - unwrap(): ignore the error and get the real
// reference / cause panic on error.
thread::sleep(Duration::from_secs(5));
}
}
You may also want to read:
Why does Rust have mutexes and other sychronization primitives, if sharing of mutable state between tasks is not allowed?
What happens when an Arc is cloned?
How do I share a mutable object between threads using Arc? (for why we need Arc<Mutex<i32>> instead of Arc<i32>)
When would you use a Mutex without an Arc? (for why we need Arc<Mutex<i32>> instead of Mutex<i32>)
The other answer solves the problem for any type, but as pnkfelix observes, atomic wrapper types are another solution that will work for the specific case of i32.
Since Rust 1.0, you can use AtomicBool, AtomicPtr<T>, AtomicIsize and AtomicUsize to synchronize multi-threaded access to bool, *mut T, isize and usize values. In Rust 1.34, several new Atomic types have been stabilized, including AtomicI32. (Check the std::sync::atomic documentation for the current list.)
Using an atomic type is most likely more efficient than locking a Mutex or RwLock, but requires more attention to the low-level details of memory ordering. If your threads share more data than can fit in one of the standard atomic types, you probably want a Mutex instead of multiple Atomics.
That said, here's a version of kennytm's answer using AtomicI32 instead of Mutex<i32>:
use std::sync::{
atomic::{AtomicI32, Ordering},
Arc,
};
use std::thread;
use std::time::Duration;
fn main() {
let num = Arc::new(AtomicI32::new(5));
let num_clone = num.clone();
thread::spawn(move || loop {
num.fetch_add(1, Ordering::SeqCst);
thread::sleep(Duration::from_secs(10));
});
output(num_clone);
}
fn output(num: Arc<AtomicI32>) {
loop {
println!("{:?}", num.load(Ordering::SeqCst));
thread::sleep(Duration::from_secs(5));
}
}
Arc is still required for shared ownership (but see How can I pass a reference to a stack variable to a thread?).
Choosing the right memory Ordering is far from trivial. SeqCst is the most conservative choice, but if there is only one memory address being shared, Relaxed should also work. See the links below for more information.
Links
std::sync::atomic module documentation
Atomics (chapter of The Rustonomicon)
LLVM Memory Model for Concurrent Operations and Atomic Instructions and Concurrency Guide