Paul Clements
Paul Clements

Reputation: 19

Using a channel to pass message between threads

I need to terminate a loop in a thread in Rust by using (tx, rx) channel in Rust to pass a message to the loop that's running. I have carefully followed chapters 15 -17 of the online rust book to implement a (tx, rx) channel in Rust to terminate a continuously running loop in a thread, but the message won't get through! I am using ICED for the Gui I'm designing and have modified an online ICED example, and it compiles.

use chrono::Utc;
use iced::pure::widget::{Button, Column, Container, Text};
use iced::pure::Sandbox;
use iced::Settings;
use rust_gpiozero::*;
use std::thread;
use std::thread::sleep;
//use std::io::{self, BufRead};
use std::env;
use std::io;
use std::io::BufRead;
use std::process;
use std::sync::mpsc;
use std::time::Duration;

//fn main() -> Result<(), iced::Error> {
//Counter::run(Settings::default())
//}
fn main() -> Result<(), iced::Error> {
    Counter::run(Settings::default())
}

struct Counter {
    count: i32,
}

#[derive(Debug, Clone, Copy)]
enum CounterMessage {
    Increment,
    Decrement,
    Increment2,
    Decrement2,
    Flash1,
    Stop,
}

impl Sandbox for Counter {
    type Message = CounterMessage;

    fn new() -> Self {
        Counter { count: 0 }
    }

    fn title(&self) -> String {
        String::from("Counter app")
    }

    fn update(&mut self, message: Self::Message) {
        let (tx, rx) = mpsc::channel();
        //let _val = String::from("hi");
        match message {
            CounterMessage::Increment => self.count += 1,
            CounterMessage::Decrement => self.count -= 1,
            CounterMessage::Increment2 => self.count += 2,
            CounterMessage::Decrement2 => self.count -= 2,
            CounterMessage::Flash1 => {
                thread::spawn(move || loop {
                    let led = LED::new(17);
                    led.on();
                    sleep(Duration::from_millis(500));
                    led.off();
                    sleep(Duration::from_millis(500));
                    //let r1 = rx.recv().unwrap();
                    //let r1 = rx.recv();
                    match rx.recv() {
                        Ok(_) => {
                            break;
                        }
                        Err(_) => {
                            println!("error!")
                        }
                    }
                    continue;
                    //}
                });
            }

            CounterMessage::Stop => {
                thread::spawn(move || {
                    let _val = String::from("hi");
                    tx.send(_val);
                });
            }
        }
    }

    fn view(&self) -> iced::pure::Element<Self::Message> {
        let label = Text::new(format!("Count: {}", self.count));
        let incr = Button::new("Increment").on_press(CounterMessage::Increment);
        let decr = Button::new("Decrement").on_press(CounterMessage::Decrement);
        let incr2 = Button::new("Increment+2").on_press(CounterMessage::Increment2);
        let decr2 = Button::new("Decrement-2").on_press(CounterMessage::Decrement2);
        let flash1 = Button::new("FLASH1").on_press(CounterMessage::Flash1);
        let stop = Button::new("STOP FLASH!").on_press(CounterMessage::Stop);

        let col = Column::new()
            .push(incr)
            .push(label)
            .push(decr)
            .push(incr2)
            .push(decr2)
            .push(flash1)
            .push(stop);
        Container::new(col)
            .center_x()
            .center_y()
            .width(iced::Length::Fill)
            .height(iced::Length::Fill)
            .into()
    }
}

Upvotes: 0

Views: 430

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 71430

Every time update() is called, it creates a new channel. The channels of the message Flash1 and of the message Stop are just not the same channel. When you send into the channel of Stop, the channel of Flash1 won't receive the message, because they're not connected. The sender part of Flash1 is dropped when we exit update() and the channel is closed, and the receiver part of Stop is also dropped and the channel is closed.

You can store the channel as a field in Counter, so it will be the same channel. One possibility, for example, is to store Option<Sender>, assign to it when the message Flash1 is called, and send into it when the message Stop is called.

Upvotes: 1

Related Questions