Specifying lifetimes in a static ref


#1

Is it possible to specify a non static lifetime from a static ref? For example, In the following code,EthernetInterface requires a non static lifetime (One of its internal methods does not work if the lifetime is static).

lazy_static! {
pub static ref LAZY_FOO:Foo<'a> = Foo{
iface:None,
};
}
pub struct Foo<'a> {
iface : Option<EthernetInterface<'a, 'a, 'a, T>>,
}

This would results in the error, ‘undeclared lifetime’ for 'a in ‘LAZY_FOO’. I cannot use 'static instead of 'a due to limitations in ‘EthernetInterface’. Is there a way around this?


#2

Is this the smoltcp crate?

What is the limitation with 'static exactly? Also, why do you want/need Foo to be a static value itself?


#3

Yes, I am using the smoltcp crate.

I implemented a small UDP server using the smoltcp crate using the code given in examples. However, I want to build a static struct for the server, which should have EthernetInterface as one of its fields (the idea is to call UDP server functions from other parts of the program).

This is the error I’m getting,

error[E0277]: the trait bound `smoltcp::iface::Cache + 'static: core::marker::Send` is not satisfied
  --> network/src/server2.rs:57:1
   |
57 | / lazy_static! {
58 | |         pub static ref SERVER: MutexIrqSafe<Server<'static, EthernetDevice>> = MutexIrqSafe::new(Server{
59 | |               sockets  : SocketSet::new(vec![]),
60 | |               iface: None,
...  |
74 | |         //  };
75 | | } 
   | |_^ `smoltcp::iface::Cache + 'static` cannot be sent between threads safely
   |
   = help: the trait `core::marker::Send` is not implemented for `smoltcp::iface::Cache + 'static`
   = note: required because of the requirements on the impl of `core::marker::Send` for `&'static mut smoltcp::iface::Cache + 'static`
   = note: required because it appears within the type `managed::object::Managed<'static, smoltcp::iface::Cache + 'static>`
   = note: required because it appears within the type `smoltcp::iface::Interface<'static, 'static, 'static, nw_server::EthernetDevice>`
   = note: required because it appears within the type `core::option::Option<smoltcp::iface::Interface<'static, 'static, 'static, nw_server::EthernetDevice>>`
   = note: required because it appears within the type `server2::Server<'static, nw_server::EthernetDevice>`
   = note: required because of the requirements on the impl of `core::marker::Sync` for `irq_safety::MutexIrqSafe<server2::Server<'static, nw_server::EthernetDevice>>`
   = note: required by `lazy_static::lazy::Lazy`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

#4

Ok - that’s telling you that smoltcp::iface::Cache is not Send, which means you cannot share it across threads (i.e. Sync) via a ref to it. lazy_static requires that whatever static value it constructs is shareable across threads. This is not related to 'static. Is your MutexIrqSafe an actual mutex that protects concurrent access? Or do you not plan on using multiple threads here?

What’s the reason for wanting SERVER to be a static value? The conventional approach would be to create this value as part of your process startup, and then wire up components so they have access to it (directly or via some indirection, like a channel).


#5

Yes, `MutexIrqSafe’ protects from concurrent access.

Hmm, I did not quite get what you meant by the conventional approach.

What I would like to do is to have only one instance of the Server where other threads can call it to send or receive packets. Since, send and receive packet functions need to access SocketSet and EthernetInterface, I thought this is the best way to have the intended functionality.

What are the options I have? And thanks, greatly appreciate your help.


#6

Well actually, Cache is not even Send so you can’t even move it to other threads - sort of like an Rc. A mutex wouldn’t even help here. I don’t know enough about smoltcp to know why that is. Would need to find that type there.

So one way to go here, which also bypasses the Cache not being Send, is to create your server in the main (or some dedicated) thread and setup channel(s) for other background threads to send commands back to the main thread. The main thread would pull messages off the channel and send them to the interface. Likewise, the interface thread would read messages/bytes and then send them to whichever background thread should see/handle it. Does that work?


#7

Btw, @whitequark would probably know offhand why Cache isn’t Send.


#8

Yeah, that works. I thought having a static struct would be better than having a dedicated thread for the server.
Again, thanks a lot.


#9

smoltcp doesn’t support the use case where a static interface is created very well. The reason is, well… I don’t actually know how to make it work.

Regarding Cache not being Send, I don’t know either, it looks like something that should be Send to me. Is there a way to make rustc tell me why something isn’t Send?


#10

Is there any raw ptr or Rc stored there?


#11

Hmm so I don’t know what’s up with Cache but SocketSet, which I assume is a Set, appears to bottom out with a RawSocket holding an Rc. I might be wrong as navigating/searching github on mobile isn’t the easiest task :slight_smile:. If this is true, then @n2n’s Server type won’t be Send for that reason alone.


#12

@n2n what version of smoltcp are you using? From the Send trace above it doesn’t look like the current code that’s in the repo.


#13

Ok, finally had time to pull in smoltcp v0.4 (latest available on crates.io, but quite old in absolute terms - from Sep 2017 and current repo code has diverged quite a bit here) in and look at the code in more detail. Interface looks like this:

pub struct Interface<'a, 'b, 'c, DeviceT: Device + 'a> {
    device:         Managed<'a, DeviceT>,
    arp_cache:      Managed<'b, ArpCache>,
    hardware_addr:  EthernetAddress,
    protocol_addrs: ManagedSlice<'c, IpAddress>,
}

ArpCache is a type alias for Cache, but Cache is a trait with no Send supertrait requirement on it:

pub trait Cache {
    /// Update the cache to map given protocol address to given hardware address.
    fn fill(&mut self, protocol_addr: &IpAddress, hardware_addr: &EthernetAddress);

    /// Look up the hardware address corresponding for the given protocol address.
    fn lookup(&mut self, protocol_addr: &IpAddress) -> Option<EthernetAddress>;
}

Managed<'b, ArpCache> is therefore not Send either.


#14

It’s an interesting design question whether arp_cache should’ve been defined as Managed<'b, ArpCache + Send>. Not clear whether a thread local ArpCache impl was envisioned and thus the Send requirement would disallow it.