5 releases

Uses old Rust 2015

0.1.0 Oct 10, 2017
0.0.4 Apr 26, 2017
0.0.3 Apr 26, 2017
0.0.2 Apr 25, 2017
0.0.1 Apr 25, 2017

#612 in Concurrency

Download history 606/week @ 2023-12-19 534/week @ 2023-12-26 858/week @ 2024-01-02 702/week @ 2024-01-09 875/week @ 2024-01-16 1218/week @ 2024-01-23 821/week @ 2024-01-30 977/week @ 2024-02-06 1120/week @ 2024-02-13 962/week @ 2024-02-20 1328/week @ 2024-02-27 1842/week @ 2024-03-05 1634/week @ 2024-03-12 2164/week @ 2024-03-19 2046/week @ 2024-03-26 929/week @ 2024-04-02

6,961 downloads per month

MPL-2.0 license

9KB
61 lines

swapper

Swap ownership of data between threads in Rust.

This crate allows threads to swap ownership of data without a transitory state where the thread owns nothing.

   let (ab, ba) = swapper::swapper();
   thread::spawn(move || {
      let mut a = String::from("hello");
      ab.swap(&mut a).unwrap();
      assert_eq!(a, "world");
   });
   let mut b = String::from("world");
   ba.swap(&mut b).unwrap();
   assert_eq!(b, "hello");

lib.rs:

Concurrency control for atomic swap of ownership.

A common pattern for thread pools is that each thread owns a token, and some times threads need to exchange tokens. A skeleton example is:

struct Token;
enum Message {
   // Messages go here
};
struct Thread {
   sender_to_other_thread: Sender<Message>,
   receiver_from_other_thread: Receiver<Message>,
   token: Token,
}
impl Thread {
   fn swap_token(&mut self) {
      // This function should swap the token with the other thread.
   }
   fn handle(&mut self, message: Message) {
       match message {
          // Message handlers go here
       }
   }
   fn run(&mut self) {
      loop {
         let message = self.receiver_from_other_thread.recv();
         match message {
            Ok(message) => self.handle(message),
            Err(_) => return,
         }
      }
   }
}

To do this with the Rust channels, ownership of the token is first passed from the thread to the channel, then to the other thead, resulting in a transitory state where the thread does not have the token. Typically to work round this, the thread stores an Option<Token> rather than a Token:

enum Message {
   SwapToken(Token, Sender<Token>),
};
struct Thread {
   sender_to_other_thread: Sender<Message>,
   receiver_from_other_thread: Receiver<Message>,
   token: Option<Token>, // ANNOYING Option
}
impl Thread {
   fn swap_token(&mut self) {
      let (sender, receiver) = mpsc::channel();
      let token = self.token.take().unwrap();
      self.sender_to_other_thread.send(Message::SwapToken(token, sender));
      let token = receiver.recv().unwrap();
      self.token = Some(token);
   }
   fn handle(&mut self, message: Message) {
       match message {
          Message::SwapToken(token, sender) => {
             let token = mem::replace(&mut self.token, Some(token)).unwrap();
             sender.send(token).unwrap();
          }
       }
   }
}

This crate provides a synchronization primitive for swapping ownership between threads. The API is similar to channels, except that rather than separate send(T) and recv():T methods, there is one swap(T):T, which swaps a T owned by one thread for a T owned by the other. For example, it allows an implementation of the thread pool which always owns a token.

enum Message {
   SwapToken(Swapper<Token>),
};
struct Thread {
   sender_to_other_thread: Sender<Message>,
   receiver_from_other_thread: Receiver<Message>,
   token: Token,
}
impl Thread {
   fn swap_token(&mut self) {
      let (our_swapper, their_swapper) = swapper::swapper();
      self.sender_to_other_thread.send(Message::SwapToken(their_swapper));
      our_swapper.swap(&mut self.token).unwrap();
   }
   fn handle(&mut self, message: Message) {
       match message {
          Message::SwapToken(swapper) => swapper.swap(&mut self.token).unwrap(),
       }
   }
}

No runtime deps