Sender panic on `Clone`, in some particular condition

Folks,

I am following up on this: Debugging closed channel and ManuallyDrop

Quick recap.

A rust structure that contains a sender:

struct DBKey {
    tx: Sender<Stuff>
    ... other_stuff ...
}

The structure is allocated through Box, let dbkey = Box::new(dbkey).
We get a raw pointer let ptr = Box::into_raw(dbkey).
We pass the pointer to a C function that store it somewhere we are not concerned with.

Later, eventually, we do the trip back.
We ask to a C function the value of that pointer, let ptr = ffi::something();
We read it back let dbkey = ptr.read() (it is a problem if i use std::mem::read() instead of Box::from_raw() in this case?) .
And we clone the sender return Ok(dbkey.tx.clone().

The first time I clone the sender everything works fine. However, the second time it panics with:

thread '<unnamed>' panicked at 'upgrading again', src/libstd/sync/mpsc/oneshot.rs:203:22
stack backtrace:
   0:     0x7f432bf4d104 - backtrace::backtrace::libunwind::trace::heb43798aede8bd30
                               at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/libunwind.rs:88
   1:     0x7f432bf4d104 - backtrace::backtrace::trace_unsynchronized::had2ba7dec4bd2732
                               at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.40/src/backtrace/mod.rs:66
   2:     0x7f432bf4d104 - std::sys_common::backtrace::_print_fmt::hda61f46e822731b2
                               at src/libstd/sys_common/backtrace.rs:84
   3:     0x7f432bf4d104 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hfe37fa5de6572965
                               at src/libstd/sys_common/backtrace.rs:61
   4:     0x7f432bf6ae8c - core::fmt::write::h74887d18db27282c
                               at src/libcore/fmt/mod.rs:1025
   5:     0x7f432bf4b497 - std::io::Write::write_fmt::h6808f3d5eceed5e5
                               at src/libstd/io/mod.rs:1426
   6:     0x7f432bf4f07e - std::sys_common::backtrace::_print::hcc0fd4b3552039ef
                               at src/libstd/sys_common/backtrace.rs:65
   7:     0x7f432bf4f07e - std::sys_common::backtrace::print::h1c9c5c1c0505592d
                               at src/libstd/sys_common/backtrace.rs:50
   8:     0x7f432bf4f07e - std::panicking::default_hook::{{closure}}::hefb6085c1ab83a59
                               at src/libstd/panicking.rs:193
   9:     0x7f432bf4ed71 - std::panicking::default_hook::h1b037d2bf0657ab3
                               at src/libstd/panicking.rs:210
  10:     0x7f432bf4f73b - std::panicking::rust_panic_with_hook::h787d7f532b084b9a
                               at src/libstd/panicking.rs:471
  11:     0x7f432bf403f7 - std::panicking::begin_panic::h0269c2ebc1ade1c9
                               at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:404
  12:     0x7f432bf2c30a - std::sync::mpsc::oneshot::Packet<T>::upgrade::h4f0412a5aacef7bb
                               at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/macros.rs:16
  13:     0x7f432bde6647 - <std::sync::mpsc::Sender<T> as core::clone::Clone>::clone::h3c925c50b0e8ef72
                               at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/sync/mpsc/mod.rs:856
  14:     0x7f432bdc48be - <redisql_lib::redis::NoisyDrop<T> as core::clone::Clone>::clone::hb22cefbfadf74dc4
                               at redisql_lib/src/redis.rs:1537
  15:     0x7f432be054aa - redisql_lib::redis::RedisKey::get_channel::h0d42630b8b3af33d
                               at redisql_lib/src/redis.rs:534
  16:     0x7f432bc97cc7 - redis_sql::v2::exec::do_exec_v2::h3522247cf6d4c905
                               at src/v2/exec.rs:118
  17:     0x7f432bc9561f - redis_sql::v2::exec::Exec_v2::h32953ca646b11ea5
                               at src/v2/exec.rs:37
  18:     0x55e632531128 - RedisModuleCommandDispatcher
                               at /home/smosciat/redis-stable/src/module.c:542
  19:     0x55e6324bcdc5 - call
                               at /home/smosciat/redis-stable/src/server.c:2439
  20:     0x55e6324bd4cf - processCommand
                               at /home/smosciat/redis-stable/src/server.c:2733
  21:     0x55e6324cdfb1 - processInputBuffer
                               at /home/smosciat/redis-stable/src/networking.c:1470
  22:     0x55e6324b6800 - aeProcessEvents
                               at /home/smosciat/redis-stable/src/ae.c:443
  23:     0x55e6324b6beb - aeMain
                               at /home/smosciat/redis-stable/src/ae.c:501
  24:     0x55e6324b3794 - main
                               at /home/smosciat/redis-stable/src/server.c:4200
  25:     0x7f432ed5bb97 - __libc_start_main
  26:     0x55e6324b39ea - _start
  27:                0x0 - <unknown>
fatal runtime error: failed to initiate panic, error 5

I follow a little bit the implementation details of channels and it turns out that channels are implemented differently if you have already send something through them or not, and if you have invoked clone or not.

So I tried another experiment. Before to transform the DBKey to a pointer, so completely in the Rust domain, I tried to clone the channel once and to send a value through it.

Doing so, the panic does not happen anymore and the code works as expected.

The question now is, am I broking some invariant? It is some sort of bug in the channel implementation? Should I report it to somebody? Should I try to reproduce it somehow in the playground?

The main thing that worries me here is that ptr.read() can potentially duplicate the sender without cloning it. E.g. if you drop the copy you just extracted with ptr.read(), you can't later retrieve it again from the pointer. It might also be troublesome if you duplicate it and then call a &mut self method on both duplicates at the same time, as that requires unique access.

And what would be the solution? Using Box::from_raw instead?

Additionally it's possible that if you clone() a duplicate, then mem::forget that duplicate, and then later ptr.read() and clone again, that you have lost some information on the mem::forget. I assume you aren't replacing the sender you ptr.read() from with the one you made a clone of.

I would probably just use unsafe { &*my_raw_pointer } to turn it into a &mut DbKey and access it through that mutable reference.

1 Like

Indeed it seems to be working!

Right I should ptr.read back!

At the moment however this solution. unsafe { &*ptr } seems the better one.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.