0

I am launching multiple threads from incoming tcp connections which need to read from a file or anything that implements the std::io::Read and std::io::Seek trait.

I have the following working implementation (1):

fn upload<R: Read + Seek + Send>(file: &mut R, listen_addr: SocketAddr) -> IoResult<()> {
  let listener = TcpListener::bind(listen_addr)?;
  let file: Arc<Mutex<&mut R>> = Arc::new(Mutex::new(file));

  std::thread::scope(|scope| {
    for socket in listener.incoming() {
      let socket = socket.unwrap() // error handling omitted ..
      let f = file.clone();
      scope.spawn(move || {
        start_upload_task(socket, f).unwrap();
      });
    }
  }
  Ok(())
}

fn start_upload_task<R: Read + Seek>(conn: TcpStream, file: Arc<Mutex<&mut R>>) -> IoResult<()>{
  // do something wtih file ..
  let mut buf = vec![0u8; 42];
  {
    let mut file = file.lock().unwrap();
    file.seek(SeekFrom::Start(42))?;
    file.read_exact(&mut buf)?;
  }
  Ok(())
}

However, since I want to use wasm_bindgen_futures::spawn_local, the future must be 'static and there is no equivalent to std::thread::scope in rust webassembly that I know of.

What I end up with is the following (2):

fn upload<R: Read + Seek + Send>(file: &mut R, listen_addr: SocketAddr) -> IoResult<()> {
  let listener = TcpListener::bind(listen_addr)?;
  let file: Arc<Mutex<&mut R>> = Arc::new(Mutex::new(file));

  for socket in listener.incoming() {
    let socket = socket.unwrap() // error handling omitted ..
    let f = file.clone();
    std::thread::spawn(move || {
      start_upload_task(socket, f).unwrap();
    });
  }
  Ok(())
}

But this gives me the following compile error:

borrowed data escapes outside of associated function requirement occurs because of the type Mutex<&mut R>, which makes the generic argument &mut R invariant the struct Mutex<T> is invariant over the parameter T see https://doc.rust-lang.org/nomicon/subtyping.html for more information about variance rustcE0521 lib.rs(258, 47): file is a reference that is only valid in the associated function body

with the following suggestion:

   --> src/lib.rs:273:25
    |
258 |       fn upload<R: Read + Seek + Send>(&self, file: &mut R, listen_addr: SocketAddr) -> IoResult<()> {
    |                                                 ----  - let's call the lifetime of this reference `'1`
    |                                                 |
    |                                                 `file` is a reference that is only valid in the associated function body

How do I get implementation (2) to work?

Kevin
  • 3,096
  • 2
  • 8
  • 37
  • Do you control the referent of `file`? If you do, you could wrap it in `Arc>` before you pass it to `upload`. – isaactfa Aug 10 '22 at 11:40
  • @isaactfa I get the same error if I use `file: Arc>` as argument to `upload`. – Kevin Aug 10 '22 at 12:42
  • `fn upload(file: Arc>) -> Ioresult<()> { ... }` does compile however, but I would prefer not passing it as a `'static` reference. – Kevin Aug 10 '22 at 12:44
  • Yeah, you definitely don't want a `&'static mut _` around. I mean, you should wrap whatever `R` you have. I.e. `upload` should take `Arc>` instead of `Arc>`. – isaactfa Aug 10 '22 at 12:52
  • @isaactfa I am still getting problems with lifetime bounds: "the parameter type `R` may not live long enough". – Kevin Aug 10 '22 at 13:02
  • I could add the `'static` trait with `fn upload(...)`, but I think this is the same as using `&' static mut _` ? – Kevin Aug 10 '22 at 13:04
  • It's very much not the same! Any owned value is bounded by `'static`. Whereas when you have a `&'static mut R` you'll never be able to borrow that `R` immutably ever again. – isaactfa Aug 10 '22 at 13:07
  • @isaactfa So the `'static` trait is the right approach here? I would then need to own the data inside the mutex. I was going for `Cursor<&[u8]>` as the `file` argument, but this means I need to change it to `Cursor>`. (`std::io::Cursor` is needed for the `Seek` trait). – Kevin Aug 10 '22 at 13:35
  • Yeah, you'll most likely have to own the data down the line. I can't vouch for `Cursor>` without putting it through the borrow checker but I believe that should work. Let me know. – isaactfa Aug 10 '22 at 13:47

0 Answers0