windy1 / zeroconf-rs

zeroconf is a cross-platform library that wraps underlying ZeroConf/mDNS implementations such as Bonjour or Avahi, providing an easy and idiomatic way to both register and browse services.
MIT License
77 stars 25 forks source link

Lifetime issue with EventLoop #43

Closed davidcorrigan714 closed 9 months ago

davidcorrigan714 commented 9 months ago

Having a hard time figuring out a solution to this lifetime problem. Essentially I have a worker thread for registering mdns entries, each of which requires an MdnsService and an EventLoop. Somehow creating the EventLoop is holding a mutable borrow to the MdnsService and I can't quite figure out how to handle the lifetimes. My code is:

pub struct ServiceData<'a> {
    pub service: AvahiMdnsService,
    pub event_loop: AvahiEventLoop<'a>,
}

    fn service_registration_thread(rx_channel: Receiver<ChannelCommand>) {
        let mut services: Vec<ServiceData> = Vec::new();

        loop {
            for message in rx_channel.try_iter() {
                match message {
                    ChannelCommand::NewService(service_ref) => {
                        let mut txt_data = TxtRecord::new();
                        for item in service_ref.txt_records.iter() {
                            txt_data.insert(item.0, item.1).unwrap();
                        }
                        let service_type = ServiceType::new(
                            &service_ref.service_name.to_owned(),
                            &service_ref.protocol.to_owned(),
                        )
                        .unwrap();
136:                        let mut new_service = MdnsService::new(service_type, service_ref.port);
                        new_service.set_name(&service_ref.instance_name.to_owned());
                        new_service.set_txt_record(txt_data);
                        new_service
                            .set_registered_callback(Box::new(MDnsService::on_service_registered));
141:                        let event_loop = [new_service.register](https://github.com/windy1/zeroconf-rs/blob/850c028aa8e85fb3ee441b344e2dbecb8d7f9a0c/zeroconf/src/linux/service.rs#L109C1-L129)().unwrap();

                        let data = ServiceData {
                            service: new_service,
145:                            event_loop: event_loop,
                        };

                        services.push(data)
                    }
                }
            }

            for service in services.iter() {
                service.event_loop.poll(Duration::from_millis(0));
            }
        }
    }

I get the error:

cannot move out of `new_service` because it is borrowed
move out of `new_service` occurs here:
async_service.rs(141, 42): borrow of `new_service` occurs here
async_service.rs(136, 29): binding `new_service` declared here
async_service.rs(145, 41): borrow later used here

It seems to have something to do with how PhantomData in AvahiEventLoop is capturing the mutable borrow, but can't quite tell if there's a way to handle this in my code or if the library really needs to change. I've tried various permutations of Rc & RefCell around new_service but that doesn't seem to help with checking the ownership of that borrow on 141.

windy1 commented 9 months ago

Yes, I believe that the PhantomData is not working as intended. The original intent here was to prevent the service from being dropped before the EventLoop , but I think this was a mistake. Admittedly, I was new to the language at the time and hadn't quite wrapped my head around lifetimes yet.

I have a branch (refactor/arc-rc) that I started a few weeks ago that should resolve this issue by removing the lifetime parameter entirely; instead managing the early-drop problem using Rcs. I will try to wrap that work up soon™️.

davidcorrigan714 commented 9 months ago

I'll have to peak at that branch. The Rcs almost seemed to cause more problems than they solve. May make sense for the EventLoop to just take a reference to poll. When the service drops it just drops a whole bunch of stuff, like the client and context so just keeping poll alive isn't all that helpful.

windy1 commented 9 months ago

This should be fixed in 0.14.0, let me know if you have any issues. Also, just FYI, you should probably be using MdnsService instead of AvahiMdnsService and EventLoop instead of AvahiEventLoop.