I want to build a single-producer multiple-consumer example in Rust, where the producer is bounded to have no more than 10 outstanding items. I modeled a solution in C that uses a a mutex and two condvars. One condvar is to wait the consumers when there is nothing to consume and one condvar is to wait for the producer when the unconsumed items count is greater than say 10. The C code is below.
As I understand it from the Rust docs, there must be a 1-1 connection between std::sync::Mutex and a std::sync::Condvar so I can't make an exact translation of my C solution.
Is there some other way to achieve the same end (that I cannot see) in Rust using std::sync::Mutex and std::sync::Condvar.
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
//
// This is a simple example of using a mutex and 2 condition variables to
// sync a single writer and multiple readers interacting with a bounded (fixed max size) queue
//
// in this toy example a queue is simulated by an int counter n_resource
//
int n_resource;
pthread_cond_t rdr_cvar;
pthread_cond_t wrtr_cvar;
pthread_mutex_t mutex;
void reader(void* data)
{
long id = (long)data;
for(;;) {
pthread_mutex_lock(&mutex);
while (n_resource <= 0) {
pthread_cond_wait(&rdr_cvar, &mutex);
}
printf("Reader %ld n_resource = %d\n", id, n_resource);
--n_resource;
// if there are still things to read - singla one reader
if(n_resource > 0) {
pthread_cond_signal(&rdr_cvar);
}
// if there is space for the writer to add another signal the writer
if(n_resource < 10) {
pthread_cond_signal(&wrtr_cvar);
}
pthread_mutex_unlock(&mutex);
}
}
void writer(void* data)
{
for(;;) {
pthread_mutex_lock(&mutex);
printf("Writer before while n_resource %d \n", n_resource);
while (n_resource > 10) {
pthread_cond_wait(&wrtr_cvar, &mutex);
}
printf("Writer after while n_resource %d \n", n_resource);
++n_resource;
// if there is something for a reader to read signal one of the readers.
if(n_resource > 0) {
pthread_cond_signal(&rdr_cvar);
}
pthread_mutex_unlock(&mutex);
}
}
int main()
{
pthread_t rdr_thread_1;
pthread_t rdr_thread_2;
pthread_t wrtr_thread;
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&rdr_cvar, NULL);
pthread_cond_init(&wrtr_cvar, NULL);
pthread_create(&rdr_thread_1, NULL, &reader, (void*)1L);
pthread_create(&rdr_thread_2, NULL, &reader, (void*)2L);
pthread_create(&wrtr_thread, NULL, &writer, NULL);
pthread_join(wrtr_thread, NULL);
pthread_join(rdr_thread_1, NULL);
pthread_join(rdr_thread_2, NULL);
}
While a CondVar needs to be associated with only one Mutex, it is not necessary that a Mutex is associated with only one CondVar.
For example, the following code seems to work just fine - you can run it on the playground.
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
struct Q {
rdr_cvar: Condvar,
wrtr_cvar: Condvar,
mutex: Mutex<i32>,
}
impl Q {
pub fn new() -> Q {
Q {
rdr_cvar: Condvar::new(),
wrtr_cvar: Condvar::new(),
mutex: Mutex::new(0),
}
}
}
fn writer(id: i32, qq: Arc<Q>) {
let q = &*qq;
for i in 0..10 {
let guard = q.mutex.lock().unwrap();
let mut guard = q.wrtr_cvar.wait_while(guard, |n| *n > 3).unwrap();
println!("{}: Writer {} n_resource = {}\n", i, id, *guard);
*guard += 1;
if *guard > 0 {
q.rdr_cvar.notify_one();
}
if *guard < 10 {
q.wrtr_cvar.notify_one();
}
}
}
fn reader(id: i32, qq: Arc<Q>) {
let q = &*qq;
for i in 0..10 {
let guard = q.mutex.lock().unwrap();
let mut guard = q.rdr_cvar.wait_while(guard, |n| *n <= 0).unwrap();
println!("{} Reader {} n_resource = {}\n", i, id, *guard);
*guard -= 1;
if *guard > 0 {
q.rdr_cvar.notify_one();
}
if *guard < 10 {
q.wrtr_cvar.notify_one();
}
}
}
fn main() {
let data = Arc::new(Q::new());
let data2 = data.clone();
let t1 = thread::spawn(move || writer(0, data2));
let t2 = thread::spawn(move || reader(1, data));
t1.join().unwrap();
t2.join().unwrap();
}
Related
I'm trying to re-implement this C code:
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#define ROWS 100000
#define COLS 10000
int twodarray[ROWS][COLS];
int main() {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
twodarray[i][j] = rand();
}
}
int64 sum = 0;
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
sum += twodarray[i][j];
}
}
}
So, after a lot of trial and error I've come up with Rust code that at least compiles
extern crate rand;
const ROWS: usize = 100000;
const COLS: usize = 10000;
fn main() {
let mut twodarray: [[u128; COLS]; ROWS] = [[0; COLS]; ROWS];
for i in 0..ROWS {
for j in 0..COLS {
twodarray[i][j] = rand::random::<u8>() as u128;
}
}
let mut sum: u128 = 0;
for k in 0..ROWS {
for j in 0..COLS {
sum += twodarray[k][j] as u128;
}
}
println!("{}", sum);
}
But when I compile and execute it, I get the following error message: thread 'main' has overflowed its stack. To be honest, I have absolutelly no idea why this is happening. I'm currently learning rust, but there is not a lot of information on 2d arrays online... I DO NOT WANT TO USE VECTOR. This exercise is specifically aimed on arrays.
EDIT:
After reading the accepted answear, I've come up with Rust code that outputs expected results:
extern crate rand;
const ROWS: usize = 100000;
const COLS: usize = 10000;
fn main() {
// let mut twodarray: Box<[[u8; COLS]; ROWS]> = Box::new([[0; COLS]; ROWS]);
// for i in 0..ROWS {
// for j in 0..COLS {
// twodarray[i][j] = rand::random::<u8>();
// }
// }
// let mut sum: u32 = 0;
// for k in 0..ROWS {
// for l in 0..COLS {
// sum += twodarray[k][l] as u32;
// }
// }
let mut twodarray: Box<[[u8; ROWS]; COLS]> = Box::new([[0; ROWS]; COLS]);
for i in 0..COLS {
for j in 0..ROWS {
twodarray[i][j] = rand::random::<u8>();
}
}
let mut sum: u32 = 0;
for k in 0..COLS {
for l in 0..ROWS {
sum += twodarray[k][l] as u32;
}
}
println!("{}", sum);
}
This two-dimensional array is huge. Rust tries to allocate it on the stack (that's the way arrays in Rust work).
You explicitly wrote you didn't want to use vectors, but in reality vectors are allocated on the heap, so you wouldn't have such a problem with them. It's good to keep that in mind.
If you insist on using arrays, maybe put them in a Box, so that they go to the heap?
That's not a Rust-specific problem. Stacks are generally a lot more limited, compared to the heap, usually up to a few megabytes. In some other languages, such as Java, arrays are allocated on the heap, but that doesn't mean there is no similar stack size limit there as well.
The mutex and threads examples on
the internet are not good as I am not able to find how to lock a block of code using a mutex to lock a method.
// mutex example
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex
std::mutex mtx; // mutex for critical section
void print_block (int n, char c) {
// critical section (exclusive access to std::cout signaled by locking mtx):
mtx.lock();
for (int i=0; i<n; ++i) { std::cout << c; }
std::cout << '\n';
mtx.unlock();
}
int main ()
{
std::thread th1 (print_block,50,'*');
std::thread th2 (print_block,50,'$');
th1.join();
th2.join();
return 0;
}
What is similar Rust code for this C++ snippet? Locking loops and printing as in Rust example the mutex must be of that type such as
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let data = Arc::new(Mutex::new(vec![1u32, 2, 3]));
for i in 0..3 {
let data = data.clone();
thread::spawn(move || {
let mut data = data.lock().unwrap();
data[i] += 1;
});
}
thread::sleep_ms(50);
}
I have written this similar code. Is it fine or can it be written in a better way?
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let mtx = Arc::new(Mutex::new(""));
let mtx1 = mtx.clone();
let mtx2 = mtx.clone();
let n = 50;
let th1 = thread::spawn(move || {
mtx1.lock().unwrap();
printData(n, "*".to_string());
});
let th2 = thread::spawn(move || {
mtx2.lock().unwrap();
printData(n, "$".to_string());
});
th1.join();
th2.join();
}
fn printData(n: u32, c: String) {
let mut str_val: String = "".to_string();
for i in 0..n {
str_val.push_str(&c);
}
println!("{}", str_val);
}
I'm trying to solve the thread-ring problem. In each thread I read the token value
if it is not mine, check if it's the end of the program
if it is then finish the thread
otherwise, read again and repeat
if it is mine (i.e. has my id) then acquire the write lock, increase the value of the token, check if it's the end then tell main thread that I finished it and finish the current thread loop
If it not over, then release the write lock, and start to read again
There is no unlock. Is there any unlock like the one I need in here?
It seems that I should release the read lock as well, because the write lock won't happen if there is someone reading the data. Is it necessary?
fn main() {
use std::sync::{Arc, RwLock};
use std::thread;
use std::sync::mpsc::channel;
const N: usize = 5; //503;
const STOP_POINT: usize = 100;
let n = Arc::new(RwLock::new(1));
let (sender, reciever) = channel();
for i in 1..N {
let (n_c, channel) = (n.clone(), sender.clone());
// println!("Thread n.{} beeing created!", i);
let a = thread::Builder::new()
.name(i.to_string())
.spawn(move || -> () {
loop {
let mut read_only = n_c.read().unwrap();
let say_my_name = (*thread::current().name().unwrap()).to_string();
// println!("Thread {} says: gonna try!", say_my_name);
while (*read_only % N) != i {
if *read_only == 0 {
break;
}
// println!("Thread {} says: aint mine!", say_my_name);
read_only = n_c.read().unwrap();
} // WAIT
println!("Thread {} says: my turn!", say_my_name);
let mut ref_to_num = n_c.write().unwrap();
*ref_to_num += 1;
if *ref_to_num == STOP_POINT {
channel.send(say_my_name).unwrap();
break;
}
}
()
});
assert_eq!(a.is_ok(), true);
// thread::spawn();
// println!("Thread n.{} created!", i);
}
println!("{}", reciever.recv().unwrap());
}
To release a lock, you let it fall out of scope or explicitly invoke its destructor by calling drop.
Here's how your program could be written using drop in two places:
fn main() {
use std::sync::{Arc, RwLock};
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;
const N: usize = 503;
const STOP_POINT: usize = 100;
let n = Arc::new(RwLock::new(1));
let (sender, receiver) = channel();
for i in 1..N {
let (n_c, channel) = (n.clone(), sender.clone());
// println!("Thread n.{} beeing created!", i);
thread::Builder::new()
.name(i.to_string())
.spawn(move || {
loop {
let mut read_only = n_c.read().unwrap();
let say_my_name = (*thread::current().name().unwrap()).to_string();
// println!("Thread {} says: gonna try!", say_my_name);
while (*read_only % N) != i {
if *read_only == 0 {
break;
}
drop(read_only); // release the lock before sleeping
// println!("Thread {} says: aint mine!", say_my_name);
thread::sleep(Duration::from_millis(1));
read_only = n_c.read().unwrap();
}
println!("Thread {} says: my turn!", say_my_name);
drop(read_only); // release the read lock before taking a write lock
let mut ref_to_num = n_c.write().unwrap();
*ref_to_num += 1;
if *ref_to_num == STOP_POINT {
channel.send(say_my_name).unwrap();
break;
}
}
})
.expect("failed to spawn a thread");
// println!("Thread n.{} created!", i);
}
println!("{}", receiver.recv().unwrap());
}
Note that if we don't reassign read_lock in the while loop, the compiler will give an error because read_lock doesn't hold a valid value after we call drop(read_lock). Rust is fine with local variables that are temporarily uninitialized, but of course we need to reinitialize them before we can use them again.
Here's how the thread's main loop could be written to use a scope to replace one of the drops:
loop {
let say_my_name = (*thread::current().name().unwrap()).to_string();
{
let mut read_only = n_c.read().unwrap();
// println!("Thread {} says: gonna try!", say_my_name);
while (*read_only % N) != i {
if *read_only == 0 {
break;
}
drop(read_only);
thread::sleep(Duration::from_millis(1));
// println!("Thread {} says: aint mine!", say_my_name);
read_only = n_c.read().unwrap();
}
println!("Thread {} says: my turn!", say_my_name);
} // read_only is dropped here
let mut ref_to_num = n_c.write().unwrap();
*ref_to_num += 1;
if *ref_to_num == STOP_POINT {
channel.send(say_my_name).unwrap();
break;
}
}
I am new to Rust, and struggling to deal with all those wrapper types in Rust. I am trying to write code that is semantically equal to the following C code. The code tries to create a big table for book keeping, but will divide the big table so that every thread will only access their local small slices of that table. The big table will not be accessed unless other threads quit and no longer access their own slice.
#include <stdio.h>
#include <pthread.h>
void* write_slice(void* arg) {
int* slice = (int*) arg;
int i;
for (i = 0; i < 10; i++)
slice[i] = i;
return NULL;
}
int main()
{
int* table = (int*) malloc(100 * sizeof(int));
int* slice[10];
int i;
for (i = 0; i < 10; i++) {
slice[i] = table + i * 10;
}
// create pthread for each slice
pthread_t p[10];
for (i = 0; i < 10; i++)
pthread_create(&p[i], NULL, write_slice, slice[i]);
for (i = 0; i < 10; i++)
pthread_join(p[i], NULL);
for (i = 0; i < 100; i++)
printf("%d,", table[i]);
}
How do I use Rust's types and ownership to achieve this?
Let's start with the code:
// cargo-deps: crossbeam="0.7.3"
extern crate crossbeam;
const CHUNKS: usize = 10;
const CHUNK_SIZE: usize = 10;
fn main() {
let mut table = [0; CHUNKS * CHUNK_SIZE];
// Scoped threads allow the compiler to prove that no threads will outlive
// table (which would be bad).
let _ = crossbeam::scope(|scope| {
// Chop `table` into disjoint sub-slices.
for slice in table.chunks_mut(CHUNK_SIZE) {
// Spawn a thread operating on that subslice.
scope.spawn(move |_| write_slice(slice));
}
// `crossbeam::scope` ensures that *all* spawned threads join before
// returning control back from this closure.
});
// At this point, all threads have joined, and we have exclusive access to
// `table` again. Huzzah for 100% safe multi-threaded stack mutation!
println!("{:?}", &table[..]);
}
fn write_slice(slice: &mut [i32]) {
for (i, e) in slice.iter_mut().enumerate() {
*e = i as i32;
}
}
One thing to note is that this needs the crossbeam crate. Rust used to have a similar "scoped" construct, but a soundness hole was found right before 1.0, so it was deprecated with no time to replace it. crossbeam is basically the replacement.
What Rust lets you do here is express the idea that, whatever the code does, none of the threads created within the call to crossbeam::scoped will survive that scope. As such, anything borrowed from outside that scope will live longer than the threads. Thus, the threads can freely access those borrows without having to worry about things like, say, a thread outliving the stack frame that table is defined by and scribbling over the stack.
So this should do more or less the same thing as the C code, though without that nagging worry that you might have missed something. :)
Finally, here's the same thing using scoped_threadpool instead. The only real practical difference is that this allows us to control how many threads are used.
// cargo-deps: scoped_threadpool="0.1.6"
extern crate scoped_threadpool;
const CHUNKS: usize = 10;
const CHUNK_SIZE: usize = 10;
fn main() {
let mut table = [0; CHUNKS * CHUNK_SIZE];
let mut pool = scoped_threadpool::Pool::new(CHUNKS as u32);
pool.scoped(|scope| {
for slice in table.chunks_mut(CHUNK_SIZE) {
scope.execute(move || write_slice(slice));
}
});
println!("{:?}", &table[..]);
}
fn write_slice(slice: &mut [i32]) {
for (i, e) in slice.iter_mut().enumerate() {
*e = i as i32;
}
}
I am trying to bundle state in a struct together with callbacks that mutate the state. It works fine when I use managed pointers:
struct StateAndCallbacks01 {
state: #mut int,
inc: #fn(),
dec: #fn()
}
let state01: #mut int = #mut 0;
let inc01: #fn() = || {
*state01 += 1;
};
let dec01: #fn() = || {
*state01 -= 1;
};
let state_cbs_01 = #StateAndCallbacks01 {
state: state01,
inc: inc01,
dec: dec01
};
(state_cbs_01.inc)();
println(fmt!("state: %d", *state_cbs_01.state));
(state_cbs_01.dec)();
println(fmt!("state: %d", *state_cbs_01.state));
Next, I want to send this structure to another task, and thus have to switch to unique pointers everywhere. I cannot make that work: "error: obsolete syntax: const or mutable owned pointer"
struct StateAndCallbacks02 {
state: ~mut int,
inc: ~fn(),
dec: ~fn()
}
let state02: ~mut int = ~mut 0;
let inc02: ~fn() = || {
*state02 += 1;
};
let dec02: ~fn() = || {
*state02 -= 1;
};
let state_cbs_02 = ~StateAndCallbacks02 {
state: state02,
inc: inc02,
dec: dec02
};
let (port, chan): (Port<bool>, Chan<bool>) = stream();
do spawn {
(state_cbs_02.inc)();
println(fmt!("state: %d", *state_cbs_02.state));
(state_cbs_02.dec)();
println(fmt!("state: %d", *state_cbs_02.state));
chan.send(true);
};
let result = port.recv();
println(fmt!("result: %s", result));
Any suggestions? Any better ways to send callbacks across tasks?
Instead of keeping the functions as fields in the struct you can instead add methods to the struct.
struct Foo {
data: int
}
impl Foo {
fn inc(&mut self) {
self.data += 1;
}
}
The impl syntax lets you define methods on the struct. Which you can later call:
let mut my_foo = Foo { data: 0 };
my_foo.inc();
You have to declare my_foo as mutable since the inc method needs to take a mutable reference to it.
The reason for the obsolete syntax error is because doing ~mut 0 is deprecated since mutability is determined by who 'owns' the object. What you'd have to do instead is let mut foo = ~0. The variable foo is the 'owner' and thus is where you declare the mutability. #-pointers are special in that they don't inherit mutability and are managed by a task local GC. (Sections 8 & 9 of the Rust tutorial better explain this)
So with all that put together, you could write your original code like so:
struct State {
data: int
}
impl State {
fn inc(&mut self) {
self.data += 1;
}
fn dec(&mut self) {
self.data -= 1;
}
}
fn main() {
let state = State {
data: 0
};
let (port, chan) = stream();
do spawn {
let mut state = state;
state.inc();
println(fmt!("State: %d", state.data));
state.dec();
println(fmt!("State: %d", state.data));
chan.send(true);
};
let result = port.recv();
println(fmt!("Result: %?", result));
}