Preforming multiple operations on a thread

Hi, I have a data structure that holds a vector of data (say string) and a counter to the number of operations on the structure. The code is something like this:

struct VecWithOpsCounter {
    counter: i32,
    data: Vec<(String, i32)>,
}

impl VecWithOpsCounter {
    fn new(self) -> Self {
        VecWithOpsCounter{
            counter: 0,
            data: Vec::new(),
        }
    }

    fn add(&mut self, s: String) {

        // These three lines should all happen together for thread safety
        // and hopefully can all fail together if S*** happens
        let count = self.counter;
        self.data.push((s, count));
        self.counter = count+1;
    }

    fn remove(&mut self) -> Option<i32> {
        // These three lines should all happen together for thread safety
        // and hopefully can all fail together if S*** happens
        if let Some((_, counter)) = self.data.pop() {
            self.counter = self.counter + 1;
            Some(counter);
        }

        None
    }
}

(note that the counter is not not the length of the vector, but is ever growing with each operation)

How do I make sure that the code with in the add and remove functions is performed "atomically", so that it is thread-safe. Also, is there a way to make it all as one operation, so that it all succeeds or fails together?

I am sure there is idiomatic way of doing it, but cannot find it.

Thanks.

You probably want to wrap your VecWithOpsCounter instance inside a Mutex. When a thread needs to use it, it should lock the mutex, perform the operation, and release the mutex guard (after which point other threads will be able to lock it).

This effectively serializes the usage of the counter, which should be fine if the expensive operations are done outside and access to the counter is only needed sometimes.

Another alternative is to have a counter per thread, and "merge" all the results once the threads are done, or on demand, which will increase parallelism.

It already is thread safe. Because the method takes an &mut self, rustc guarantees that it cannot be called from multiple threads. You only have to worry about atomic operations and such when you use locks or atomics.

1 Like