1

I'm building a multiplex in rust. It's one of my first applications and a great learning experience!

However, I'm facing a problem and I cannot find out how to solve it in rust:

Whenever a new channel is added to the multiplex, I have to listen for data on this channel. The new channel is allocated on the stack when it is requested by the open() function. However, this channel must not be allocated on the stack but on the heap somehow, because it should stay alive and should not be freed in the next iteration of my receiving loop.

Right now my code looks like this (v0.10-pre):

extern crate collections;
extern crate sync;

use std::comm::{Chan, Port, Select};
use std::mem::size_of_val;
use std::io::ChanWriter;
use std::io::{ChanWriter, PortReader};
use collections::hashmap::HashMap;
use sync::{rendezvous, SyncPort, SyncChan};
use std::task::try;

use std::rc::Rc;

struct MultiplexStream {
    internal_port: Port<(u32, Option<(Port<~[u8]>, Chan<~[u8]>)>)>,
    internal_chan: Chan<u32>
}

impl MultiplexStream {
    fn new(downstream: (Port<~[u8]>, Chan<~[u8]>)) -> ~MultiplexStream {
        let (downstream_port, downstream_chan) = downstream;

        let (p1, c1): (Port<u32>, Chan<u32>) = Chan::new();
        let (p2, c2):
            (Port<(u32, Option<(Port<~[u8]>, Chan<~[u8]>)>)>,
             Chan<(u32, Option<(Port<~[u8]>, Chan<~[u8]>)>)>) = Chan::new();

        let mux = ~MultiplexStream {
            internal_port: p2,
            internal_chan: c1
        };

        spawn(proc() {
            let mut pool = Select::new();
            let mut by_port_num  = HashMap::new();
            let mut by_handle_id = HashMap::new();
            let mut handle_id2port_num = HashMap::new();

            let mut internal_handle = pool.handle(&p1);
            let mut downstream_handle = pool.handle(&downstream_port);

            unsafe {
                internal_handle.add();
                downstream_handle.add();
            }

            loop {
                let handle_id = pool.wait();

                if handle_id == internal_handle.id() {
                    // setup new port
                    let port_num: u32 = p1.recv();

                    if by_port_num.contains_key(&port_num) {
                        c2.send((port_num, None))
                    }
                    else {
                        let (p1_,c1_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();
                        let (p2_,c2_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();

                        /********************************/
                        let mut h = pool.handle(&p1_);  // <--
                        /********************************/
                        /* the error is HERE ^^^        */
                        /********************************/

                        unsafe { h.add() };
                        by_port_num.insert(port_num, c2_);
                        handle_id2port_num.insert(h.id(), port_num);
                        by_handle_id.insert(h.id(), h);
                        c2.send((port_num, Some((p2_,c1_))));
                    }
                }
                else if handle_id == downstream_handle.id() {
                    // demultiplex
                    let res = try(proc() {
                        let mut reader = PortReader::new(downstream_port);
                        let port_num = reader.read_le_u32().unwrap();
                        let data = reader.read_to_end().unwrap();
                        return (port_num, data);
                    });
                    if res.is_ok() {
                        let (port_num, data) = res.unwrap();
                        by_port_num.get(&port_num).send(data);
                    }
                    else {
                        // TODO: handle error
                    }
                }
                else {
                    // multiplex
                    let h = by_handle_id.get_mut(&handle_id);
                    let port_num = handle_id2port_num.get(&handle_id);
                    let port_num = *port_num;
                    let data = h.recv();

                    try(proc() {
                        let mut writer = ChanWriter::new(downstream_chan);
                        writer.write_le_u32(port_num);
                        writer.write(data);
                        writer.flush();
                    });

                    // todo check if chan was closed
                }
            }
        });

        return mux;
    }

    fn open(self, port_num: u32) -> Result<(Port<~[u8]>, Chan<~[u8]>), ()> {
        let res = try(proc() {
            self.internal_chan.send(port_num);
            let (n, res) = self.internal_port.recv();
            assert!(n == port_num);

            return res;
        });

        if res.is_err() {
            return Err(());
        }
        let res = res.unwrap();
        if res.is_none() {
            return Err(());
        }

        let (p,c) = res.unwrap();
        return Ok((p,c));
    }
}

And the compiler raises this error:

multiplex_stream.rs:81:31: 81:35 error: `p1_` does not live long enough
multiplex_stream.rs:81                      let mut h = pool.handle(&p1_);
                                                                    ^~~~
multiplex_stream.rs:48:16: 122:4 note: reference must be valid for the block at 48:15...
multiplex_stream.rs:48      spawn(proc() {
multiplex_stream.rs:49          let mut pool = Select::new();
multiplex_stream.rs:50          let mut by_port_num  = HashMap::new();
multiplex_stream.rs:51          let mut by_handle_id = HashMap::new();
multiplex_stream.rs:52          let mut handle_id2port_num = HashMap::new();
multiplex_stream.rs:53 
                       ...
multiplex_stream.rs:77:11: 87:7 note: ...but borrowed value is only valid for the block at 77:10
multiplex_stream.rs:77                  else {
multiplex_stream.rs:78                      let (p1_,c1_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();
multiplex_stream.rs:79                      let (p2_,c2_): (Port<~[u8]>, Chan<~[u8]>) = Chan::new();
multiplex_stream.rs:80 
multiplex_stream.rs:81                      let mut h = pool.handle(&p1_);
multiplex_stream.rs:82                      unsafe { h.add() };

Does anyone have an idea how to solve this issue?

user2845946
  • 1,755
  • 29
  • 38

1 Answers1

1

The problem is that the new channel that you create does not live long enough—its scope is that of the else block only. You need to ensure that it will live longer—its scope must be at least that of pool.

I haven't made the effort to understand precisely what your code is doing, but what I would expect to be the simplest way to ensure the lifetime of the ports is long enough is to place it into a vector at the same scope as pool, e.g. let ports = ~[];, inserting it with ports.push(p1_); and then taking the reference as &ports[ports.len() - 1]. Sorry, that won't cut it—you can't add new items to a vector while references to its elements are active. You'll need to restructure things somewhat if you want that appraoch to work.

Chris Morgan
  • 86,207
  • 24
  • 208
  • 215
  • That gives `failed to find an implementation of trait std::vec::Vector> for std::comm::Port<~[u8]>` for the `add` function call – user3272410 Feb 25 '14 at 07:27
  • @user3272410: that suggests that you've tried to add the port to the wrong object, that `ports` is not `~[]`? – Chris Morgan Feb 25 '14 at 11:16
  • I defined it like this: `let mut ports: ~[Port<~[u8]>] = ~[];` PS: I'm posting from a different computer now – Manuel Feb 25 '14 at 12:02
  • @Manuel: you're using a different account, too. You should see about merging them. By the way, the type annotations are almost always unnecessary; `let mut ports = ~[];` is sufficient. I can't diagnose your problem without more of the code you've written—it's not clear to me what's going wrong now. – Chris Morgan Feb 25 '14 at 12:07
  • the trick is to use `push()` instead of `add()` However, that gives `error: cannot borrow 'ports' as mutable because 'ports[..]' is also borrowed as immutable multiplex_stream.rs:83 ports.push(p1_); ^~~~~ multiplex_stream.rs:84:31: 84:52 note: previous borrow of 'ports[..]' occurs here; the immutable borrow prevents subsequent moves or mutable borrows of 'ports[..]' until the borrow ends multiplex_stream.rs:84 let mut h = pool.handle(&ports[ports.len()-1]);` – user3272410 Feb 25 '14 at 16:20
  • Oh, dear… that's what comes of thinking of both sets (`add`) and vectors (`push`) while doing it; I then said the wrong one. – Chris Morgan Feb 26 '14 at 02:32