diff --git a/Cargo.lock b/Cargo.lock index 8768c911cf..2f140121fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3079,7 +3079,7 @@ dependencies = [ "pin-project-lite 0.2.13", "quinn-proto", "quinn-udp", - "rustc-hash 2.0.0", + "rustc-hash", "rustls", "socket2 0.5.7", "thiserror", @@ -3089,14 +3089,14 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.3" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" dependencies = [ "bytes", "rand 0.8.5", "ring", - "rustc-hash 1.1.0", + "rustc-hash", "rustls", "rustls-platform-verifier", "slab", @@ -3416,12 +3416,6 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.0.0" diff --git a/ci/valgrind-check/src/pub_sub/bin/z_pub_sub.rs b/ci/valgrind-check/src/pub_sub/bin/z_pub_sub.rs index f3b1dd0efe..ad96d0b2b0 100644 --- a/ci/valgrind-check/src/pub_sub/bin/z_pub_sub.rs +++ b/ci/valgrind-check/src/pub_sub/bin/z_pub_sub.rs @@ -13,7 +13,7 @@ // use std::time::Duration; -use zenoh::{config::Config, key_expr::KeyExpr, prelude::*}; +use zenoh::{config::Config, key_expr::KeyExpr}; #[tokio::main] async fn main() { diff --git a/ci/valgrind-check/src/queryable_get/bin/z_queryable_get.rs b/ci/valgrind-check/src/queryable_get/bin/z_queryable_get.rs index 8ea7be201b..e82ecba477 100644 --- a/ci/valgrind-check/src/queryable_get/bin/z_queryable_get.rs +++ b/ci/valgrind-check/src/queryable_get/bin/z_queryable_get.rs @@ -16,7 +16,6 @@ use std::{convert::TryFrom, time::Duration}; use zenoh::{ config::Config, key_expr::KeyExpr, - prelude::*, query::{QueryTarget, Selector}, }; diff --git a/commons/zenoh-codec/src/network/mod.rs b/commons/zenoh-codec/src/network/mod.rs index c68a3470aa..65c75f1452 100644 --- a/commons/zenoh-codec/src/network/mod.rs +++ b/commons/zenoh-codec/src/network/mod.rs @@ -76,7 +76,9 @@ where let header: u8 = self.codec.read(&mut *reader)?; let codec = Zenoh080Header::new(header); - codec.read(&mut *reader) + let mut msg: NetworkMessage = codec.read(&mut *reader)?; + msg.reliability = self.reliability; + Ok(msg) } } diff --git a/commons/zenoh-codec/src/transport/batch.rs b/commons/zenoh-codec/src/transport/batch.rs index bfdc21f618..d4fd603864 100644 --- a/commons/zenoh-codec/src/transport/batch.rs +++ b/commons/zenoh-codec/src/transport/batch.rs @@ -150,13 +150,12 @@ where fn write(self, writer: &mut W, x: (&NetworkMessage, &FrameHeader)) -> Self::Output { let (m, f) = x; - // @TODO: m.is_reliable() always return true for the time being - // if let (Reliability::Reliable, false) | (Reliability::BestEffort, true) = - // (f.reliability, m.is_reliable()) - // { - // // We are not serializing on the right frame. - // return Err(BatchError::NewFrame); - // } + if let (Reliability::Reliable, false) | (Reliability::BestEffort, true) = + (f.reliability, m.is_reliable()) + { + // We are not serializing on the right frame. + return Err(BatchError::NewFrame); + } // Mark the write operation let mark = writer.mark(); diff --git a/commons/zenoh-protocol/src/core/mod.rs b/commons/zenoh-protocol/src/core/mod.rs index ebf1bb7f85..629357e339 100644 --- a/commons/zenoh-protocol/src/core/mod.rs +++ b/commons/zenoh-protocol/src/core/mod.rs @@ -346,12 +346,12 @@ impl TryFrom for Priority { #[repr(u8)] pub enum Reliability { #[default] - BestEffort, Reliable, + BestEffort, } impl Reliability { - pub const DEFAULT: Self = Self::BestEffort; + pub const DEFAULT: Self = Self::Reliable; #[cfg(feature = "test")] pub fn rand() -> Self { diff --git a/commons/zenoh-protocol/src/network/mod.rs b/commons/zenoh-protocol/src/network/mod.rs index 407df6dd52..336f952f3d 100644 --- a/commons/zenoh-protocol/src/network/mod.rs +++ b/commons/zenoh-protocol/src/network/mod.rs @@ -30,7 +30,7 @@ pub use push::Push; pub use request::{AtomicRequestId, Request, RequestId}; pub use response::{Response, ResponseFinal}; -use crate::core::{CongestionControl, Priority}; +use crate::core::{CongestionControl, Priority, Reliability}; pub mod id { // WARNING: it's crucial that these IDs do NOT collide with the IDs @@ -83,6 +83,7 @@ pub enum NetworkBody { #[derive(Debug, Clone, PartialEq, Eq)] pub struct NetworkMessage { pub body: NetworkBody, + pub reliability: Reliability, #[cfg(feature = "stats")] pub size: Option, } @@ -109,8 +110,7 @@ impl NetworkMessage { #[inline] pub fn is_reliable(&self) -> bool { - // TODO - true + self.reliability == Reliability::Reliable } #[inline] @@ -179,6 +179,7 @@ impl From for NetworkMessage { fn from(body: NetworkBody) -> Self { Self { body, + reliability: Reliability::DEFAULT, #[cfg(feature = "stats")] size: None, } diff --git a/commons/zenoh-protocol/src/transport/frame.rs b/commons/zenoh-protocol/src/transport/frame.rs index b3ef1d819f..10e55cc99e 100644 --- a/commons/zenoh-protocol/src/transport/frame.rs +++ b/commons/zenoh-protocol/src/transport/frame.rs @@ -95,7 +95,8 @@ impl Frame { let ext_qos = ext::QoSType::rand(); let mut payload = vec![]; for _ in 0..rng.gen_range(1..4) { - let m = NetworkMessage::rand(); + let mut m = NetworkMessage::rand(); + m.reliability = reliability; payload.push(m); } diff --git a/examples/examples/z_forward.rs b/examples/examples/z_forward.rs index be9df7e2b0..1f6969766f 100644 --- a/examples/examples/z_forward.rs +++ b/examples/examples/z_forward.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use clap::Parser; -use zenoh::{key_expr::KeyExpr, prelude::*, Config}; +use zenoh::{key_expr::KeyExpr, Config}; use zenoh_examples::CommonArgs; use zenoh_ext::SubscriberForward; diff --git a/examples/examples/z_get_liveliness.rs b/examples/examples/z_get_liveliness.rs index 53f7abc92a..d0040fcea4 100644 --- a/examples/examples/z_get_liveliness.rs +++ b/examples/examples/z_get_liveliness.rs @@ -14,7 +14,7 @@ use std::time::Duration; use clap::Parser; -use zenoh::{key_expr::KeyExpr, prelude::*, Config}; +use zenoh::{key_expr::KeyExpr, Config}; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_info.rs b/examples/examples/z_info.rs index aa40ef62d4..606cdcbd16 100644 --- a/examples/examples/z_info.rs +++ b/examples/examples/z_info.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use clap::Parser; -use zenoh::{prelude::*, session::ZenohId}; +use zenoh::session::ZenohId; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_liveliness.rs b/examples/examples/z_liveliness.rs index bf8890a267..1c51c2fce6 100644 --- a/examples/examples/z_liveliness.rs +++ b/examples/examples/z_liveliness.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use clap::Parser; -use zenoh::{key_expr::KeyExpr, prelude::*, Config}; +use zenoh::{key_expr::KeyExpr, Config}; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_pong.rs b/examples/examples/z_pong.rs index 86b31d41f3..8794d6bc7a 100644 --- a/examples/examples/z_pong.rs +++ b/examples/examples/z_pong.rs @@ -21,7 +21,7 @@ fn main() { let (config, express) = parse_args(); - let session = zenoh::open(config).wait().unwrap().into_arc(); + let session = zenoh::open(config).wait().unwrap(); // The key expression to read the data from let key_expr_ping = keyexpr::new("test/ping").unwrap(); diff --git a/examples/examples/z_pull.rs b/examples/examples/z_pull.rs index 6716ef8cc5..1239f7347f 100644 --- a/examples/examples/z_pull.rs +++ b/examples/examples/z_pull.rs @@ -14,7 +14,7 @@ use std::time::Duration; use clap::Parser; -use zenoh::{handlers::RingChannel, key_expr::KeyExpr, prelude::*, Config}; +use zenoh::{handlers::RingChannel, key_expr::KeyExpr, Config}; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_queryable.rs b/examples/examples/z_queryable.rs index 4b950a0a33..905f8d50c9 100644 --- a/examples/examples/z_queryable.rs +++ b/examples/examples/z_queryable.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use clap::Parser; -use zenoh::{key_expr::KeyExpr, prelude::*, Config}; +use zenoh::{key_expr::KeyExpr, Config}; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_queryable_shm.rs b/examples/examples/z_queryable_shm.rs index e92efbdc38..6801457d5a 100644 --- a/examples/examples/z_queryable_shm.rs +++ b/examples/examples/z_queryable_shm.rs @@ -15,7 +15,6 @@ use clap::Parser; use zenoh::{ bytes::ZBytes, key_expr::KeyExpr, - prelude::*, shm::{ zshm, BlockOn, GarbageCollect, PosixShmProviderBackend, ShmProviderBuilder, POSIX_PROTOCOL_ID, diff --git a/examples/examples/z_storage.rs b/examples/examples/z_storage.rs index f812c78094..360e00f9d1 100644 --- a/examples/examples/z_storage.rs +++ b/examples/examples/z_storage.rs @@ -19,7 +19,6 @@ use clap::Parser; use futures::select; use zenoh::{ key_expr::{keyexpr, KeyExpr}, - prelude::*, sample::{Sample, SampleKind}, Config, }; diff --git a/examples/examples/z_sub.rs b/examples/examples/z_sub.rs index 7f3a93c5fb..eca53a7849 100644 --- a/examples/examples/z_sub.rs +++ b/examples/examples/z_sub.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use clap::Parser; -use zenoh::{key_expr::KeyExpr, prelude::*, Config}; +use zenoh::{key_expr::KeyExpr, Config}; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_sub_liveliness.rs b/examples/examples/z_sub_liveliness.rs index bb91c9f491..0b70d20786 100644 --- a/examples/examples/z_sub_liveliness.rs +++ b/examples/examples/z_sub_liveliness.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use clap::Parser; -use zenoh::{key_expr::KeyExpr, prelude::*, sample::SampleKind, Config}; +use zenoh::{key_expr::KeyExpr, sample::SampleKind, Config}; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_sub_shm.rs b/examples/examples/z_sub_shm.rs index f45dab099d..1f35a2636e 100644 --- a/examples/examples/z_sub_shm.rs +++ b/examples/examples/z_sub_shm.rs @@ -14,7 +14,7 @@ use clap::Parser; #[cfg(all(feature = "shared-memory", feature = "unstable"))] use zenoh::shm::zshm; -use zenoh::{bytes::ZBytes, config::Config, key_expr::KeyExpr, prelude::*}; +use zenoh::{bytes::ZBytes, config::Config, key_expr::KeyExpr}; use zenoh_examples::CommonArgs; #[tokio::main] diff --git a/examples/examples/z_sub_thr.rs b/examples/examples/z_sub_thr.rs index 78626d1d1d..460a8dfe73 100644 --- a/examples/examples/z_sub_thr.rs +++ b/examples/examples/z_sub_thr.rs @@ -87,9 +87,7 @@ fn main() { } }) .wait() - .unwrap() - // Make the subscriber run in background, until the session is closed. - .background(); + .unwrap(); println!("Press CTRL-C to quit..."); std::thread::park(); diff --git a/io/zenoh-transport/src/common/batch.rs b/io/zenoh-transport/src/common/batch.rs index 5537ec46fb..578adf22d1 100644 --- a/io/zenoh-transport/src/common/batch.rs +++ b/io/zenoh-transport/src/common/batch.rs @@ -570,7 +570,7 @@ mod tests { let mut batch = WBatch::new(config); let tmsg: TransportMessage = KeepAlive.into(); - let nmsg: NetworkMessage = Push { + let mut nmsg: NetworkMessage = Push { wire_expr: WireExpr::empty(), ext_qos: ext::QoSType::new(Priority::DEFAULT, CongestionControl::Block, false), ext_tstamp: None, @@ -601,6 +601,7 @@ mod tests { sn: 0, ext_qos: frame::ext::QoSType::DEFAULT, }; + nmsg.reliability = frame.reliability; // Serialize with a frame batch.encode((&nmsg, &frame)).unwrap(); @@ -608,6 +609,7 @@ mod tests { nmsgs_in.push(nmsg.clone()); frame.reliability = Reliability::BestEffort; + nmsg.reliability = frame.reliability; batch.encode((&nmsg, &frame)).unwrap(); assert_ne!(batch.len(), 0); nmsgs_in.push(nmsg.clone()); diff --git a/io/zenoh-transport/src/common/defragmentation.rs b/io/zenoh-transport/src/common/defragmentation.rs index 476fad632c..481f4b00bd 100644 --- a/io/zenoh-transport/src/common/defragmentation.rs +++ b/io/zenoh-transport/src/common/defragmentation.rs @@ -66,7 +66,11 @@ impl DefragBuffer { pub(crate) fn push(&mut self, sn: TransportSn, zslice: ZSlice) -> ZResult<()> { if sn != self.sn.get() { self.clear(); - bail!("Expected SN {}, received {}", self.sn.get(), sn) + bail!( + "Defragmentation SN error: expected SN {}, received {}", + self.sn.get(), + sn + ) } let new_len = self.len + zslice.len(); diff --git a/io/zenoh-transport/src/common/pipeline.rs b/io/zenoh-transport/src/common/pipeline.rs index 60ea3b215d..8a712d56db 100644 --- a/io/zenoh-transport/src/common/pipeline.rs +++ b/io/zenoh-transport/src/common/pipeline.rs @@ -30,7 +30,7 @@ use zenoh_codec::{transport::batch::BatchError, WCodec, Zenoh080}; use zenoh_config::QueueSizeConf; use zenoh_core::zlock; use zenoh_protocol::{ - core::{Priority, Reliability}, + core::Priority, network::NetworkMessage, transport::{ fragment::FragmentHeader, @@ -220,7 +220,7 @@ impl StageIn { // The Frame let frame = FrameHeader { - reliability: Reliability::Reliable, // TODO + reliability: msg.reliability, sn, ext_qos: frame::ext::QoSType::new(priority), }; diff --git a/io/zenoh-transport/src/multicast/rx.rs b/io/zenoh-transport/src/multicast/rx.rs index 93dc3c727a..8562d5b3eb 100644 --- a/io/zenoh-transport/src/multicast/rx.rs +++ b/io/zenoh-transport/src/multicast/rx.rs @@ -166,11 +166,14 @@ impl TransportMulticastInner { Reliability::BestEffort => zlock!(c.best_effort), }; - self.verify_sn(sn, &mut guard)?; - + if !self.verify_sn(sn, &mut guard)? { + // Drop invalid message and continue + return Ok(()); + } for msg in payload.drain(..) { self.trigger_callback(msg, peer)?; } + Ok(()) } @@ -202,23 +205,30 @@ impl TransportMulticastInner { Reliability::BestEffort => zlock!(c.best_effort), }; - self.verify_sn(sn, &mut guard)?; - + if !self.verify_sn(sn, &mut guard)? { + // Drop invalid message and continue + return Ok(()); + } if guard.defrag.is_empty() { let _ = guard.defrag.sync(sn); } - guard.defrag.push(sn, payload)?; + if let Err(e) = guard.defrag.push(sn, payload) { + // Defrag errors don't close transport + tracing::trace!("{}", e); + return Ok(()); + } if !more { // When shared-memory feature is disabled, msg does not need to be mutable - let msg = guard.defrag.defragment().ok_or_else(|| { - zerror!( + if let Some(msg) = guard.defrag.defragment() { + return self.trigger_callback(msg, peer); + } else { + tracing::trace!( "Transport: {}. Peer: {}. Priority: {:?}. Defragmentation error.", self.manager.config.zid, peer.zid, priority - ) - })?; - return self.trigger_callback(msg, peer); + ); + } } Ok(()) @@ -228,7 +238,7 @@ impl TransportMulticastInner { &self, sn: TransportSn, guard: &mut MutexGuard<'_, TransportChannelRx>, - ) -> ZResult<()> { + ) -> ZResult { let precedes = guard.sn.precedes(sn)?; if !precedes { tracing::debug!( @@ -237,19 +247,14 @@ impl TransportMulticastInner { sn, guard.sn.next() ); - // Drop the fragments if needed - if !guard.defrag.is_empty() { - guard.defrag.clear(); - } - // Keep reading - return Ok(()); + return Ok(false); } // Set will always return OK because we have already checked // with precedes() that the sn has the right resolution let _ = guard.sn.set(sn); - Ok(()) + Ok(true) } pub(super) fn read_messages( diff --git a/io/zenoh-transport/src/unicast/universal/rx.rs b/io/zenoh-transport/src/unicast/universal/rx.rs index afd8e114d7..e69a305876 100644 --- a/io/zenoh-transport/src/unicast/universal/rx.rs +++ b/io/zenoh-transport/src/unicast/universal/rx.rs @@ -97,8 +97,10 @@ impl TransportUnicastUniversal { Reliability::BestEffort => zlock!(c.best_effort), }; - self.verify_sn(sn, &mut guard)?; - + if !self.verify_sn(sn, &mut guard)? { + // Drop invalid message and continue + return Ok(()); + } let callback = zread!(self.callback).clone(); if let Some(callback) = callback.as_ref() { for msg in payload.drain(..) { @@ -111,6 +113,7 @@ impl TransportUnicastUniversal { payload ); } + Ok(()) } @@ -140,28 +143,33 @@ impl TransportUnicastUniversal { Reliability::BestEffort => zlock!(c.best_effort), }; - self.verify_sn(sn, &mut guard)?; - + if !self.verify_sn(sn, &mut guard)? { + // Drop invalid message and continue + return Ok(()); + } if guard.defrag.is_empty() { let _ = guard.defrag.sync(sn); } - guard.defrag.push(sn, payload)?; + if let Err(e) = guard.defrag.push(sn, payload) { + // Defrag errors don't close transport + tracing::trace!("{}", e); + return Ok(()); + } if !more { // When shared-memory feature is disabled, msg does not need to be mutable - let msg = guard - .defrag - .defragment() - .ok_or_else(|| zerror!("Transport: {}. Defragmentation error.", self.config.zid))?; - - let callback = zread!(self.callback).clone(); - if let Some(callback) = callback.as_ref() { - return self.trigger_callback(callback.as_ref(), msg); + if let Some(msg) = guard.defrag.defragment() { + let callback = zread!(self.callback).clone(); + if let Some(callback) = callback.as_ref() { + return self.trigger_callback(callback.as_ref(), msg); + } else { + tracing::debug!( + "Transport: {}. No callback available, dropping messages: {:?}", + self.config.zid, + msg + ); + } } else { - tracing::debug!( - "Transport: {}. No callback available, dropping messages: {:?}", - self.config.zid, - msg - ); + tracing::trace!("Transport: {}. Defragmentation error.", self.config.zid); } } @@ -172,24 +180,19 @@ impl TransportUnicastUniversal { &self, sn: TransportSn, guard: &mut MutexGuard<'_, TransportChannelRx>, - ) -> ZResult<()> { + ) -> ZResult { let precedes = guard.sn.roll(sn)?; if !precedes { - tracing::debug!( + tracing::trace!( "Transport: {}. Frame with invalid SN dropped: {}. Expected: {}.", self.config.zid, sn, - guard.sn.get() + guard.sn.next() ); - // Drop the fragments if needed - if !guard.defrag.is_empty() { - guard.defrag.clear(); - } - // Keep reading - return Ok(()); + return Ok(false); } - Ok(()) + Ok(true) } pub(super) fn read_messages(&self, mut batch: RBatch, link: &Link) -> ZResult<()> { diff --git a/io/zenoh-transport/tests/unicast_defragmentation.rs b/io/zenoh-transport/tests/unicast_defragmentation.rs deleted file mode 100644 index fc54180c96..0000000000 --- a/io/zenoh-transport/tests/unicast_defragmentation.rs +++ /dev/null @@ -1,228 +0,0 @@ -// -// Copyright (c) 2023 ZettaScale Technology -// -// This program and the accompanying materials are made available under the -// terms of the Eclipse Public License 2.0 which is available at -// http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 -// which is available at https://www.apache.org/licenses/LICENSE-2.0. -// -// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 -// -// Contributors: -// ZettaScale Zenoh Team, -// -use std::{convert::TryFrom, sync::Arc, time::Duration}; - -use zenoh_core::ztimeout; -use zenoh_protocol::{ - core::{ - Channel, CongestionControl, Encoding, EndPoint, Priority, Reliability, WhatAmI, - ZenohIdProto, - }, - network::{ - push::{ - ext::{NodeIdType, QoSType}, - Push, - }, - NetworkMessage, - }, - zenoh::Put, -}; -use zenoh_transport::{DummyTransportEventHandler, TransportManager}; - -const TIMEOUT: Duration = Duration::from_secs(60); -const SLEEP: Duration = Duration::from_secs(1); - -const MSG_SIZE: usize = 131_072; -const MSG_DEFRAG_BUF: usize = 128_000; - -async fn run(endpoint: &EndPoint, channel: Channel, msg_size: usize) { - // Define client and router IDs - let client_id = ZenohIdProto::try_from([1]).unwrap(); - let router_id = ZenohIdProto::try_from([2]).unwrap(); - - // Create the router transport manager - let router_manager = TransportManager::builder() - .zid(router_id) - .whatami(WhatAmI::Router) - .defrag_buff_size(MSG_DEFRAG_BUF) - .build(Arc::new(DummyTransportEventHandler)) - .unwrap(); - - // Create the client transport manager - let client_manager = TransportManager::builder() - .whatami(WhatAmI::Client) - .zid(client_id) - .defrag_buff_size(MSG_DEFRAG_BUF) - .build(Arc::new(DummyTransportEventHandler)) - .unwrap(); - - // Create the listener on the router - println!("Add locator: {endpoint}"); - let _ = ztimeout!(router_manager.add_listener(endpoint.clone())).unwrap(); - - // Create an empty transport with the client - // Open transport -> This should be accepted - println!("Opening transport with {endpoint}"); - let _ = ztimeout!(client_manager.open_transport_unicast(endpoint.clone())).unwrap(); - - let client_transport = ztimeout!(client_manager.get_transport_unicast(&router_id)).unwrap(); - - // Create the message to send - let message: NetworkMessage = Push { - wire_expr: "test".into(), - ext_qos: QoSType::new(channel.priority, CongestionControl::Block, false), - ext_tstamp: None, - ext_nodeid: NodeIdType::DEFAULT, - payload: Put { - payload: vec![0u8; msg_size].into(), - timestamp: None, - encoding: Encoding::empty(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_attachment: None, - ext_unknown: vec![], - } - .into(), - } - .into(); - - println!( - "Sending message of {msg_size} bytes while defragmentation buffer size is {MSG_DEFRAG_BUF} bytes" - ); - client_transport.schedule(message.clone()).unwrap(); - - // Wait that the client transport has been closed - ztimeout!(async { - while client_transport.get_zid().is_ok() { - tokio::time::sleep(SLEEP).await; - } - }); - - // Wait on the router manager that the transport has been closed - ztimeout!(async { - while !router_manager.get_transports_unicast().await.is_empty() { - tokio::time::sleep(SLEEP).await; - } - }); - - // Stop the locators on the manager - println!("Del locator: {endpoint}"); - ztimeout!(router_manager.del_listener(endpoint)).unwrap(); - - // Wait a little bit - ztimeout!(async { - while !router_manager.get_listeners().await.is_empty() { - tokio::time::sleep(SLEEP).await; - } - }); - - tokio::time::sleep(SLEEP).await; - - ztimeout!(router_manager.close()); - ztimeout!(client_manager.close()); - - // Wait a little bit - tokio::time::sleep(SLEEP).await; -} - -#[cfg(feature = "transport_tcp")] -#[tokio::test(flavor = "multi_thread", worker_threads = 4)] -async fn transport_unicast_defragmentation_tcp_only() { - zenoh_util::try_init_log_from_env(); - - // Define the locators - let endpoint: EndPoint = format!("tcp/127.0.0.1:{}", 11000).parse().unwrap(); - // Define the reliability and congestion control - let channel = [ - Channel { - priority: Priority::DEFAULT, - reliability: Reliability::Reliable, - }, - Channel { - priority: Priority::DEFAULT, - reliability: Reliability::BestEffort, - }, - Channel { - priority: Priority::RealTime, - reliability: Reliability::Reliable, - }, - Channel { - priority: Priority::RealTime, - reliability: Reliability::BestEffort, - }, - ]; - // Run - for ch in channel.iter() { - run(&endpoint, *ch, MSG_SIZE).await; - } -} - -#[cfg(feature = "transport_ws")] -#[tokio::test(flavor = "multi_thread", worker_threads = 4)] -#[ignore] -async fn transport_unicast_defragmentation_ws_only() { - zenoh_util::try_init_log_from_env(); - - // Define the locators - let endpoint: EndPoint = format!("ws/127.0.0.1:{}", 11010).parse().unwrap(); - // Define the reliability and congestion control - let channel = [ - Channel { - priority: Priority::DEFAULT, - reliability: Reliability::Reliable, - }, - Channel { - priority: Priority::DEFAULT, - reliability: Reliability::BestEffort, - }, - Channel { - priority: Priority::RealTime, - reliability: Reliability::Reliable, - }, - Channel { - priority: Priority::RealTime, - reliability: Reliability::BestEffort, - }, - ]; - // Run - for ch in channel.iter() { - run(&endpoint, *ch, MSG_SIZE).await; - } -} - -#[cfg(feature = "transport_unixpipe")] -#[tokio::test(flavor = "multi_thread", worker_threads = 4)] -#[ignore] -async fn transport_unicast_defragmentation_unixpipe_only() { - zenoh_util::try_init_log_from_env(); - - // Define the locators - let endpoint: EndPoint = "unixpipe/transport_unicast_defragmentation_unixpipe_only" - .parse() - .unwrap(); - // Define the reliability and congestion control - let channel = [ - Channel { - priority: Priority::DEFAULT, - reliability: Reliability::Reliable, - }, - Channel { - priority: Priority::DEFAULT, - reliability: Reliability::BestEffort, - }, - Channel { - priority: Priority::RealTime, - reliability: Reliability::Reliable, - }, - Channel { - priority: Priority::RealTime, - reliability: Reliability::BestEffort, - }, - ]; - // Run - for ch in channel.iter() { - run(&endpoint, *ch, MSG_SIZE).await; - } -} diff --git a/plugins/zenoh-backend-traits/src/config.rs b/plugins/zenoh-backend-traits/src/config.rs index e440e3014e..b791ba5c1a 100644 --- a/plugins/zenoh-backend-traits/src/config.rs +++ b/plugins/zenoh-backend-traits/src/config.rs @@ -164,6 +164,8 @@ impl + AsRef, V: AsObject> TryFrom<(S, &V)> for PluginConfi }) }) .unwrap_or(Ok(true))?; + // TODO(fuzzypixelz): refactor this function's interface to get access to the configuration + // source, this we can support spec syntax in the lib search dir. let backend_search_dirs = match value.get("backend_search_dirs") { Some(serde_json::Value::String(path)) => LibSearchDirs::from_paths(&[path.clone()]), Some(serde_json::Value::Array(paths)) => { @@ -174,7 +176,7 @@ impl + AsRef, V: AsObject> TryFrom<(S, &V)> for PluginConfi }; specs.push(path.clone()); } - LibSearchDirs::from_specs(&specs)? + LibSearchDirs::from_paths(&specs) } None => LibSearchDirs::default(), _ => bail!("`backend_search_dirs` field of {}'s configuration must be a string or array of strings", name.as_ref()) diff --git a/plugins/zenoh-plugin-example/src/lib.rs b/plugins/zenoh-plugin-example/src/lib.rs index b7c494946d..086e23aaa8 100644 --- a/plugins/zenoh-plugin-example/src/lib.rs +++ b/plugins/zenoh-plugin-example/src/lib.rs @@ -36,7 +36,6 @@ use zenoh::{ key_expr::{keyexpr, KeyExpr}, prelude::ZResult, sample::Sample, - session::SessionDeclarations, }; use zenoh_plugin_trait::{plugin_long_version, plugin_version, Plugin, PluginControl}; diff --git a/plugins/zenoh-plugin-rest/examples/z_serve_sse.rs b/plugins/zenoh-plugin-rest/examples/z_serve_sse.rs index aefdfd4f86..fbd0269498 100644 --- a/plugins/zenoh-plugin-rest/examples/z_serve_sse.rs +++ b/plugins/zenoh-plugin-rest/examples/z_serve_sse.rs @@ -18,7 +18,6 @@ use zenoh::{ config::Config, key_expr::keyexpr, qos::{CongestionControl, QoSBuilderTrait}, - session::SessionDeclarations, }; const HTML: &str = r#" diff --git a/plugins/zenoh-plugin-rest/src/lib.rs b/plugins/zenoh-plugin-rest/src/lib.rs index eb65a991d6..289fc9e055 100644 --- a/plugins/zenoh-plugin-rest/src/lib.rs +++ b/plugins/zenoh-plugin-rest/src/lib.rs @@ -47,7 +47,7 @@ use zenoh::{ prelude::*, query::{Parameters, QueryConsolidation, Reply, Selector, ZenohParameters}, sample::{Sample, SampleKind}, - session::{Session, SessionDeclarations}, + session::Session, }; use zenoh_plugin_trait::{plugin_long_version, plugin_version, Plugin, PluginControl}; diff --git a/plugins/zenoh-plugin-storage-manager/src/lib.rs b/plugins/zenoh-plugin-storage-manager/src/lib.rs index ac778f3633..77e53cc80d 100644 --- a/plugins/zenoh-plugin-storage-manager/src/lib.rs +++ b/plugins/zenoh-plugin-storage-manager/src/lib.rs @@ -443,14 +443,16 @@ pub fn strip_prefix( ); } - match key_expr.strip_prefix(prefix).as_slice() { - [stripped_key_expr] => { - if stripped_key_expr.is_empty() { - return Ok(None); - } + // `key_expr.strip_prefix` returns empty vec if `key_expr == prefix`, + // but also returns empty vec if `prefix` is not a prefix to `key_expr`. + // First case needs to be handled before calling `key_expr.strip_prefix` + if key_expr.as_str().eq(prefix.as_str()) { + return Ok(None); + } - OwnedKeyExpr::from_str(stripped_key_expr).map(Some) - } + match key_expr.strip_prefix(prefix).as_slice() { + // NOTE: `stripped_key_expr.is_empty()` should be impossible as "" is not a valid key expression + [stripped_key_expr] => OwnedKeyExpr::from_str(stripped_key_expr).map(Some), _ => bail!("Failed to strip prefix < {} > from: {}", prefix, key_expr), } } diff --git a/plugins/zenoh-plugin-storage-manager/src/replica/mod.rs b/plugins/zenoh-plugin-storage-manager/src/replica/mod.rs index 4766914e21..a9302cd8cf 100644 --- a/plugins/zenoh-plugin-storage-manager/src/replica/mod.rs +++ b/plugins/zenoh-plugin-storage-manager/src/replica/mod.rs @@ -24,7 +24,7 @@ use std::{ use flume::{Receiver, Sender}; use futures::{pin_mut, select, FutureExt}; use tokio::{sync::RwLock, time::interval}; -use zenoh::{key_expr::keyexpr, prelude::*}; +use zenoh::key_expr::keyexpr; use zenoh_backend_traits::config::{ReplicaConfig, StorageConfig}; use crate::{backends_mgt::StoreIntercept, storages_mgt::StorageMessage}; diff --git a/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs b/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs index d2147b137c..94e9d85d82 100644 --- a/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs +++ b/plugins/zenoh-plugin-storage-manager/src/replica/storage.rs @@ -36,7 +36,7 @@ use zenoh::{ }, query::{ConsolidationMode, QueryTarget}, sample::{Sample, SampleBuilder, SampleKind, TimestampBuilderTrait}, - session::{Session, SessionDeclarations}, + session::Session, time::{Timestamp, NTP64}, }; use zenoh_backend_traits::{ diff --git a/zenoh-ext/examples/examples/z_query_sub.rs b/zenoh-ext/examples/examples/z_query_sub.rs index c819a2a831..1c1a3eab27 100644 --- a/zenoh-ext/examples/examples/z_query_sub.rs +++ b/zenoh-ext/examples/examples/z_query_sub.rs @@ -12,7 +12,7 @@ // ZettaScale Zenoh Team, // use clap::{arg, Parser}; -use zenoh::{config::Config, prelude::*, query::ReplyKeyExpr}; +use zenoh::{config::Config, query::ReplyKeyExpr}; use zenoh_ext::*; use zenoh_ext_examples::CommonArgs; diff --git a/zenoh-ext/src/publication_cache.rs b/zenoh-ext/src/publication_cache.rs index 9c1536c2a1..af548a3b6c 100644 --- a/zenoh-ext/src/publication_cache.rs +++ b/zenoh-ext/src/publication_cache.rs @@ -25,14 +25,13 @@ use zenoh::{ pubsub::FlumeSubscriber, query::{Query, Queryable, ZenohParameters}, sample::{Locality, Sample}, - session::{SessionDeclarations, SessionRef}, - Error, Resolvable, Resolve, Result as ZResult, + Error, Resolvable, Resolve, Result as ZResult, Session, }; /// The builder of PublicationCache, allowing to configure it. #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct PublicationCacheBuilder<'a, 'b, 'c> { - session: SessionRef<'a>, + session: &'a Session, pub_key_expr: ZResult>, queryable_prefix: Option>>, queryable_origin: Option, @@ -43,7 +42,7 @@ pub struct PublicationCacheBuilder<'a, 'b, 'c> { impl<'a, 'b, 'c> PublicationCacheBuilder<'a, 'b, 'c> { pub(crate) fn new( - session: SessionRef<'a>, + session: &'a Session, pub_key_expr: ZResult>, ) -> PublicationCacheBuilder<'a, 'b, 'c> { PublicationCacheBuilder { @@ -95,8 +94,8 @@ impl<'a, 'b, 'c> PublicationCacheBuilder<'a, 'b, 'c> { } } -impl<'a> Resolvable for PublicationCacheBuilder<'a, '_, '_> { - type To = ZResult>; +impl Resolvable for PublicationCacheBuilder<'_, '_, '_> { + type To = ZResult; } impl Wait for PublicationCacheBuilder<'_, '_, '_> { @@ -105,7 +104,7 @@ impl Wait for PublicationCacheBuilder<'_, '_, '_> { } } -impl<'a> IntoFuture for PublicationCacheBuilder<'a, '_, '_> { +impl IntoFuture for PublicationCacheBuilder<'_, '_, '_> { type Output = ::To; type IntoFuture = Ready<::To>; @@ -114,14 +113,14 @@ impl<'a> IntoFuture for PublicationCacheBuilder<'a, '_, '_> { } } -pub struct PublicationCache<'a> { - local_sub: FlumeSubscriber<'a>, - _queryable: Queryable<'a, flume::Receiver>, +pub struct PublicationCache { + local_sub: FlumeSubscriber, + _queryable: Queryable>, task: TerminatableTask, } -impl<'a> PublicationCache<'a> { - fn new(conf: PublicationCacheBuilder<'a, '_, '_>) -> ZResult> { +impl PublicationCache { + fn new(conf: PublicationCacheBuilder<'_, '_, '_>) -> ZResult { let key_expr = conf.pub_key_expr?; // the queryable_prefix (optional), and the key_expr for PublicationCache's queryable ("[]/") let (queryable_prefix, queryable_key_expr): (Option, KeyExpr) = @@ -258,7 +257,7 @@ impl<'a> PublicationCache<'a> { /// Undeclare this [`PublicationCache`]`. #[inline] - pub fn undeclare(self) -> impl Resolve> + 'a { + pub fn undeclare(self) -> impl Resolve> { ResolveFuture::new(async move { let PublicationCache { _queryable, diff --git a/zenoh-ext/src/querying_subscriber.rs b/zenoh-ext/src/querying_subscriber.rs index 224abfde87..893cbcbad0 100644 --- a/zenoh-ext/src/querying_subscriber.rs +++ b/zenoh-ext/src/querying_subscriber.rs @@ -28,9 +28,8 @@ use zenoh::{ pubsub::{Reliability, Subscriber}, query::{QueryConsolidation, QueryTarget, ReplyKeyExpr, Selector}, sample::{Locality, Sample, SampleBuilder, TimestampBuilderTrait}, - session::{SessionDeclarations, SessionRef}, time::Timestamp, - Error, Resolvable, Resolve, Result as ZResult, + Error, Resolvable, Resolve, Result as ZResult, Session, }; use crate::ExtractSample; @@ -38,7 +37,7 @@ use crate::ExtractSample; /// The builder of [`FetchingSubscriber`], allowing to configure it. #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] pub struct QueryingSubscriberBuilder<'a, 'b, KeySpace, Handler> { - pub(crate) session: SessionRef<'a>, + pub(crate) session: &'a Session, pub(crate) key_expr: ZResult>, pub(crate) key_space: KeySpace, pub(crate) reliability: Reliability, @@ -224,7 +223,7 @@ where Handler: IntoHandler<'static, Sample>, Handler::Handler: Send, { - type To = ZResult>; + type To = ZResult>; } impl Wait for QueryingSubscriberBuilder<'_, '_, KeySpace, Handler> @@ -362,7 +361,7 @@ pub struct FetchingSubscriberBuilder< > where TryIntoSample: ExtractSample, { - pub(crate) session: SessionRef<'a>, + pub(crate) session: &'a Session, pub(crate) key_expr: ZResult>, pub(crate) key_space: KeySpace, pub(crate) reliability: Reliability, @@ -548,7 +547,7 @@ where Handler::Handler: Send, TryIntoSample: ExtractSample, { - type To = ZResult>; + type To = ZResult>; } impl< @@ -620,28 +619,29 @@ where /// } /// # } /// ``` -pub struct FetchingSubscriber<'a, Handler> { - subscriber: Subscriber<'a, ()>, +pub struct FetchingSubscriber { + subscriber: Subscriber<()>, callback: Arc, state: Arc>, handler: Handler, } -impl std::ops::Deref for FetchingSubscriber<'_, Handler> { +impl std::ops::Deref for FetchingSubscriber { type Target = Handler; fn deref(&self) -> &Self::Target { &self.handler } } -impl std::ops::DerefMut for FetchingSubscriber<'_, Handler> { +impl std::ops::DerefMut for FetchingSubscriber { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.handler } } -impl<'a, Handler> FetchingSubscriber<'a, Handler> { +impl FetchingSubscriber { fn new< + 'a, KeySpace, InputHandler, Fetch: FnOnce(Box) -> ZResult<()> + Send + Sync, @@ -724,7 +724,7 @@ impl<'a, Handler> FetchingSubscriber<'a, Handler> { /// Undeclare this [`FetchingSubscriber`]`. #[inline] - pub fn undeclare(self) -> impl Resolve> + 'a { + pub fn undeclare(self) -> impl Resolve> { self.subscriber.undeclare() } diff --git a/zenoh-ext/src/session_ext.rs b/zenoh-ext/src/session_ext.rs index 606f00743b..21f2fc5c6e 100644 --- a/zenoh-ext/src/session_ext.rs +++ b/zenoh-ext/src/session_ext.rs @@ -11,54 +11,13 @@ // Contributors: // ZettaScale Zenoh Team, // -use std::{convert::TryInto, sync::Arc}; -use zenoh::{ - key_expr::KeyExpr, - session::{Session, SessionRef}, - Error, -}; +use zenoh::{key_expr::KeyExpr, session::Session, Error}; use super::PublicationCacheBuilder; /// Some extensions to the [`zenoh::Session`](zenoh::Session) pub trait SessionExt<'s, 'a> { - fn declare_publication_cache<'b, 'c, TryIntoKeyExpr>( - &'s self, - pub_key_expr: TryIntoKeyExpr, - ) -> PublicationCacheBuilder<'a, 'b, 'c> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into; -} - -impl<'s, 'a> SessionExt<'s, 'a> for SessionRef<'a> { - fn declare_publication_cache<'b, 'c, TryIntoKeyExpr>( - &'s self, - pub_key_expr: TryIntoKeyExpr, - ) -> PublicationCacheBuilder<'a, 'b, 'c> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - PublicationCacheBuilder::new(self.clone(), pub_key_expr.try_into().map_err(Into::into)) - } -} - -impl<'a> SessionExt<'a, 'a> for Session { - fn declare_publication_cache<'b, 'c, TryIntoKeyExpr>( - &'a self, - pub_key_expr: TryIntoKeyExpr, - ) -> PublicationCacheBuilder<'a, 'b, 'c> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - SessionRef::Borrow(self).declare_publication_cache(pub_key_expr) - } -} - -impl<'s> SessionExt<'s, 'static> for Arc { /// Examples: /// ``` /// # #[tokio::main] @@ -69,7 +28,7 @@ impl<'s> SessionExt<'s, 'static> for Arc { /// /// let mut config = zenoh::config::default(); /// config.timestamping.set_enabled(Some(Unique(true))); - /// let session = zenoh::open(config).await.unwrap().into_arc(); + /// let session = zenoh::open(config).await.unwrap(); /// let publication_cache = session.declare_publication_cache("key/expression").await.unwrap(); /// tokio::task::spawn(async move { /// publication_cache.key_expr(); @@ -79,11 +38,21 @@ impl<'s> SessionExt<'s, 'static> for Arc { fn declare_publication_cache<'b, 'c, TryIntoKeyExpr>( &'s self, pub_key_expr: TryIntoKeyExpr, - ) -> PublicationCacheBuilder<'static, 'b, 'c> + ) -> PublicationCacheBuilder<'a, 'b, 'c> + where + TryIntoKeyExpr: TryInto>, + >>::Error: Into; +} + +impl<'a> SessionExt<'a, 'a> for Session { + fn declare_publication_cache<'b, 'c, TryIntoKeyExpr>( + &'a self, + pub_key_expr: TryIntoKeyExpr, + ) -> PublicationCacheBuilder<'a, 'b, 'c> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - SessionRef::Shared(self.clone()).declare_publication_cache(pub_key_expr) + PublicationCacheBuilder::new(self, pub_key_expr.try_into().map_err(Into::into)) } } diff --git a/zenoh-ext/src/subscriber_ext.rs b/zenoh-ext/src/subscriber_ext.rs index a7356f86dc..434ec15234 100644 --- a/zenoh-ext/src/subscriber_ext.rs +++ b/zenoh-ext/src/subscriber_ext.rs @@ -32,7 +32,7 @@ pub trait SubscriberForward<'a, S> { type Output; fn forward(&'a mut self, sink: S) -> Self::Output; } -impl<'a, S> SubscriberForward<'a, S> for Subscriber<'_, flume::Receiver> +impl<'a, S> SubscriberForward<'a, S> for Subscriber> where S: futures::sink::Sink, { diff --git a/zenoh-ext/tests/liveliness.rs b/zenoh-ext/tests/liveliness.rs index 637d07ba57..51c8a79cd3 100644 --- a/zenoh-ext/tests/liveliness.rs +++ b/zenoh-ext/tests/liveliness.rs @@ -21,7 +21,7 @@ use zenoh::{ async fn test_liveliness_querying_subscriber_clique() { use std::time::Duration; - use zenoh::{internal::ztimeout, prelude::*}; + use zenoh::internal::ztimeout; use zenoh_ext::SubscriberBuilderExt; const TIMEOUT: Duration = Duration::from_secs(60); @@ -99,7 +99,7 @@ async fn test_liveliness_querying_subscriber_clique() { async fn test_liveliness_querying_subscriber_brokered() { use std::time::Duration; - use zenoh::{internal::ztimeout, prelude::*}; + use zenoh::internal::ztimeout; use zenoh_ext::SubscriberBuilderExt; const TIMEOUT: Duration = Duration::from_secs(60); diff --git a/zenoh/src/api/admin.rs b/zenoh/src/api/admin.rs index e794c87db5..060bb78c43 100644 --- a/zenoh/src/api/admin.rs +++ b/zenoh/src/api/admin.rs @@ -19,6 +19,8 @@ use std::{ use zenoh_core::{Result as ZResult, Wait}; use zenoh_keyexpr::keyexpr; +#[cfg(feature = "unstable")] +use zenoh_protocol::core::Reliability; use zenoh_protocol::{core::WireExpr, network::NetworkMessage}; use zenoh_transport::{ TransportEventHandler, TransportMulticastEventHandler, TransportPeer, TransportPeerEventHandler, @@ -30,9 +32,9 @@ use super::{ key_expr::KeyExpr, queryable::Query, sample::{DataInfo, Locality, SampleKind}, - session::Session, subscriber::SubscriberKind, }; +use crate::api::session::WeakSession; lazy_static::lazy_static!( static ref KE_STARSTAR: &'static keyexpr = unsafe { keyexpr::from_str_unchecked("**") }; @@ -42,10 +44,10 @@ lazy_static::lazy_static!( static ref KE_LINK: &'static keyexpr = unsafe { keyexpr::from_str_unchecked("link") }; ); -pub(crate) fn init(session: &Session) { - if let Ok(own_zid) = keyexpr::new(&session.zid().to_string()) { +pub(crate) fn init(session: WeakSession) { + if let Ok(own_zid) = keyexpr::new(&session.runtime.zid().to_string()) { let admin_key = KeyExpr::from(*KE_PREFIX / own_zid / *KE_SESSION / *KE_STARSTAR) - .to_wire(session) + .to_wire(&session) .to_owned(); let _admin_qabl = session.declare_queryable_inner( @@ -54,13 +56,13 @@ pub(crate) fn init(session: &Session) { Locality::SessionLocal, Arc::new({ let session = session.clone(); - move |q| super::admin::on_admin_query(&session, q) + move |q| on_admin_query(&session, q) }), ); } } -pub(crate) fn on_admin_query(session: &Session, query: Query) { +pub(crate) fn on_admin_query(session: &WeakSession, query: Query) { fn reply_peer(own_zid: &keyexpr, query: &Query, peer: TransportPeer) { let zid = peer.zid.to_string(); if let Ok(zid) = keyexpr::new(&zid) { @@ -102,7 +104,7 @@ pub(crate) fn on_admin_query(session: &Session, query: Query) { } } - if let Ok(own_zid) = keyexpr::new(&session.zid().to_string()) { + if let Ok(own_zid) = keyexpr::new(&session.runtime.zid().to_string()) { for transport in zenoh_runtime::ZRuntime::Net .block_in_place(session.runtime.manager().get_transports_unicast()) { @@ -122,14 +124,12 @@ pub(crate) fn on_admin_query(session: &Session, query: Query) { #[derive(Clone)] pub(crate) struct Handler { - pub(crate) session: Arc, + pub(crate) session: WeakSession, } impl Handler { - pub(crate) fn new(session: Session) -> Self { - Self { - session: Arc::new(session), - } + pub(crate) fn new(session: WeakSession) -> Self { + Self { session } } } @@ -155,7 +155,7 @@ impl TransportMulticastEventHandler for Handler { &self, peer: zenoh_transport::TransportPeer, ) -> ZResult> { - if let Ok(own_zid) = keyexpr::new(&self.session.zid().to_string()) { + if let Ok(own_zid) = keyexpr::new(&self.session.runtime.zid().to_string()) { if let Ok(zid) = keyexpr::new(&peer.zid.to_string()) { let expr = WireExpr::from( &(*KE_PREFIX / own_zid / *KE_SESSION / *KE_TRANSPORT_UNICAST / zid), @@ -171,6 +171,8 @@ impl TransportMulticastEventHandler for Handler { Some(info), serde_json::to_vec(&peer).unwrap().into(), SubscriberKind::Subscriber, + #[cfg(feature = "unstable")] + Reliability::Reliable, None, ); Ok(Arc::new(PeerHandler { @@ -196,7 +198,7 @@ impl TransportMulticastEventHandler for Handler { pub(crate) struct PeerHandler { pub(crate) expr: WireExpr<'static>, - pub(crate) session: Arc, + pub(crate) session: WeakSession, } impl TransportPeerEventHandler for PeerHandler { @@ -220,6 +222,8 @@ impl TransportPeerEventHandler for PeerHandler { Some(info), serde_json::to_vec(&link).unwrap().into(), SubscriberKind::Subscriber, + #[cfg(feature = "unstable")] + Reliability::Reliable, None, ); } @@ -240,6 +244,8 @@ impl TransportPeerEventHandler for PeerHandler { Some(info), vec![0u8; 0].into(), SubscriberKind::Subscriber, + #[cfg(feature = "unstable")] + Reliability::Reliable, None, ); } @@ -257,6 +263,8 @@ impl TransportPeerEventHandler for PeerHandler { Some(info), vec![0u8; 0].into(), SubscriberKind::Subscriber, + #[cfg(feature = "unstable")] + Reliability::Reliable, None, ); } diff --git a/zenoh/src/api/builders/publisher.rs b/zenoh/src/api/builders/publisher.rs index 666b4378e0..53c32c8a7d 100644 --- a/zenoh/src/api/builders/publisher.rs +++ b/zenoh/src/api/builders/publisher.rs @@ -14,20 +14,24 @@ use std::future::{IntoFuture, Ready}; use zenoh_core::{Resolvable, Result as ZResult, Wait}; +#[cfg(feature = "unstable")] +use zenoh_protocol::core::Reliability; use zenoh_protocol::{core::CongestionControl, network::Mapping}; #[cfg(feature = "unstable")] use crate::api::sample::SourceInfo; -use crate::api::{ - builders::sample::{ - EncodingBuilderTrait, QoSBuilderTrait, SampleBuilderTrait, TimestampBuilderTrait, +use crate::{ + api::{ + builders::sample::{ + EncodingBuilderTrait, QoSBuilderTrait, SampleBuilderTrait, TimestampBuilderTrait, + }, + bytes::{OptionZBytes, ZBytes}, + encoding::Encoding, + key_expr::KeyExpr, + publisher::{Priority, Publisher}, + sample::{Locality, SampleKind}, }, - bytes::{OptionZBytes, ZBytes}, - encoding::Encoding, - key_expr::KeyExpr, - publisher::{Priority, Publisher}, - sample::{Locality, SampleKind}, - session::SessionRef, + Session, }; pub type SessionPutBuilder<'a, 'b> = @@ -111,6 +115,17 @@ impl PublicationBuilder, T> { self.publisher = self.publisher.allowed_destination(destination); self } + /// Change the `reliability` to apply when routing the data. + /// NOTE: Currently `reliability` does not trigger any data retransmission on the wire. + /// It is rather used as a marker on the wire and it may be used to select the best link available (e.g. TCP for reliable data and UDP for best effort data). + #[zenoh_macros::unstable] + #[inline] + pub fn reliability(self, reliability: Reliability) -> Self { + Self { + publisher: self.publisher.reliability(reliability), + ..self + } + } } impl EncodingBuilderTrait for PublisherBuilder<'_, '_> { @@ -232,20 +247,22 @@ impl IntoFuture for PublicationBuilder, PublicationBuil /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] -pub struct PublisherBuilder<'a, 'b: 'a> { - pub(crate) session: SessionRef<'a>, +pub struct PublisherBuilder<'a, 'b> { + pub(crate) session: &'a Session, pub(crate) key_expr: ZResult>, pub(crate) encoding: Encoding, pub(crate) congestion_control: CongestionControl, pub(crate) priority: Priority, pub(crate) is_express: bool, + #[cfg(feature = "unstable")] + pub(crate) reliability: Reliability, pub(crate) destination: Locality, } impl<'a, 'b> Clone for PublisherBuilder<'a, 'b> { fn clone(&self) -> Self { Self { - session: self.session.clone(), + session: self.session, key_expr: match &self.key_expr { Ok(k) => Ok(k.clone()), Err(e) => Err(zerror!("Cloned KE Error: {}", e).into()), @@ -254,6 +271,8 @@ impl<'a, 'b> Clone for PublisherBuilder<'a, 'b> { congestion_control: self.congestion_control, priority: self.priority, is_express: self.is_express, + #[cfg(feature = "unstable")] + reliability: self.reliability, destination: self.destination, } } @@ -294,10 +313,24 @@ impl<'a, 'b> PublisherBuilder<'a, 'b> { self } + /// Change the `reliability`` to apply when routing the data. + /// NOTE: Currently `reliability` does not trigger any data retransmission on the wire. + /// It is rather used as a marker on the wire and it may be used to select the best link available (e.g. TCP for reliable data and UDP for best effort data). + #[zenoh_macros::unstable] + #[inline] + pub fn reliability(self, reliability: Reliability) -> Self { + Self { + reliability, + ..self + } + } + // internal function for performing the publication - fn create_one_shot_publisher(self) -> ZResult> { + fn create_one_shot_publisher(self) -> ZResult> { Ok(Publisher { - session: self.session, + #[cfg(feature = "unstable")] + session_id: self.session.0.runtime.zid(), + session: self.session.downgrade(), id: 0, // This is a one shot Publisher key_expr: self.key_expr?, encoding: self.encoding, @@ -306,6 +339,8 @@ impl<'a, 'b> PublisherBuilder<'a, 'b> { is_express: self.is_express, destination: self.destination, #[cfg(feature = "unstable")] + reliability: self.reliability, + #[cfg(feature = "unstable")] matching_listeners: Default::default(), undeclare_on_drop: true, }) @@ -313,15 +348,15 @@ impl<'a, 'b> PublisherBuilder<'a, 'b> { } impl<'a, 'b> Resolvable for PublisherBuilder<'a, 'b> { - type To = ZResult>; + type To = ZResult>; } impl<'a, 'b> Wait for PublisherBuilder<'a, 'b> { fn wait(self) -> ::To { let mut key_expr = self.key_expr?; - if !key_expr.is_fully_optimized(&self.session) { - let session_id = self.session.id; - let expr_id = self.session.declare_prefix(key_expr.as_str()).wait(); + if !key_expr.is_fully_optimized(&self.session.0) { + let session_id = self.session.0.id; + let expr_id = self.session.0.declare_prefix(key_expr.as_str()).wait()?; let prefix_len = key_expr .len() .try_into() @@ -349,21 +384,27 @@ impl<'a, 'b> Wait for PublisherBuilder<'a, 'b> { } } } - self.session - .declare_publisher_inner(key_expr.clone(), self.destination) - .map(|id| Publisher { - session: self.session, - id, - key_expr, - encoding: self.encoding, - congestion_control: self.congestion_control, - priority: self.priority, - is_express: self.is_express, - destination: self.destination, - #[cfg(feature = "unstable")] - matching_listeners: Default::default(), - undeclare_on_drop: true, - }) + let id = self + .session + .0 + .declare_publisher_inner(key_expr.clone(), self.destination)?; + Ok(Publisher { + #[cfg(feature = "unstable")] + session_id: self.session.0.runtime.zid(), + session: self.session.downgrade(), + id, + key_expr, + encoding: self.encoding, + congestion_control: self.congestion_control, + priority: self.priority, + is_express: self.is_express, + destination: self.destination, + #[cfg(feature = "unstable")] + reliability: self.reliability, + #[cfg(feature = "unstable")] + matching_listeners: Default::default(), + undeclare_on_drop: true, + }) } } diff --git a/zenoh/src/api/builders/sample.rs b/zenoh/src/api/builders/sample.rs index 53cf099448..4c1fa81406 100644 --- a/zenoh/src/api/builders/sample.rs +++ b/zenoh/src/api/builders/sample.rs @@ -16,6 +16,8 @@ use std::marker::PhantomData; use uhlc::Timestamp; use zenoh_core::zresult; use zenoh_protocol::core::CongestionControl; +#[cfg(feature = "unstable")] +use zenoh_protocol::core::Reliability; use crate::api::{ bytes::{OptionZBytes, ZBytes}, @@ -87,6 +89,8 @@ impl SampleBuilder { timestamp: None, qos: QoS::default(), #[cfg(feature = "unstable")] + reliability: Reliability::DEFAULT, + #[cfg(feature = "unstable")] source_info: SourceInfo::empty(), attachment: None, }, @@ -117,6 +121,8 @@ impl SampleBuilder { timestamp: None, qos: QoS::default(), #[cfg(feature = "unstable")] + reliability: Reliability::DEFAULT, + #[cfg(feature = "unstable")] source_info: SourceInfo::empty(), attachment: None, }, @@ -147,6 +153,17 @@ impl SampleBuilder { _t: PhantomData::, } } + + #[zenoh_macros::unstable] + pub fn reliability(self, reliability: Reliability) -> Self { + Self { + sample: Sample { + reliability, + ..self.sample + }, + _t: PhantomData::, + } + } } impl TimestampBuilderTrait for SampleBuilder { diff --git a/zenoh/src/api/info.rs b/zenoh/src/api/info.rs index 32bed0eb53..0993663996 100644 --- a/zenoh/src/api/info.rs +++ b/zenoh/src/api/info.rs @@ -19,7 +19,7 @@ use zenoh_config::wrappers::ZenohId; use zenoh_core::{Resolvable, Wait}; use zenoh_protocol::core::WhatAmI; -use super::session::SessionRef; +use crate::net::runtime::Runtime; /// A builder retuned by [`SessionInfo::zid()`](SessionInfo::zid) that allows /// to access the [`ZenohId`] of the current zenoh [`Session`](crate::Session). @@ -35,9 +35,8 @@ use super::session::SessionRef; /// # } /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] -#[derive(Debug)] pub struct ZenohIdBuilder<'a> { - pub(crate) session: SessionRef<'a>, + runtime: &'a Runtime, } impl<'a> Resolvable for ZenohIdBuilder<'a> { @@ -46,7 +45,7 @@ impl<'a> Resolvable for ZenohIdBuilder<'a> { impl<'a> Wait for ZenohIdBuilder<'a> { fn wait(self) -> Self::To { - self.session.runtime.zid() + self.runtime.zid() } } @@ -75,9 +74,8 @@ impl<'a> IntoFuture for ZenohIdBuilder<'a> { /// # } /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] -#[derive(Debug)] pub struct RoutersZenohIdBuilder<'a> { - pub(crate) session: SessionRef<'a>, + runtime: &'a Runtime, } impl<'a> Resolvable for RoutersZenohIdBuilder<'a> { @@ -88,7 +86,7 @@ impl<'a> Wait for RoutersZenohIdBuilder<'a> { fn wait(self) -> Self::To { Box::new( zenoh_runtime::ZRuntime::Application - .block_in_place(self.session.runtime.manager().get_transports_unicast()) + .block_in_place(self.runtime.manager().get_transports_unicast()) .into_iter() .filter_map(|s| { s.get_whatami() @@ -125,9 +123,8 @@ impl<'a> IntoFuture for RoutersZenohIdBuilder<'a> { /// # } /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] -#[derive(Debug)] pub struct PeersZenohIdBuilder<'a> { - pub(crate) session: SessionRef<'a>, + runtime: &'a Runtime, } impl<'a> Resolvable for PeersZenohIdBuilder<'a> { @@ -138,7 +135,7 @@ impl<'a> Wait for PeersZenohIdBuilder<'a> { fn wait(self) -> ::To { Box::new( zenoh_runtime::ZRuntime::Application - .block_in_place(self.session.runtime.manager().get_transports_unicast()) + .block_in_place(self.runtime.manager().get_transports_unicast()) .into_iter() .filter_map(|s| { s.get_whatami() @@ -159,7 +156,7 @@ impl<'a> IntoFuture for PeersZenohIdBuilder<'a> { } } -/// Struct returned by [`Session::info()`](crate::session::SessionDeclarations::info) which allows +/// Struct returned by [`Session::info()`](crate::Session::info) which allows /// to access information about the current zenoh [`Session`](crate::Session). /// /// # Examples @@ -173,11 +170,11 @@ impl<'a> IntoFuture for PeersZenohIdBuilder<'a> { /// let zid = info.zid().await; /// # } /// ``` -pub struct SessionInfo<'a> { - pub(crate) session: SessionRef<'a>, +pub struct SessionInfo { + pub(crate) runtime: Runtime, } -impl SessionInfo<'_> { +impl SessionInfo { /// Return the [`ZenohId`] of the current zenoh [`Session`](crate::Session). /// /// # Examples @@ -192,7 +189,7 @@ impl SessionInfo<'_> { /// ``` pub fn zid(&self) -> ZenohIdBuilder<'_> { ZenohIdBuilder { - session: self.session.clone(), + runtime: &self.runtime, } } @@ -212,7 +209,7 @@ impl SessionInfo<'_> { /// ``` pub fn routers_zid(&self) -> RoutersZenohIdBuilder<'_> { RoutersZenohIdBuilder { - session: self.session.clone(), + runtime: &self.runtime, } } @@ -231,7 +228,7 @@ impl SessionInfo<'_> { /// ``` pub fn peers_zid(&self) -> PeersZenohIdBuilder<'_> { PeersZenohIdBuilder { - session: self.session.clone(), + runtime: &self.runtime, } } } diff --git a/zenoh/src/api/key_expr.rs b/zenoh/src/api/key_expr.rs index fc472e0db3..6ed0cbcea3 100644 --- a/zenoh/src/api/key_expr.rs +++ b/zenoh/src/api/key_expr.rs @@ -25,7 +25,7 @@ use zenoh_protocol::{ }; use zenoh_result::ZResult; -use super::session::{Session, UndeclarableSealed}; +use super::session::{Session, SessionInner, UndeclarableSealed}; use crate::net::primitives::Primitives; #[derive(Clone, Debug)] @@ -492,7 +492,7 @@ impl<'a> KeyExpr<'a> { //pub(crate) fn is_optimized(&self, session: &Session) -> bool { // matches!(&self.0, KeyExprInner::Wire { expr_id, session_id, .. } | KeyExprInner::BorrowedWire { expr_id, session_id, .. } if *expr_id != 0 && session.id == *session_id) //} - pub(crate) fn is_fully_optimized(&self, session: &Session) -> bool { + pub(crate) fn is_fully_optimized(&self, session: &SessionInner) -> bool { match &self.0 { KeyExprInner::Wire { key_expr, @@ -509,7 +509,7 @@ impl<'a> KeyExpr<'a> { _ => false, } } - pub(crate) fn to_wire(&'a self, session: &Session) -> WireExpr<'a> { + pub(crate) fn to_wire(&'a self, session: &SessionInner) -> WireExpr<'a> { match &self.0 { KeyExprInner::Wire { key_expr, @@ -549,8 +549,10 @@ impl<'a> KeyExpr<'a> { } } -impl<'a> UndeclarableSealed<&'a Session, KeyExprUndeclaration<'a>> for KeyExpr<'a> { - fn undeclare_inner(self, session: &'a Session) -> KeyExprUndeclaration<'a> { +impl<'a> UndeclarableSealed<&'a Session> for KeyExpr<'a> { + type Undeclaration = KeyExprUndeclaration<'a>; + + fn undeclare_inner(self, session: &'a Session) -> Self::Undeclaration { KeyExprUndeclaration { session, expr: self, @@ -592,7 +594,7 @@ impl Wait for KeyExprUndeclaration<'_> { session_id, .. } if *prefix_len as usize == key_expr.len() => { - if *session_id == session.id { + if *session_id == session.0.id { *expr_id } else { return Err(zerror!("Failed to undeclare {}, as it was declared by an other Session", expr).into()) @@ -605,7 +607,7 @@ impl Wait for KeyExprUndeclaration<'_> { session_id, .. } if *prefix_len as usize == key_expr.len() => { - if *session_id == session.id { + if *session_id == session.0.id { *expr_id } else { return Err(zerror!("Failed to undeclare {}, as it was declared by an other Session", expr).into()) @@ -614,10 +616,10 @@ impl Wait for KeyExprUndeclaration<'_> { _ => return Err(zerror!("Failed to undeclare {}, make sure you use the result of `Session::declare_keyexpr` to call `Session::undeclare`", expr).into()), }; tracing::trace!("undeclare_keyexpr({:?})", expr_id); - let mut state = zwrite!(session.state); + let mut state = zwrite!(session.0.state); state.local_resources.remove(&expr_id); - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_declare(zenoh_protocol::network::Declare { interest_id: None, diff --git a/zenoh/src/api/liveliness.rs b/zenoh/src/api/liveliness.rs index 64f87c6de5..ce6a60ca35 100644 --- a/zenoh/src/api/liveliness.rs +++ b/zenoh/src/api/liveliness.rs @@ -15,10 +15,12 @@ use std::{ convert::TryInto, future::{IntoFuture, Ready}, + mem::size_of, sync::Arc, time::Duration, }; +use tracing::error; use zenoh_config::unwrap_or_default; use zenoh_core::{Resolvable, Resolve, Result as ZResult, Wait}; @@ -27,10 +29,11 @@ use super::{ key_expr::KeyExpr, query::Reply, sample::{Locality, Sample}, - session::{Session, SessionRef, UndeclarableSealed}, + session::{Session, UndeclarableSealed}, subscriber::{Subscriber, SubscriberInner}, Id, }; +use crate::api::session::WeakSession; /// A structure with functions to declare a /// [`LivelinessToken`](LivelinessToken), query @@ -94,7 +97,7 @@ use super::{ /// ``` #[zenoh_macros::unstable] pub struct Liveliness<'a> { - pub(crate) session: SessionRef<'a>, + pub(crate) session: &'a Session, } #[zenoh_macros::unstable] @@ -129,7 +132,7 @@ impl<'a> Liveliness<'a> { >>::Error: Into, { LivelinessTokenBuilder { - session: self.session.clone(), + session: self.session, key_expr: TryIntoKeyExpr::try_into(key_expr).map_err(Into::into), } } @@ -166,7 +169,7 @@ impl<'a> Liveliness<'a> { >>::Error: Into, { LivelinessSubscriberBuilder { - session: self.session.clone(), + session: self.session, key_expr: TryIntoKeyExpr::try_into(key_expr).map_err(Into::into), handler: DefaultHandler::default(), } @@ -204,11 +207,11 @@ impl<'a> Liveliness<'a> { { let key_expr = key_expr.try_into().map_err(Into::into); let timeout = { - let conf = self.session.runtime.config().lock(); + let conf = self.session.0.runtime.config().lock(); Duration::from_millis(unwrap_or_default!(conf.queries_default_timeout())) }; LivelinessGetBuilder { - session: &self.session, + session: self.session, key_expr, timeout, handler: DefaultHandler::default(), @@ -236,13 +239,13 @@ impl<'a> Liveliness<'a> { #[zenoh_macros::unstable] #[derive(Debug)] pub struct LivelinessTokenBuilder<'a, 'b> { - pub(crate) session: SessionRef<'a>, + pub(crate) session: &'a Session, pub(crate) key_expr: ZResult>, } #[zenoh_macros::unstable] -impl<'a> Resolvable for LivelinessTokenBuilder<'a, '_> { - type To = ZResult>; +impl Resolvable for LivelinessTokenBuilder<'_, '_> { + type To = ZResult; } #[zenoh_macros::unstable] @@ -252,9 +255,10 @@ impl Wait for LivelinessTokenBuilder<'_, '_> { let session = self.session; let key_expr = self.key_expr?.into_owned(); session + .0 .declare_liveliness_inner(&key_expr) .map(|tok_state| LivelinessToken { - session, + session: self.session.downgrade(), state: tok_state, undeclare_on_drop: true, }) @@ -288,7 +292,7 @@ pub(crate) struct LivelinessTokenState { /// that declared the token has Zenoh connectivity with the Zenoh application /// that monitors it. /// -/// `LivelinessTokens` are automatically undeclared when dropped. +/// Liveliness tokens are automatically undeclared when dropped. /// /// # Examples /// ```no_run @@ -306,13 +310,13 @@ pub(crate) struct LivelinessTokenState { /// ``` #[zenoh_macros::unstable] #[derive(Debug)] -pub struct LivelinessToken<'a> { - pub(crate) session: SessionRef<'a>, - pub(crate) state: Arc, +pub struct LivelinessToken { + session: WeakSession, + state: Arc, undeclare_on_drop: bool, } -/// A [`Resolvable`] returned when undeclaring a [`LivelinessToken`](LivelinessToken). +/// A [`Resolvable`] returned when undeclaring a [`LivelinessToken`]. /// /// # Examples /// ``` @@ -332,26 +336,22 @@ pub struct LivelinessToken<'a> { /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[zenoh_macros::unstable] -pub struct LivelinessTokenUndeclaration<'a> { - token: LivelinessToken<'a>, -} +pub struct LivelinessTokenUndeclaration(LivelinessToken); #[zenoh_macros::unstable] -impl Resolvable for LivelinessTokenUndeclaration<'_> { +impl Resolvable for LivelinessTokenUndeclaration { type To = ZResult<()>; } #[zenoh_macros::unstable] -impl Wait for LivelinessTokenUndeclaration<'_> { +impl Wait for LivelinessTokenUndeclaration { fn wait(mut self) -> ::To { - // set the flag first to avoid double panic if this function panic - self.token.undeclare_on_drop = false; - self.token.session.undeclare_liveliness(self.token.state.id) + self.0.undeclare_impl() } } #[zenoh_macros::unstable] -impl<'a> IntoFuture for LivelinessTokenUndeclaration<'a> { +impl IntoFuture for LivelinessTokenUndeclaration { type Output = ::To; type IntoFuture = Ready<::To>; @@ -361,12 +361,8 @@ impl<'a> IntoFuture for LivelinessTokenUndeclaration<'a> { } #[zenoh_macros::unstable] -impl<'a> LivelinessToken<'a> { - /// Undeclare a [`LivelinessToken`]. - /// - /// LivelinessTokens are automatically closed when dropped, - /// but you may want to use this function to handle errors or - /// undeclare the LivelinessToken asynchronously. +impl LivelinessToken { + /// Undeclare the [`LivelinessToken`]. /// /// # Examples /// ``` @@ -385,33 +381,33 @@ impl<'a> LivelinessToken<'a> { /// # } /// ``` #[inline] - pub fn undeclare(self) -> impl Resolve> + 'a { + pub fn undeclare(self) -> impl Resolve> { UndeclarableSealed::undeclare_inner(self, ()) } - /// Keep this liveliness token in background, until the session is closed. - #[inline] - #[zenoh_macros::unstable] - pub fn background(mut self) { - // It's not necessary to undeclare this resource when session close, as other sessions - // will clean all resources related to the closed one. - // So we can just never undeclare it. + fn undeclare_impl(&mut self) -> ZResult<()> { + // set the flag first to avoid double panic if this function panic self.undeclare_on_drop = false; + self.session.undeclare_liveliness(self.state.id) } } #[zenoh_macros::unstable] -impl<'a> UndeclarableSealed<(), LivelinessTokenUndeclaration<'a>> for LivelinessToken<'a> { - fn undeclare_inner(self, _: ()) -> LivelinessTokenUndeclaration<'a> { - LivelinessTokenUndeclaration { token: self } +impl UndeclarableSealed<()> for LivelinessToken { + type Undeclaration = LivelinessTokenUndeclaration; + + fn undeclare_inner(self, _: ()) -> Self::Undeclaration { + LivelinessTokenUndeclaration(self) } } #[zenoh_macros::unstable] -impl Drop for LivelinessToken<'_> { +impl Drop for LivelinessToken { fn drop(&mut self) { if self.undeclare_on_drop { - let _ = self.session.undeclare_liveliness(self.state.id); + if let Err(error) = self.undeclare_impl() { + error!(error); + } } } } @@ -436,7 +432,7 @@ impl Drop for LivelinessToken<'_> { #[zenoh_macros::unstable] #[derive(Debug)] pub struct LivelinessSubscriberBuilder<'a, 'b, Handler> { - pub session: SessionRef<'a>, + pub session: &'a Session, pub key_expr: ZResult>, pub handler: Handler, } @@ -559,7 +555,7 @@ where Handler: IntoHandler<'static, Sample> + Send, Handler::Handler: Send, { - type To = ZResult>; + type To = ZResult>; } #[zenoh_macros::unstable] @@ -576,13 +572,17 @@ where let session = self.session; let (callback, handler) = self.handler.into_handler(); session + .0 .declare_liveliness_subscriber_inner(&key_expr, Locality::default(), callback) .map(|sub_state| Subscriber { - subscriber: SubscriberInner { - session, + inner: SubscriberInner { + #[cfg(feature = "unstable")] + session_id: session.zid(), + session: self.session.downgrade(), state: sub_state, kind: SubscriberKind::LivelinessSubscriber, - undeclare_on_drop: true, + // `size_of::() == 0` means callback-only subscriber + undeclare_on_drop: size_of::() > 0, }, handler, }) @@ -770,6 +770,7 @@ where fn wait(self) -> ::To { let (callback, receiver) = self.handler.into_handler(); self.session + .0 .liveliness_query(&self.key_expr?, self.timeout, callback) .map(|_| receiver) } diff --git a/zenoh/src/api/publisher.rs b/zenoh/src/api/publisher.rs index c8e0ace03e..0ebcc326ae 100644 --- a/zenoh/src/api/publisher.rs +++ b/zenoh/src/api/publisher.rs @@ -21,9 +21,10 @@ use std::{ }; use futures::Sink; +use tracing::error; use zenoh_core::{zread, Resolvable, Resolve, Wait}; use zenoh_protocol::{ - core::CongestionControl, + core::{CongestionControl, Reliability}, network::{push::ext, Push}, zenoh::{Del, PushBody, Put}, }; @@ -34,11 +35,9 @@ use { handlers::{Callback, DefaultHandler, IntoHandler}, sample::SourceInfo, }, - std::{ - collections::HashSet, - sync::{Arc, Mutex}, - }, + std::{collections::HashSet, sync::Arc, sync::Mutex}, zenoh_config::wrappers::EntityGlobalId, + zenoh_config::ZenohId, zenoh_protocol::core::EntityGlobalIdProto, }; @@ -51,10 +50,10 @@ use super::{ encoding::Encoding, key_expr::KeyExpr, sample::{DataInfo, Locality, QoS, Sample, SampleFields, SampleKind}, - session::{SessionRef, UndeclarableSealed}, + session::UndeclarableSealed, }; use crate::{ - api::{subscriber::SubscriberKind, Id}, + api::{session::WeakSession, subscriber::SubscriberKind, Id}, net::primitives::Primitives, }; @@ -74,35 +73,6 @@ impl fmt::Debug for PublisherState { } } -#[zenoh_macros::unstable] -#[derive(Clone)] -pub enum PublisherRef<'a> { - Borrow(&'a Publisher<'a>), - Shared(std::sync::Arc>), -} - -#[zenoh_macros::unstable] -impl<'a> std::ops::Deref for PublisherRef<'a> { - type Target = Publisher<'a>; - - fn deref(&self) -> &Self::Target { - match self { - PublisherRef::Borrow(b) => b, - PublisherRef::Shared(s) => s, - } - } -} - -#[zenoh_macros::unstable] -impl std::fmt::Debug for PublisherRef<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PublisherRef::Borrow(b) => Publisher::fmt(b, f), - PublisherRef::Shared(s) => Publisher::fmt(s, f), - } - } -} - /// A publisher that allows to send data through a stream. /// /// Publishers are automatically undeclared when dropped. @@ -113,7 +83,7 @@ impl std::fmt::Debug for PublisherRef<'_> { /// # async fn main() { /// use zenoh::prelude::*; /// -/// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); +/// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); /// let publisher = session.declare_publisher("key/expression").await.unwrap(); /// publisher.put("value").await.unwrap(); /// # } @@ -128,7 +98,7 @@ impl std::fmt::Debug for PublisherRef<'_> { /// use futures::StreamExt; /// use zenoh::prelude::*; /// -/// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); +/// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); /// let mut subscriber = session.declare_subscriber("key/expression").await.unwrap(); /// let publisher = session.declare_publisher("another/key/expression").await.unwrap(); /// subscriber.stream().map(Ok).forward(publisher).await.unwrap(); @@ -136,7 +106,9 @@ impl std::fmt::Debug for PublisherRef<'_> { /// ``` #[derive(Debug, Clone)] pub struct Publisher<'a> { - pub(crate) session: SessionRef<'a>, + #[cfg(feature = "unstable")] + pub(crate) session_id: ZenohId, + pub(crate) session: WeakSession, pub(crate) id: Id, pub(crate) key_expr: KeyExpr<'a>, pub(crate) encoding: Encoding, @@ -145,6 +117,8 @@ pub struct Publisher<'a> { pub(crate) is_express: bool, pub(crate) destination: Locality, #[cfg(feature = "unstable")] + pub(crate) reliability: Reliability, + #[cfg(feature = "unstable")] pub(crate) matching_listeners: Arc>>, pub(crate) undeclare_on_drop: bool, } @@ -168,7 +142,7 @@ impl<'a> Publisher<'a> { #[zenoh_macros::unstable] pub fn id(&self) -> EntityGlobalId { EntityGlobalIdProto { - zid: self.session.zid().into(), + zid: self.session_id.into(), eid: self.id, } .into() @@ -197,42 +171,6 @@ impl<'a> Publisher<'a> { self.priority } - /// Consumes the given `Publisher`, returning a thread-safe reference-counting - /// pointer to it (`Arc`). This is equivalent to `Arc::new(Publisher)`. - /// - /// This is useful to share ownership of the `Publisher` between several threads - /// and tasks. It also allows to create [`MatchingListener`] with static - /// lifetime that can be moved to several threads and tasks. - /// - /// Note: the given zenoh `Publisher` will be undeclared when the last reference to - /// it is dropped. - /// - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let publisher = session.declare_publisher("key/expression").await.unwrap().into_arc(); - /// let matching_listener = publisher.matching_listener().await.unwrap(); - /// - /// tokio::task::spawn(async move { - /// while let Ok(matching_status) = matching_listener.recv_async().await { - /// if matching_status.matching_subscribers() { - /// println!("Publisher has matching subscribers."); - /// } else { - /// println!("Publisher has NO MORE matching subscribers."); - /// } - /// } - /// }).await; - /// # } - /// ``` - #[zenoh_macros::unstable] - pub fn into_arc(self) -> std::sync::Arc { - std::sync::Arc::new(self) - } - /// Put data. /// /// # Examples @@ -241,7 +179,7 @@ impl<'a> Publisher<'a> { /// # async fn main() { /// use zenoh::prelude::*; /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); /// let publisher = session.declare_publisher("key/expression").await.unwrap(); /// publisher.put("value").await.unwrap(); /// # } @@ -272,7 +210,7 @@ impl<'a> Publisher<'a> { /// # async fn main() { /// use zenoh::prelude::*; /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); /// let publisher = session.declare_publisher("key/expression").await.unwrap(); /// publisher.delete().await.unwrap(); /// # } @@ -299,7 +237,7 @@ impl<'a> Publisher<'a> { /// # async fn main() { /// use zenoh::prelude::*; /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); /// let publisher = session.declare_publisher("key/expression").await.unwrap(); /// let matching_subscribers: bool = publisher /// .matching_status() @@ -340,14 +278,14 @@ impl<'a> Publisher<'a> { /// # } /// ``` #[zenoh_macros::unstable] - pub fn matching_listener(&self) -> MatchingListenerBuilder<'_, DefaultHandler> { + pub fn matching_listener(&self) -> MatchingListenerBuilder<'_, 'a, DefaultHandler> { MatchingListenerBuilder { - publisher: PublisherRef::Borrow(self), + publisher: self, handler: DefaultHandler::default(), } } - /// Undeclares the [`Publisher`], informing the network that it needn't optimize publications for its key expression anymore. + /// Undeclare the [`Publisher`], informing the network that it needn't optimize publications for its key expression anymore. /// /// # Examples /// ``` @@ -364,107 +302,25 @@ impl<'a> Publisher<'a> { UndeclarableSealed::undeclare_inner(self, ()) } - #[cfg(feature = "unstable")] - fn undeclare_matching_listeners(&self) -> ZResult<()> { - let ids: Vec = zlock!(self.matching_listeners).drain().collect(); - for id in ids { - self.session.undeclare_matches_listener_inner(id)? + fn undeclare_impl(&mut self) -> ZResult<()> { + // set the flag first to avoid double panic if this function panic + self.undeclare_on_drop = false; + #[cfg(feature = "unstable")] + { + let ids: Vec = zlock!(self.matching_listeners).drain().collect(); + for id in ids { + self.session.undeclare_matches_listener_inner(id)? + } } - Ok(()) + self.session.undeclare_publisher_inner(self.id) } } -/// Functions to create zenoh entities with `'static` lifetime. -/// -/// This trait contains functions to create zenoh entities like -/// [`MatchingListener`] with a `'static` lifetime. -/// This is useful to move zenoh entities to several threads and tasks. -/// -/// This trait is implemented for `Arc`. -/// -/// # Examples -/// ```no_run -/// # #[tokio::main] -/// # async fn main() { -/// use zenoh::prelude::*; -/// -/// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); -/// let publisher = session.declare_publisher("key/expression").await.unwrap().into_arc(); -/// let matching_listener = publisher.matching_listener().await.unwrap(); -/// -/// tokio::task::spawn(async move { -/// while let Ok(matching_status) = matching_listener.recv_async().await { -/// if matching_status.matching_subscribers() { -/// println!("Publisher has matching subscribers."); -/// } else { -/// println!("Publisher has NO MORE matching subscribers."); -/// } -/// } -/// }).await; -/// # } -/// ``` -#[zenoh_macros::unstable] -pub trait PublisherDeclarations { - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let publisher = session.declare_publisher("key/expression").await.unwrap().into_arc(); - /// let matching_listener = publisher.matching_listener().await.unwrap(); - /// - /// tokio::task::spawn(async move { - /// while let Ok(matching_status) = matching_listener.recv_async().await { - /// if matching_status.matching_subscribers() { - /// println!("Publisher has matching subscribers."); - /// } else { - /// println!("Publisher has NO MORE matching subscribers."); - /// } - /// } - /// }).await; - /// # } - /// ``` - #[zenoh_macros::unstable] - fn matching_listener(&self) -> MatchingListenerBuilder<'static, DefaultHandler>; -} +impl<'a> UndeclarableSealed<()> for Publisher<'a> { + type Undeclaration = PublisherUndeclaration<'a>; -#[zenoh_macros::unstable] -impl PublisherDeclarations for std::sync::Arc> { - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let publisher = session.declare_publisher("key/expression").await.unwrap().into_arc(); - /// let matching_listener = publisher.matching_listener().await.unwrap(); - /// - /// tokio::task::spawn(async move { - /// while let Ok(matching_status) = matching_listener.recv_async().await { - /// if matching_status.matching_subscribers() { - /// println!("Publisher has matching subscribers."); - /// } else { - /// println!("Publisher has NO MORE matching subscribers."); - /// } - /// } - /// }).await; - /// # } - /// ``` - #[zenoh_macros::unstable] - fn matching_listener(&self) -> MatchingListenerBuilder<'static, DefaultHandler> { - MatchingListenerBuilder { - publisher: PublisherRef::Shared(self.clone()), - handler: DefaultHandler::default(), - } - } -} - -impl<'a> UndeclarableSealed<(), PublisherUndeclaration<'a>> for Publisher<'a> { - fn undeclare_inner(self, _: ()) -> PublisherUndeclaration<'a> { - PublisherUndeclaration { publisher: self } + fn undeclare_inner(self, _: ()) -> Self::Undeclaration { + PublisherUndeclaration(self) } } @@ -482,9 +338,7 @@ impl<'a> UndeclarableSealed<(), PublisherUndeclaration<'a>> for Publisher<'a> { /// # } /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] -pub struct PublisherUndeclaration<'a> { - publisher: Publisher<'a>, -} +pub struct PublisherUndeclaration<'a>(Publisher<'a>); impl Resolvable for PublisherUndeclaration<'_> { type To = ZResult<()>; @@ -492,13 +346,7 @@ impl Resolvable for PublisherUndeclaration<'_> { impl Wait for PublisherUndeclaration<'_> { fn wait(mut self) -> ::To { - // set the flag first to avoid double panic if this function panic - self.publisher.undeclare_on_drop = false; - #[cfg(feature = "unstable")] - self.publisher.undeclare_matching_listeners()?; - self.publisher - .session - .undeclare_publisher_inner(self.publisher.id) + self.0.undeclare_impl() } } @@ -514,9 +362,9 @@ impl IntoFuture for PublisherUndeclaration<'_> { impl Drop for Publisher<'_> { fn drop(&mut self) { if self.undeclare_on_drop { - #[cfg(feature = "unstable")] - let _ = self.undeclare_matching_listeners(); - let _ = self.session.undeclare_publisher_inner(self.id); + if let Err(error) = self.undeclare_impl() { + error!(error); + } } } } @@ -561,6 +409,7 @@ impl<'a> Sink for Publisher<'a> { } impl Publisher<'_> { + #[allow(clippy::too_many_arguments)] // TODO fixme pub(crate) fn resolve_put( &self, payload: ZBytes, @@ -571,51 +420,53 @@ impl Publisher<'_> { attachment: Option, ) -> ZResult<()> { tracing::trace!("write({:?}, [...])", &self.key_expr); - let primitives = zread!(self.session.state) - .primitives - .as_ref() - .unwrap() - .clone(); + let primitives = zread!(self.session.state).primitives()?; let timestamp = if timestamp.is_none() { self.session.runtime.new_timestamp() } else { timestamp }; if self.destination != Locality::SessionLocal { - primitives.send_push(Push { - wire_expr: self.key_expr.to_wire(&self.session).to_owned(), - ext_qos: ext::QoSType::new( - self.priority.into(), - self.congestion_control, - self.is_express, - ), - ext_tstamp: None, - ext_nodeid: ext::NodeIdType::DEFAULT, - payload: match kind { - SampleKind::Put => PushBody::Put(Put { - timestamp, - encoding: encoding.clone().into(), - #[cfg(feature = "unstable")] - ext_sinfo: source_info.into(), - #[cfg(not(feature = "unstable"))] - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_attachment: attachment.clone().map(|a| a.into()), - ext_unknown: vec![], - payload: payload.clone().into(), - }), - SampleKind::Delete => PushBody::Del(Del { - timestamp, - #[cfg(feature = "unstable")] - ext_sinfo: source_info.into(), - #[cfg(not(feature = "unstable"))] - ext_sinfo: None, - ext_attachment: attachment.clone().map(|a| a.into()), - ext_unknown: vec![], - }), + primitives.send_push( + Push { + wire_expr: self.key_expr.to_wire(&self.session).to_owned(), + ext_qos: ext::QoSType::new( + self.priority.into(), + self.congestion_control, + self.is_express, + ), + ext_tstamp: None, + ext_nodeid: ext::NodeIdType::DEFAULT, + payload: match kind { + SampleKind::Put => PushBody::Put(Put { + timestamp, + encoding: encoding.clone().into(), + #[cfg(feature = "unstable")] + ext_sinfo: source_info.into(), + #[cfg(not(feature = "unstable"))] + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_attachment: attachment.clone().map(|a| a.into()), + ext_unknown: vec![], + payload: payload.clone().into(), + }), + SampleKind::Delete => PushBody::Del(Del { + timestamp, + #[cfg(feature = "unstable")] + ext_sinfo: source_info.into(), + #[cfg(not(feature = "unstable"))] + ext_sinfo: None, + ext_attachment: attachment.clone().map(|a| a.into()), + ext_unknown: vec![], + }), + }, }, - }); + #[cfg(feature = "unstable")] + self.reliability, + #[cfg(not(feature = "unstable"))] + Reliability::DEFAULT, + ); } if self.destination != Locality::Remote { let data_info = DataInfo { @@ -637,6 +488,8 @@ impl Publisher<'_> { Some(data_info), payload.into(), SubscriberKind::Subscriber, + #[cfg(feature = "unstable")] + self.reliability, attachment, ); } @@ -743,7 +596,7 @@ impl TryFrom for Priority { /// # async fn main() { /// use zenoh::prelude::*; /// -/// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); +/// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); /// let publisher = session.declare_publisher("key/expression").await.unwrap(); /// let matching_status = publisher.matching_status().await.unwrap(); /// # } @@ -764,7 +617,7 @@ impl MatchingStatus { /// # async fn main() { /// use zenoh::prelude::*; /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); /// let publisher = session.declare_publisher("key/expression").await.unwrap(); /// let matching_subscribers: bool = publisher /// .matching_status() @@ -781,13 +634,13 @@ impl MatchingStatus { /// A builder for initializing a [`MatchingListener`]. #[zenoh_macros::unstable] #[derive(Debug)] -pub struct MatchingListenerBuilder<'a, Handler> { - pub(crate) publisher: PublisherRef<'a>, +pub struct MatchingListenerBuilder<'a, 'b, Handler> { + pub(crate) publisher: &'a Publisher<'b>, pub handler: Handler, } #[zenoh_macros::unstable] -impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { +impl<'a, 'b> MatchingListenerBuilder<'a, 'b, DefaultHandler> { /// Receive the MatchingStatuses for this listener with a callback. /// /// # Examples @@ -813,7 +666,7 @@ impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { /// ``` #[inline] #[zenoh_macros::unstable] - pub fn callback(self, callback: Callback) -> MatchingListenerBuilder<'a, Callback> + pub fn callback(self, callback: Callback) -> MatchingListenerBuilder<'a, 'b, Callback> where Callback: Fn(MatchingStatus) + Send + Sync + 'static, { @@ -850,7 +703,7 @@ impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { pub fn callback_mut( self, callback: CallbackMut, - ) -> MatchingListenerBuilder<'a, impl Fn(MatchingStatus) + Send + Sync + 'static> + ) -> MatchingListenerBuilder<'a, 'b, impl Fn(MatchingStatus) + Send + Sync + 'static> where CallbackMut: FnMut(MatchingStatus) + Send + Sync + 'static, { @@ -883,7 +736,7 @@ impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { /// ``` #[inline] #[zenoh_macros::unstable] - pub fn with(self, handler: Handler) -> MatchingListenerBuilder<'a, Handler> + pub fn with(self, handler: Handler) -> MatchingListenerBuilder<'a, 'b, Handler> where Handler: IntoHandler<'static, MatchingStatus>, { @@ -896,7 +749,7 @@ impl<'a> MatchingListenerBuilder<'a, DefaultHandler> { } #[zenoh_macros::unstable] -impl<'a, Handler> Resolvable for MatchingListenerBuilder<'a, Handler> +impl<'a, 'b, Handler> Resolvable for MatchingListenerBuilder<'a, 'b, Handler> where Handler: IntoHandler<'static, MatchingStatus> + Send, Handler::Handler: Send, @@ -905,7 +758,7 @@ where } #[zenoh_macros::unstable] -impl<'a, Handler> Wait for MatchingListenerBuilder<'a, Handler> +impl<'a, 'b, Handler> Wait for MatchingListenerBuilder<'a, 'b, Handler> where Handler: IntoHandler<'static, MatchingStatus> + Send, Handler::Handler: Send, @@ -916,13 +769,12 @@ where let state = self .publisher .session - .declare_matches_listener_inner(&self.publisher, callback)?; + .declare_matches_listener_inner(self.publisher, callback)?; zlock!(self.publisher.matching_listeners).insert(state.id); Ok(MatchingListener { listener: MatchingListenerInner { - publisher: self.publisher, + publisher: self.publisher.clone(), state, - undeclare_on_drop: true, }, receiver, }) @@ -930,7 +782,7 @@ where } #[zenoh_macros::unstable] -impl<'a, Handler> IntoFuture for MatchingListenerBuilder<'a, Handler> +impl<'a, 'b, Handler> IntoFuture for MatchingListenerBuilder<'a, 'b, Handler> where Handler: IntoHandler<'static, MatchingStatus> + Send, Handler::Handler: Send, @@ -947,15 +799,15 @@ where #[zenoh_macros::unstable] pub(crate) struct MatchingListenerState { pub(crate) id: Id, - pub(crate) current: std::sync::Mutex, + pub(crate) current: Mutex, pub(crate) key_expr: KeyExpr<'static>, pub(crate) destination: Locality, pub(crate) callback: Callback<'static, MatchingStatus>, } #[zenoh_macros::unstable] -impl std::fmt::Debug for MatchingListenerState { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { +impl fmt::Debug for MatchingListenerState { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("MatchingListener") .field("id", &self.id) .field("key_expr", &self.key_expr) @@ -965,9 +817,8 @@ impl std::fmt::Debug for MatchingListenerState { #[zenoh_macros::unstable] pub(crate) struct MatchingListenerInner<'a> { - pub(crate) publisher: PublisherRef<'a>, - pub(crate) state: std::sync::Arc, - undeclare_on_drop: bool, + pub(crate) publisher: Publisher<'a>, + pub(crate) state: Arc, } #[zenoh_macros::unstable] @@ -979,8 +830,10 @@ impl<'a> MatchingListenerInner<'a> { } #[zenoh_macros::unstable] -impl<'a> UndeclarableSealed<(), MatchingListenerUndeclaration<'a>> for MatchingListenerInner<'a> { - fn undeclare_inner(self, _: ()) -> MatchingListenerUndeclaration<'a> { +impl<'a> UndeclarableSealed<()> for MatchingListenerInner<'a> { + type Undeclaration = MatchingListenerUndeclaration<'a>; + + fn undeclare_inner(self, _: ()) -> Self::Undeclaration { MatchingListenerUndeclaration { subscriber: self } } } @@ -988,6 +841,9 @@ impl<'a> UndeclarableSealed<(), MatchingListenerUndeclaration<'a>> for MatchingL /// A listener that sends notifications when the [`MatchingStatus`] of a /// publisher changes. /// +/// Matching litsteners run in background until the publisher is undeclared. +/// They can be manually undeclared, but will not be undeclared on drop. +/// /// # Examples /// ```no_run /// # #[tokio::main] @@ -1014,10 +870,7 @@ pub struct MatchingListener<'a, Receiver> { #[zenoh_macros::unstable] impl<'a, Receiver> MatchingListener<'a, Receiver> { - /// Close a [`MatchingListener`]. - /// - /// MatchingListeners are automatically closed when dropped, but you may want to use this function to handle errors or - /// close the MatchingListener asynchronously. + /// Undeclare the [`MatchingListener`]. /// /// # Examples /// ``` @@ -1035,19 +888,13 @@ impl<'a, Receiver> MatchingListener<'a, Receiver> { pub fn undeclare(self) -> MatchingListenerUndeclaration<'a> { self.listener.undeclare() } - - /// Make the matching listener run in background, until the publisher is undeclared. - #[inline] - #[zenoh_macros::unstable] - pub fn background(mut self) { - // The matching listener will be undeclared as part of publisher undeclaration. - self.listener.undeclare_on_drop = false; - } } #[zenoh_macros::unstable] -impl<'a, T> UndeclarableSealed<(), MatchingListenerUndeclaration<'a>> for MatchingListener<'a, T> { - fn undeclare_inner(self, _: ()) -> MatchingListenerUndeclaration<'a> { +impl<'a, T> UndeclarableSealed<()> for MatchingListener<'a, T> { + type Undeclaration = MatchingListenerUndeclaration<'a>; + + fn undeclare_inner(self, _: ()) -> Self::Undeclaration { UndeclarableSealed::undeclare_inner(self.listener, ()) } } @@ -1079,9 +926,7 @@ impl Resolvable for MatchingListenerUndeclaration<'_> { #[zenoh_macros::unstable] impl Wait for MatchingListenerUndeclaration<'_> { - fn wait(mut self) -> ::To { - // set the flag first to avoid double panic if this function panic - self.subscriber.undeclare_on_drop = false; + fn wait(self) -> ::To { zlock!(self.subscriber.publisher.matching_listeners).remove(&self.subscriber.state.id); self.subscriber .publisher @@ -1100,25 +945,12 @@ impl IntoFuture for MatchingListenerUndeclaration<'_> { } } -#[zenoh_macros::unstable] -impl Drop for MatchingListenerInner<'_> { - fn drop(&mut self) { - if self.undeclare_on_drop { - zlock!(self.publisher.matching_listeners).remove(&self.state.id); - let _ = self - .publisher - .session - .undeclare_matches_listener_inner(self.state.id); - } - } -} - #[cfg(test)] mod tests { use zenoh_config::Config; use zenoh_core::Wait; - use crate::api::{sample::SampleKind, session::SessionDeclarations}; + use crate::api::sample::SampleKind; #[cfg(feature = "internal")] #[test] diff --git a/zenoh/src/api/query.rs b/zenoh/src/api/query.rs index 2a1016db5f..bea028ff97 100644 --- a/zenoh/src/api/query.rs +++ b/zenoh/src/api/query.rs @@ -489,6 +489,7 @@ where parameters, } = self.selector?; self.session + .0 .query( &key_expr, ¶meters, diff --git a/zenoh/src/api/queryable.rs b/zenoh/src/api/queryable.rs index 61ae0093ea..0904fa138e 100644 --- a/zenoh/src/api/queryable.rs +++ b/zenoh/src/api/queryable.rs @@ -14,10 +14,12 @@ use std::{ fmt, future::{IntoFuture, Ready}, + mem::size_of, ops::{Deref, DerefMut}, sync::Arc, }; +use tracing::error; use uhlc::Timestamp; use zenoh_core::{Resolvable, Resolve, Wait}; use zenoh_protocol::{ @@ -28,30 +30,33 @@ use zenoh_protocol::{ use zenoh_result::ZResult; #[zenoh_macros::unstable] use { - super::{query::ReplyKeyExpr, sample::SourceInfo}, - zenoh_config::wrappers::EntityGlobalId, + crate::api::{query::ReplyKeyExpr, sample::SourceInfo}, + zenoh_config::wrappers::{EntityGlobalId, ZenohId}, zenoh_protocol::core::EntityGlobalIdProto, }; #[zenoh_macros::unstable] -use super::selector::ZenohParameters; -use super::{ - builders::sample::{ - EncodingBuilderTrait, QoSBuilderTrait, SampleBuilder, SampleBuilderTrait, - TimestampBuilderTrait, +use crate::api::selector::ZenohParameters; +use crate::{ + api::{ + builders::sample::{ + EncodingBuilderTrait, QoSBuilderTrait, SampleBuilder, SampleBuilderTrait, + TimestampBuilderTrait, + }, + bytes::{OptionZBytes, ZBytes}, + encoding::Encoding, + handlers::{locked, DefaultHandler, IntoHandler}, + key_expr::KeyExpr, + publisher::Priority, + sample::{Locality, QoSBuilder, Sample, SampleKind}, + selector::Selector, + session::{UndeclarableSealed, WeakSession}, + value::Value, + Id, }, - bytes::{OptionZBytes, ZBytes}, - encoding::Encoding, - handlers::{locked, DefaultHandler, IntoHandler}, - key_expr::KeyExpr, - publisher::Priority, - sample::{Locality, QoSBuilder, Sample, SampleKind}, - selector::Selector, - session::{SessionRef, UndeclarableSealed}, - value::Value, - Id, + net::primitives::Primitives, + Session, }; -use crate::net::primitives::Primitives; pub(crate) struct QueryInner { pub(crate) key_expr: KeyExpr<'static>, @@ -534,43 +539,14 @@ impl fmt::Debug for QueryableState { } } -/// An entity able to reply to queries through a callback. -/// -/// CallbackQueryables can be created from a zenoh [`Session`](crate::Session) -/// with the [`declare_queryable`](crate::Session::declare_queryable) function -/// and the [`callback`](QueryableBuilder::callback) function -/// of the resulting builder. -/// -/// Queryables are automatically undeclared when dropped. -/// -/// # Examples -/// ```no_run -/// # #[tokio::main] -/// # async fn main() { -/// use futures::prelude::*; -/// use zenoh::prelude::*; -/// -/// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); -/// let queryable = session.declare_queryable("key/expression").await.unwrap(); -/// while let Ok(query) = queryable.recv_async().await { -/// println!(">> Handling query '{}'", query.selector()); -/// query.reply("key/expression", "value") -/// .await -/// .unwrap(); -/// } -/// # } -/// ``` #[derive(Debug)] -pub(crate) struct CallbackQueryable<'a> { - pub(crate) session: SessionRef<'a>, +pub(crate) struct QueryableInner { + #[cfg(feature = "unstable")] + pub(crate) session_id: ZenohId, + pub(crate) session: WeakSession, pub(crate) state: Arc, - undeclare_on_drop: bool, -} - -impl<'a> UndeclarableSealed<(), QueryableUndeclaration<'a>> for CallbackQueryable<'a> { - fn undeclare_inner(self, _: ()) -> QueryableUndeclaration<'a> { - QueryableUndeclaration { queryable: self } - } + // Queryable is undeclared on drop unless its handler is a ZST, i.e. it is callback-only + pub(crate) undeclare_on_drop: bool, } /// A [`Resolvable`] returned when undeclaring a queryable. @@ -587,25 +563,19 @@ impl<'a> UndeclarableSealed<(), QueryableUndeclaration<'a>> for CallbackQueryabl /// # } /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] -pub struct QueryableUndeclaration<'a> { - queryable: CallbackQueryable<'a>, -} +pub struct QueryableUndeclaration(Queryable); -impl Resolvable for QueryableUndeclaration<'_> { +impl Resolvable for QueryableUndeclaration { type To = ZResult<()>; } -impl Wait for QueryableUndeclaration<'_> { +impl Wait for QueryableUndeclaration { fn wait(mut self) -> ::To { - // set the flag first to avoid double panic if this function panic - self.queryable.undeclare_on_drop = false; - self.queryable - .session - .close_queryable(self.queryable.state.id) + self.0.undeclare_impl() } } -impl<'a> IntoFuture for QueryableUndeclaration<'a> { +impl IntoFuture for QueryableUndeclaration { type Output = ::To; type IntoFuture = Ready<::To>; @@ -614,14 +584,6 @@ impl<'a> IntoFuture for QueryableUndeclaration<'a> { } } -impl Drop for CallbackQueryable<'_> { - fn drop(&mut self) { - if self.undeclare_on_drop { - let _ = self.session.close_queryable(self.state.id); - } - } -} - /// A builder for initializing a [`Queryable`]. /// /// # Examples @@ -637,7 +599,7 @@ impl Drop for CallbackQueryable<'_> { #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] #[derive(Debug)] pub struct QueryableBuilder<'a, 'b, Handler> { - pub(crate) session: SessionRef<'a>, + pub(crate) session: &'a Session, pub(crate) key_expr: ZResult>, pub(crate) complete: bool, pub(crate) origin: Locality, @@ -774,13 +736,41 @@ impl<'a, 'b, Handler> QueryableBuilder<'a, 'b, Handler> { /// A queryable that provides data through a [`Handler`](crate::handlers::IntoHandler). /// /// Queryables can be created from a zenoh [`Session`](crate::Session) -/// with the [`declare_queryable`](crate::session::SessionDeclarations::declare_queryable) function +/// with the [`declare_queryable`](crate::Session::declare_queryable) function /// and the [`with`](QueryableBuilder::with) function /// of the resulting builder. /// -/// Queryables are automatically undeclared when dropped. +/// Callback queryables will run in background until the session is closed, +/// or until it is undeclared. +/// On the other hand, queryables with a handler are automatically undeclared when dropped. /// /// # Examples +/// +/// Using callback: +/// ```no_run +/// # #[tokio::main] +/// # async fn main() { +/// use futures::prelude::*; +/// use zenoh::prelude::*; +/// +/// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); +/// let (tx, rx) = flume::bounded(32); +/// session +/// .declare_queryable("key/expression") +/// .callback(move |query| tx.send(query).unwrap()) +/// .await +/// .unwrap(); +/// // queryable run in background until the session is closed +/// tokio::spawn(async move { +/// while let Ok(query) = rx.recv_async().await { +/// println!(">> Handling query '{}'", query.selector()); +/// query.reply("key/expression", "value").await.unwrap(); +/// } +/// }); +/// # } +/// ``` +/// +/// Using channel handler: /// ```no_run /// # #[tokio::main] /// # async fn main() { @@ -798,16 +788,17 @@ impl<'a, 'b, Handler> QueryableBuilder<'a, 'b, Handler> { /// .await /// .unwrap(); /// } +/// // queryable is undeclared at the end of the scope /// # } /// ``` #[non_exhaustive] #[derive(Debug)] -pub struct Queryable<'a, Handler> { - pub(crate) queryable: CallbackQueryable<'a>, +pub struct Queryable { + pub(crate) inner: QueryableInner, pub(crate) handler: Handler, } -impl<'a, Handler> Queryable<'a, Handler> { +impl Queryable { /// Returns the [`EntityGlobalId`] of this Queryable. /// /// # Examples @@ -826,8 +817,8 @@ impl<'a, Handler> Queryable<'a, Handler> { #[zenoh_macros::unstable] pub fn id(&self) -> EntityGlobalId { EntityGlobalIdProto { - zid: self.queryable.session.zid().into(), - eid: self.queryable.state.id, + zid: self.inner.session_id.into(), + eid: self.inner.state.id, } .into() } @@ -846,29 +837,55 @@ impl<'a, Handler> Queryable<'a, Handler> { &mut self.handler } + /// Undeclare the [`Queryable`]. + /// + /// # Examples + /// ``` + /// # #[tokio::main] + /// # async fn main() { + /// use zenoh::prelude::*; + /// + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); + /// let queryable = session.declare_queryable("key/expression") + /// .await + /// .unwrap(); + /// queryable.undeclare().await.unwrap(); + /// # } + /// ``` #[inline] - pub fn undeclare(self) -> impl Resolve> + 'a { + pub fn undeclare(self) -> impl Resolve> + where + Handler: Send, + { UndeclarableSealed::undeclare_inner(self, ()) } - /// Make the queryable run in background, until the session is closed. - #[inline] - #[zenoh_macros::unstable] - pub fn background(mut self) { - // It's not necessary to undeclare this resource when session close, as other sessions - // will clean all resources related to the closed one. - // So we can just never undeclare it. - self.queryable.undeclare_on_drop = false; + fn undeclare_impl(&mut self) -> ZResult<()> { + // set the flag first to avoid double panic if this function panic + self.inner.undeclare_on_drop = false; + self.inner.session.close_queryable(self.inner.state.id) } } -impl<'a, T> UndeclarableSealed<(), QueryableUndeclaration<'a>> for Queryable<'a, T> { - fn undeclare_inner(self, _: ()) -> QueryableUndeclaration<'a> { - UndeclarableSealed::undeclare_inner(self.queryable, ()) +impl Drop for Queryable { + fn drop(&mut self) { + if self.inner.undeclare_on_drop { + if let Err(error) = self.undeclare_impl() { + error!(error); + } + } + } +} + +impl UndeclarableSealed<()> for Queryable { + type Undeclaration = QueryableUndeclaration; + + fn undeclare_inner(self, _: ()) -> Self::Undeclaration { + QueryableUndeclaration(self) } } -impl Deref for Queryable<'_, Handler> { +impl Deref for Queryable { type Target = Handler; fn deref(&self) -> &Self::Target { @@ -876,21 +893,21 @@ impl Deref for Queryable<'_, Handler> { } } -impl DerefMut for Queryable<'_, Handler> { +impl DerefMut for Queryable { fn deref_mut(&mut self) -> &mut Self::Target { self.handler_mut() } } -impl<'a, Handler> Resolvable for QueryableBuilder<'a, '_, Handler> +impl Resolvable for QueryableBuilder<'_, '_, Handler> where Handler: IntoHandler<'static, Query> + Send, Handler::Handler: Send, { - type To = ZResult>; + type To = ZResult>; } -impl<'a, Handler> Wait for QueryableBuilder<'a, '_, Handler> +impl Wait for QueryableBuilder<'_, '_, Handler> where Handler: IntoHandler<'static, Query> + Send, Handler::Handler: Send, @@ -899,24 +916,28 @@ where let session = self.session; let (callback, receiver) = self.handler.into_handler(); session + .0 .declare_queryable_inner( - &self.key_expr?.to_wire(&session), + &self.key_expr?.to_wire(&session.0), self.complete, self.origin, callback, ) .map(|qable_state| Queryable { - queryable: CallbackQueryable { - session, + inner: QueryableInner { + #[cfg(feature = "unstable")] + session_id: session.zid(), + session: self.session.downgrade(), state: qable_state, - undeclare_on_drop: true, + // `size_of::() == 0` means callback-only queryable + undeclare_on_drop: size_of::() > 0, }, handler: receiver, }) } } -impl<'a, Handler> IntoFuture for QueryableBuilder<'a, '_, Handler> +impl IntoFuture for QueryableBuilder<'_, '_, Handler> where Handler: IntoHandler<'static, Query> + Send, Handler::Handler: Send, diff --git a/zenoh/src/api/sample.rs b/zenoh/src/api/sample.rs index 220785c668..253e98d4b5 100644 --- a/zenoh/src/api/sample.rs +++ b/zenoh/src/api/sample.rs @@ -18,6 +18,8 @@ use std::{convert::TryFrom, fmt}; #[cfg(feature = "unstable")] use serde::Serialize; use zenoh_config::wrappers::EntityGlobalId; +#[cfg(feature = "unstable")] +use zenoh_protocol::core::Reliability; use zenoh_protocol::{ core::{CongestionControl, Timestamp}, network::declare::ext::QoSType, @@ -63,6 +65,7 @@ pub(crate) trait DataInfoIntoSample { self, key_expr: IntoKeyExpr, payload: IntoZBytes, + #[cfg(feature = "unstable")] reliability: Reliability, attachment: Option, ) -> Sample where @@ -80,6 +83,7 @@ impl DataInfoIntoSample for DataInfo { self, key_expr: IntoKeyExpr, payload: IntoZBytes, + #[cfg(feature = "unstable")] reliability: Reliability, attachment: Option, ) -> Sample where @@ -94,6 +98,8 @@ impl DataInfoIntoSample for DataInfo { timestamp: self.timestamp, qos: self.qos, #[cfg(feature = "unstable")] + reliability, + #[cfg(feature = "unstable")] source_info: SourceInfo { source_id: self.source_id, source_sn: self.source_sn, @@ -109,6 +115,7 @@ impl DataInfoIntoSample for Option { self, key_expr: IntoKeyExpr, payload: IntoZBytes, + #[cfg(feature = "unstable")] reliability: Reliability, attachment: Option, ) -> Sample where @@ -116,7 +123,13 @@ impl DataInfoIntoSample for Option { IntoZBytes: Into, { if let Some(data_info) = self { - data_info.into_sample(key_expr, payload, attachment) + data_info.into_sample( + key_expr, + payload, + #[cfg(feature = "unstable")] + reliability, + attachment, + ) } else { Sample { key_expr: key_expr.into(), @@ -126,6 +139,8 @@ impl DataInfoIntoSample for Option { timestamp: None, qos: QoS::default(), #[cfg(feature = "unstable")] + reliability, + #[cfg(feature = "unstable")] source_info: SourceInfo::empty(), attachment, } @@ -252,6 +267,8 @@ pub struct SampleFields { pub priority: Priority, pub congestion_control: CongestionControl, #[cfg(feature = "unstable")] + pub reliability: Reliability, + #[cfg(feature = "unstable")] pub source_info: SourceInfo, pub attachment: Option, } @@ -268,6 +285,8 @@ impl From for SampleFields { priority: sample.qos.priority(), congestion_control: sample.qos.congestion_control(), #[cfg(feature = "unstable")] + reliability: sample.reliability, + #[cfg(feature = "unstable")] source_info: sample.source_info, attachment: sample.attachment, } @@ -285,6 +304,8 @@ pub struct Sample { pub(crate) timestamp: Option, pub(crate) qos: QoS, #[cfg(feature = "unstable")] + pub(crate) reliability: Reliability, + #[cfg(feature = "unstable")] pub(crate) source_info: SourceInfo, pub(crate) attachment: Option, } @@ -336,6 +357,12 @@ impl Sample { self.qos.priority() } + /// Gets the reliability of this Sample + #[zenoh_macros::unstable] + pub fn reliability(&self) -> Reliability { + self.reliability + } + /// Gets the express flag value. If `true`, the message is not batched during transmission, in order to reduce latency. pub fn express(&self) -> bool { self.qos.express() diff --git a/zenoh/src/api/session.rs b/zenoh/src/api/session.rs index 451c1340ad..cf6cfd32da 100644 --- a/zenoh/src/api/session.rs +++ b/zenoh/src/api/session.rs @@ -19,7 +19,7 @@ use std::{ ops::Deref, sync::{ atomic::{AtomicU16, Ordering}, - Arc, RwLock, + Arc, Mutex, RwLock, }, time::{Duration, SystemTime, UNIX_EPOCH}, }; @@ -39,7 +39,8 @@ use zenoh_protocol::network::{ use zenoh_protocol::{ core::{ key_expr::{keyexpr, OwnedKeyExpr}, - AtomicExprId, CongestionControl, EntityId, ExprId, Parameters, WireExpr, EMPTY_EXPR_ID, + AtomicExprId, CongestionControl, EntityId, ExprId, Parameters, Reliability, WireExpr, + EMPTY_EXPR_ID, }, network::{ self, @@ -101,8 +102,6 @@ use crate::net::{ routing::dispatcher::face::Face, runtime::{Runtime, RuntimeBuilder}, }; -#[cfg(feature = "unstable")] -use crate::pubsub::Reliability; zconfigurable! { pub(crate) static ref API_DATA_RECEPTION_CHANNEL_SIZE: usize = 256; @@ -175,6 +174,14 @@ impl SessionState { } impl SessionState { + #[inline] + pub(crate) fn primitives(&self) -> ZResult> { + self.primitives + .as_ref() + .cloned() + .ok_or_else(|| zerror!("session closed").into()) + } + #[inline] fn get_local_res(&self, id: &ExprId) -> Option<&Resource> { self.local_resources.get(id) @@ -360,140 +367,120 @@ impl Resource { } } -#[derive(Clone)] -pub enum SessionRef<'a> { - Borrow(&'a Session), - Shared(Arc), +/// A trait implemented by types that can be undeclared. +pub trait UndeclarableSealed { + type Undeclaration: Resolve> + Send; + fn undeclare_inner(self, session: S) -> Self::Undeclaration; } -impl<'s, 'a> SessionDeclarations<'s, 'a> for SessionRef<'a> { - fn declare_subscriber<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> SubscriberBuilder<'a, 'b, DefaultHandler> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - SubscriberBuilder { - session: self.clone(), - key_expr: TryIntoKeyExpr::try_into(key_expr).map_err(Into::into), - #[cfg(feature = "unstable")] - reliability: Reliability::DEFAULT, - origin: Locality::default(), - handler: DefaultHandler::default(), - } - } - fn declare_queryable<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> QueryableBuilder<'a, 'b, DefaultHandler> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - QueryableBuilder { - session: self.clone(), - key_expr: key_expr.try_into().map_err(Into::into), - complete: false, - origin: Locality::default(), - handler: DefaultHandler::default(), - } - } - fn declare_publisher<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> PublisherBuilder<'a, 'b> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - PublisherBuilder { - session: self.clone(), - key_expr: key_expr.try_into().map_err(Into::into), - encoding: Encoding::default(), - congestion_control: CongestionControl::DEFAULT, - priority: Priority::DEFAULT, - is_express: false, - destination: Locality::default(), - } +impl<'a, T> UndeclarableSealed<&'a Session> for T +where + T: UndeclarableSealed<()>, +{ + type Undeclaration = >::Undeclaration; + + fn undeclare_inner(self, _session: &'a Session) -> Self::Undeclaration { + self.undeclare_inner(()) } - #[zenoh_macros::unstable] - fn liveliness(&'s self) -> Liveliness<'a> { - Liveliness { - session: self.clone(), - } +} + +// NOTE: `UndeclarableInner` is only pub(crate) to hide the `undeclare_inner` method. So we don't +// care about the `private_bounds` lint in this particular case. +#[allow(private_bounds)] +/// A trait implemented by types that can be undeclared. +pub trait Undeclarable: UndeclarableSealed {} + +impl Undeclarable for T where T: UndeclarableSealed {} + +pub(crate) struct SessionInner { + weak_counter: Mutex, + pub(crate) runtime: Runtime, + pub(crate) state: RwLock, + pub(crate) id: u16, + owns_runtime: bool, + task_controller: TaskController, +} + +impl fmt::Debug for SessionInner { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Session") + .field("id", &self.runtime.zid()) + .finish() } - fn info(&'s self) -> SessionInfo<'a> { - SessionInfo { - session: self.clone(), - } +} + +/// A zenoh session. +/// +pub struct Session(pub(crate) Arc); + +impl Session { + pub(crate) fn downgrade(&self) -> WeakSession { + WeakSession::new(&self.0) } } -impl Deref for SessionRef<'_> { - type Target = Session; +impl fmt::Debug for Session { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +} - fn deref(&self) -> &Self::Target { - match self { - SessionRef::Borrow(b) => b, - SessionRef::Shared(s) => s, - } +impl Clone for Session { + fn clone(&self) -> Self { + let _weak = self.0.weak_counter.lock().unwrap(); + Self(self.0.clone()) } } -impl fmt::Debug for SessionRef<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SessionRef::Borrow(b) => Session::fmt(b, f), - SessionRef::Shared(s) => Session::fmt(s, f), +impl Drop for Session { + fn drop(&mut self) { + let weak = self.0.weak_counter.lock().unwrap(); + if Arc::strong_count(&self.0) == *weak + 1 { + drop(weak); + if let Err(error) = self.close().wait() { + tracing::error!(error) + } } } } -pub(crate) trait UndeclarableSealed> -where - O: Resolve + Send, -{ - fn undeclare_inner(self, session: S) -> O; +pub(crate) struct WeakSession(Arc); + +impl WeakSession { + fn new(session: &Arc) -> Self { + let mut weak = session.weak_counter.lock().unwrap(); + *weak += 1; + Self(session.clone()) + } } -impl<'a, O, T, G> UndeclarableSealed<&'a Session, O, T> for G -where - O: Resolve + Send, - G: UndeclarableSealed<(), O, T>, -{ - fn undeclare_inner(self, _: &'a Session) -> O { - self.undeclare_inner(()) +impl Clone for WeakSession { + fn clone(&self) -> Self { + let mut weak = self.0.weak_counter.lock().unwrap(); + *weak += 1; + Self(self.0.clone()) } } -// NOTE: `UndeclarableInner` is only pub(crate) to hide the `undeclare_inner` method. So we don't -// care about the `private_bounds` lint in this particular case. -#[allow(private_bounds)] -/// A trait implemented by types that can be undeclared. -pub trait Undeclarable: UndeclarableSealed -where - O: Resolve + Send, -{ +impl fmt::Debug for WeakSession { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } } -impl Undeclarable for U -where - O: Resolve + Send, - U: UndeclarableSealed, -{ +impl Deref for WeakSession { + type Target = Arc; + + fn deref(&self) -> &Self::Target { + &self.0 + } } -/// A zenoh session. -/// -pub struct Session { - pub(crate) runtime: Runtime, - pub(crate) state: Arc>, - pub(crate) id: u16, - close_on_drop: bool, - owns_runtime: bool, - task_controller: TaskController, +impl Drop for WeakSession { + fn drop(&mut self) { + let mut weak = self.0.weak_counter.lock().unwrap(); + *weak -= 1; + } } static SESSION_ID_COUNTER: AtomicU16 = AtomicU16::new(0); @@ -502,96 +489,34 @@ impl Session { runtime: Runtime, aggregated_subscribers: Vec, aggregated_publishers: Vec, + owns_runtime: bool, ) -> impl Resolve { ResolveClosure::new(move || { let router = runtime.router(); - let state = Arc::new(RwLock::new(SessionState::new( + let state = RwLock::new(SessionState::new( aggregated_subscribers, aggregated_publishers, - ))); - let session = Session { + )); + let session = Session(Arc::new(SessionInner { + weak_counter: Mutex::new(0), runtime: runtime.clone(), - state: state.clone(), + state, id: SESSION_ID_COUNTER.fetch_add(1, Ordering::SeqCst), - close_on_drop: true, - owns_runtime: false, + owns_runtime, task_controller: TaskController::default(), - }; + })); - runtime.new_handler(Arc::new(admin::Handler::new(session.clone()))); + runtime.new_handler(Arc::new(admin::Handler::new(session.downgrade()))); - let primitives = Some(router.new_primitives(Arc::new(session.clone()))); - zwrite!(state).primitives = primitives; + let primitives = Some(router.new_primitives(Arc::new(session.downgrade()))); + zwrite!(session.0.state).primitives = primitives; - admin::init(&session); + admin::init(session.downgrade()); session }) } - /// Consumes the given `Session`, returning a thread-safe reference-counting - /// pointer to it (`Arc`). This is equivalent to `Arc::new(session)`. - /// - /// This is useful to share ownership of the `Session` between several threads - /// and tasks. It also allows to create [`Subscriber`](crate::pubsub::Subscriber) and - /// [`Queryable`](crate::query::Queryable) with static lifetime that can be moved to several - /// threads and tasks - /// - /// Note: the given zenoh `Session` will be closed when the last reference to - /// it is dropped. - /// - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let subscriber = session.declare_subscriber("key/expression") - /// .await - /// .unwrap(); - /// tokio::task::spawn(async move { - /// while let Ok(sample) = subscriber.recv_async().await { - /// println!("Received: {:?}", sample); - /// } - /// }).await; - /// # } - /// ``` - pub fn into_arc(self) -> Arc { - Arc::new(self) - } - - /// Consumes and leaks the given `Session`, returning a `'static` mutable - /// reference to it. The given `Session` will live for the remainder of - /// the program's life. Dropping the returned reference will cause a memory - /// leak. - /// - /// This is useful to move entities (like [`Subscriber`](crate::pubsub::Subscriber)) which - /// lifetimes are bound to the session lifetime in several threads or tasks. - /// - /// Note: the given zenoh `Session` cannot be closed any more. At process - /// termination the zenoh session will terminate abruptly. If possible prefer - /// using [`Session::into_arc()`](Session::into_arc). - /// - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::Session::leak(zenoh::open(zenoh::config::peer()).await.unwrap()); - /// let subscriber = session.declare_subscriber("key/expression").await.unwrap(); - /// tokio::task::spawn(async move { - /// while let Ok(sample) = subscriber.recv_async().await { - /// println!("Received: {:?}", sample); - /// } - /// }).await; - /// # } - /// ``` - pub fn leak(s: Self) -> &'static mut Self { - Box::leak(Box::new(s)) - } - /// Returns the identifier of the current session. `zid()` is a convenient shortcut. /// See [`Session::info()`](`Session::info()`) and [`SessionInfo::zid()`](`SessionInfo::zid()`) for more details. pub fn zid(&self) -> ZenohId { @@ -599,7 +524,7 @@ impl Session { } pub fn hlc(&self) -> Option<&HLC> { - self.runtime.hlc() + self.0.runtime.hlc() } /// Close the zenoh [`Session`](Session). @@ -617,29 +542,13 @@ impl Session { /// session.close().await.unwrap(); /// # } /// ``` - pub fn close(mut self) -> impl Resolve> { - ResolveFuture::new(async move { - trace!("close()"); - // set the flag first to avoid double panic if this function panic - self.close_on_drop = false; - self.task_controller.terminate_all(Duration::from_secs(10)); - if self.owns_runtime { - self.runtime.close().await?; - } - let mut state = zwrite!(self.state); - // clean up to break cyclic references from self.state to itself - let primitives = state.primitives.take(); - state.queryables.clear(); - drop(state); - primitives.as_ref().unwrap().send_close(); - Ok(()) - }) + pub fn close(&self) -> impl Resolve> + '_ { + self.0.close() } - pub fn undeclare<'a, T, O>(&'a self, decl: T) -> O + pub fn undeclare<'a, T>(&'a self, decl: T) -> impl Resolve> + 'a where - O: Resolve>, - T: Undeclarable<&'a Self, O, ZResult<()>>, + T: Undeclarable<&'a Session> + 'a, { UndeclarableSealed::undeclare_inner(decl, self) } @@ -674,7 +583,7 @@ impl Session { /// # } /// ``` pub fn config(&self) -> &Notifier { - self.runtime.config() + self.0.runtime.config() } /// Get a new Timestamp from a Zenoh session [`Session`](Session). @@ -699,49 +608,175 @@ impl Session { // Called in the case that the runtime is not initialized with an hlc // UNIX_EPOCH is Returns a Timespec::zero(), Unwrap Should be permissable here let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().into(); - Timestamp::new(now, self.runtime.zid().into()) + Timestamp::new(now, self.0.runtime.zid().into()) } } } } -impl<'a> SessionDeclarations<'a, 'a> for Session { - fn info(&self) -> SessionInfo { - SessionRef::Borrow(self).info() +impl Session { + /// Get information about the zenoh [`Session`](Session). + /// + /// # Examples + /// ``` + /// # #[tokio::main] + /// # async fn main() { + /// use zenoh::prelude::*; + /// + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); + /// let info = session.info(); + /// # } + /// ``` + pub fn info(&self) -> SessionInfo { + SessionInfo { + runtime: self.0.runtime.clone(), + } } - fn declare_subscriber<'b, TryIntoKeyExpr>( - &'a self, + + /// Create a [`Subscriber`](crate::pubsub::Subscriber) for the given key expression. + /// + /// # Arguments + /// + /// * `key_expr` - The resourkey expression to subscribe to + /// + /// # Examples + /// ```no_run + /// # #[tokio::main] + /// # async fn main() { + /// use zenoh::prelude::*; + /// + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); + /// let subscriber = session.declare_subscriber("key/expression") + /// .await + /// .unwrap(); + /// tokio::task::spawn(async move { + /// while let Ok(sample) = subscriber.recv_async().await { + /// println!("Received: {:?}", sample); + /// } + /// }).await; + /// # } + /// ``` + pub fn declare_subscriber<'b, TryIntoKeyExpr>( + &self, key_expr: TryIntoKeyExpr, - ) -> SubscriberBuilder<'a, 'b, DefaultHandler> + ) -> SubscriberBuilder<'_, 'b, DefaultHandler> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - SessionRef::Borrow(self).declare_subscriber(key_expr) + SubscriberBuilder { + session: self, + key_expr: TryIntoKeyExpr::try_into(key_expr).map_err(Into::into), + #[cfg(feature = "unstable")] + reliability: Reliability::DEFAULT, + origin: Locality::default(), + handler: DefaultHandler::default(), + } } - fn declare_queryable<'b, TryIntoKeyExpr>( - &'a self, + + /// Create a [`Queryable`](crate::query::Queryable) for the given key expression. + /// + /// # Arguments + /// + /// * `key_expr` - The key expression matching the queries the + /// [`Queryable`](crate::query::Queryable) will reply to + /// + /// # Examples + /// ```no_run + /// # #[tokio::main] + /// # async fn main() { + /// use zenoh::prelude::*; + /// + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); + /// let queryable = session.declare_queryable("key/expression") + /// .await + /// .unwrap(); + /// tokio::task::spawn(async move { + /// while let Ok(query) = queryable.recv_async().await { + /// query.reply( + /// "key/expression", + /// "value", + /// ).await.unwrap(); + /// } + /// }).await; + /// # } + /// ``` + pub fn declare_queryable<'b, TryIntoKeyExpr>( + &self, key_expr: TryIntoKeyExpr, - ) -> QueryableBuilder<'a, 'b, DefaultHandler> + ) -> QueryableBuilder<'_, 'b, DefaultHandler> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - SessionRef::Borrow(self).declare_queryable(key_expr) + QueryableBuilder { + session: self, + key_expr: key_expr.try_into().map_err(Into::into), + complete: false, + origin: Locality::default(), + handler: DefaultHandler::default(), + } } - fn declare_publisher<'b, TryIntoKeyExpr>( - &'a self, + + /// Create a [`Publisher`](crate::pubsub::Publisher) for the given key expression. + /// + /// # Arguments + /// + /// * `key_expr` - The key expression matching resources to write + /// + /// # Examples + /// ``` + /// # #[tokio::main] + /// # async fn main() { + /// use zenoh::prelude::*; + /// + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); + /// let publisher = session.declare_publisher("key/expression") + /// .await + /// .unwrap(); + /// publisher.put("value").await.unwrap(); + /// # } + /// ``` + pub fn declare_publisher<'b, TryIntoKeyExpr>( + &self, key_expr: TryIntoKeyExpr, - ) -> PublisherBuilder<'a, 'b> + ) -> PublisherBuilder<'_, 'b> where TryIntoKeyExpr: TryInto>, >>::Error: Into, { - SessionRef::Borrow(self).declare_publisher(key_expr) + PublisherBuilder { + session: self, + key_expr: key_expr.try_into().map_err(Into::into), + encoding: Encoding::default(), + congestion_control: CongestionControl::DEFAULT, + priority: Priority::DEFAULT, + is_express: false, + #[cfg(feature = "unstable")] + reliability: Reliability::DEFAULT, + destination: Locality::default(), + } } + + /// Obtain a [`Liveliness`] struct tied to this Zenoh [`Session`]. + /// + /// # Examples + /// ``` + /// # #[tokio::main] + /// # async fn main() { + /// use zenoh::prelude::*; + /// + /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); + /// let liveliness = session + /// .liveliness() + /// .declare_token("key/expression") + /// .await + /// .unwrap(); + /// # } + /// ``` #[zenoh_macros::unstable] - fn liveliness(&'a self) -> Liveliness { - SessionRef::Borrow(self).liveliness() + pub fn liveliness(&self) -> Liveliness<'_> { + Liveliness { session: self } } } @@ -770,18 +805,11 @@ impl Session { >>::Error: Into, { let key_expr: ZResult = key_expr.try_into().map_err(Into::into); - self._declare_keyexpr(key_expr) - } - - fn _declare_keyexpr<'a, 'b: 'a>( - &'a self, - key_expr: ZResult>, - ) -> impl Resolve>> + 'a { - let sid = self.id; + let sid = self.0.id; ResolveClosure::new(move || { let key_expr: KeyExpr = key_expr?; let prefix_len = key_expr.len() as u32; - let expr_id = self.declare_prefix(key_expr.as_str()).wait(); + let expr_id = self.0.declare_prefix(key_expr.as_str()).wait()?; let key_expr = match key_expr.0 { KeyExprInner::Borrowed(key_expr) | KeyExprInner::BorrowedWire { key_expr, .. } => { KeyExpr(KeyExprInner::BorrowedWire { @@ -917,7 +945,7 @@ impl Session { { let selector = selector.try_into().map_err(Into::into); let timeout = { - let conf = self.runtime.config().lock(); + let conf = self.0.runtime.config().lock(); Duration::from_millis(unwrap_or_default!(conf.queries_default_timeout())) }; let qos: QoS = request::ext::QoSType::REQUEST.into(); @@ -939,17 +967,6 @@ impl Session { } impl Session { - pub(crate) fn clone(&self) -> Self { - Self { - runtime: self.runtime.clone(), - state: self.state.clone(), - id: self.id, - close_on_drop: false, - owns_runtime: self.owns_runtime, - task_controller: self.task_controller.clone(), - } - } - #[allow(clippy::new_ret_no_self)] pub(super) fn new( config: Config, @@ -967,28 +984,50 @@ impl Session { } let mut runtime = runtime.build().await?; - let mut session = Self::init( + let session = Self::init( runtime.clone(), aggregated_subscribers, aggregated_publishers, + true, ) .await; - session.owns_runtime = true; runtime.start().await?; Ok(session) }) } +} +impl SessionInner { + fn close(&self) -> impl Resolve> + '_ { + ResolveFuture::new(async move { + let Some(primitives) = zwrite!(self.state).primitives.take() else { + return Ok(()); + }; + trace!("close()"); + self.task_controller.terminate_all(Duration::from_secs(10)); + if self.owns_runtime { + self.runtime.close().await?; + } else { + primitives.send_close(); + } + zwrite!(self.state).queryables.clear(); + Ok(()) + }) + } - pub(crate) fn declare_prefix<'a>(&'a self, prefix: &'a str) -> impl Resolve + 'a { + pub(crate) fn declare_prefix<'a>( + &'a self, + prefix: &'a str, + ) -> impl Resolve> + 'a { ResolveClosure::new(move || { trace!("declare_prefix({:?})", prefix); let mut state = zwrite!(self.state); + let primitives = state.primitives()?; match state .local_resources .iter() .find(|(_expr_id, res)| res.name() == prefix) { - Some((expr_id, _res)) => *expr_id, + Some((expr_id, _res)) => Ok(*expr_id), None => { let expr_id = state.expr_id_counter.fetch_add(1, Ordering::SeqCst); let mut res = Resource::new(Box::from(prefix)); @@ -1005,7 +1044,6 @@ impl Session { } } state.local_resources.insert(expr_id, res); - let primitives = state.primitives.as_ref().unwrap().clone(); drop(state); primitives.send_declare(Declare { interest_id: None, @@ -1021,7 +1059,7 @@ impl Session { }, }), }); - expr_id + Ok(expr_id) } } }) @@ -1078,7 +1116,7 @@ impl Session { state.publishers.insert(id, pub_state); if let Some(res) = declared_pub { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_interest(Interest { id, @@ -1103,7 +1141,7 @@ impl Session { if !state.publishers.values().any(|p| { p.destination != Locality::SessionLocal && p.remote_id == pub_state.remote_id }) { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_interest(Interest { id: pub_state.remote_id, @@ -1123,7 +1161,7 @@ impl Session { } pub(crate) fn declare_subscriber_inner( - &self, + self: &Arc, key_expr: &KeyExpr, origin: Locality, callback: Callback<'static, Sample>, @@ -1207,7 +1245,7 @@ impl Session { } if let Some(key_expr) = declared_sub { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); // If key_expr is a pure Expr, remap it to optimal Rid or RidWithSuffix // let key_expr = if !key_expr.is_optimized(self) { @@ -1254,7 +1292,11 @@ impl Session { Ok(sub_state) } - pub(crate) fn undeclare_subscriber_inner(&self, sid: Id, kind: SubscriberKind) -> ZResult<()> { + pub(crate) fn undeclare_subscriber_inner( + self: &Arc, + sid: Id, + kind: SubscriberKind, + ) -> ZResult<()> { let mut state = zwrite!(self.state); if let Some(sub_state) = state.subscribers_mut(kind).remove(&sid) { trace!("undeclare_subscriber({:?})", sub_state); @@ -1281,7 +1323,7 @@ impl Session { if !state.subscribers(kind).values().any(|s| { s.origin != Locality::SessionLocal && s.remote_id == sub_state.remote_id }) { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_declare(Declare { interest_id: None, @@ -1304,7 +1346,7 @@ impl Session { } else { #[cfg(feature = "unstable")] if kind == SubscriberKind::LivelinessSubscriber { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_interest(Interest { @@ -1346,7 +1388,7 @@ impl Session { state.queryables.insert(id, qable_state.clone()); if origin != Locality::SessionLocal { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); let qabl_info = QueryableInfoType { complete, @@ -1372,7 +1414,7 @@ impl Session { if let Some(qable_state) = state.queryables.remove(&qid) { trace!("undeclare_queryable({:?})", qable_state); if qable_state.origin != Locality::SessionLocal { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_declare(Declare { interest_id: None, @@ -1407,7 +1449,7 @@ impl Session { }); state.tokens.insert(tok_state.id, tok_state.clone()); - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_declare(Declare { interest_id: None, @@ -1469,7 +1511,7 @@ impl Session { } } - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_interest(Interest { @@ -1494,7 +1536,7 @@ impl Session { let key_expr = &tok_state.key_expr; let twin_tok = state.tokens.values().any(|s| s.key_expr == *key_expr); if !twin_tok { - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); primitives.send_declare(Declare { interest_id: None, @@ -1585,14 +1627,14 @@ impl Session { } #[zenoh_macros::unstable] - pub(crate) fn update_status_up(&self, state: &SessionState, key_expr: &KeyExpr) { + pub(crate) fn update_status_up(self: &Arc, state: &SessionState, key_expr: &KeyExpr) { for msub in state.matching_listeners.values() { if key_expr.intersects(&msub.key_expr) { // Cannot hold session lock when calling tables (matching_status()) // TODO: check which ZRuntime should be used self.task_controller .spawn_with_rt(zenoh_runtime::ZRuntime::Net, { - let session = self.clone(); + let session = WeakSession::new(self); let msub = msub.clone(); async move { match msub.current.lock() { @@ -1623,14 +1665,14 @@ impl Session { } #[zenoh_macros::unstable] - pub(crate) fn update_status_down(&self, state: &SessionState, key_expr: &KeyExpr) { + pub(crate) fn update_status_down(self: &Arc, state: &SessionState, key_expr: &KeyExpr) { for msub in state.matching_listeners.values() { if key_expr.intersects(&msub.key_expr) { // Cannot hold session lock when calling tables (matching_status()) // TODO: check which ZRuntime should be used self.task_controller .spawn_with_rt(zenoh_runtime::ZRuntime::Net, { - let session = self.clone(); + let session = WeakSession::new(self); let msub = msub.clone(); async move { match msub.current.lock() { @@ -1671,6 +1713,7 @@ impl Session { } } + #[allow(clippy::too_many_arguments)] // TODO fixme pub(crate) fn execute_subscriber_callbacks( &self, local: bool, @@ -1678,6 +1721,7 @@ impl Session { info: Option, payload: ZBuf, kind: SubscriberKind, + #[cfg(feature = "unstable")] reliability: Reliability, attachment: Option, ) { let mut callbacks = SingleOrVec::default(); @@ -1726,20 +1770,30 @@ impl Session { drop(state); let zenoh_collections::single_or_vec::IntoIter { drain, last } = callbacks.into_iter(); for (cb, key_expr) in drain { - let sample = info - .clone() - .into_sample(key_expr, payload.clone(), attachment.clone()); + let sample = info.clone().into_sample( + key_expr, + payload.clone(), + #[cfg(feature = "unstable")] + reliability, + attachment.clone(), + ); cb(sample); } if let Some((cb, key_expr)) = last { - let sample = info.into_sample(key_expr, payload, attachment.clone()); + let sample = info.into_sample( + key_expr, + payload, + #[cfg(feature = "unstable")] + reliability, + attachment.clone(), + ); cb(sample); } } #[allow(clippy::too_many_arguments)] pub(crate) fn query( - &self, + self: &Arc, key_expr: &KeyExpr<'_>, parameters: &Parameters<'_>, target: QueryTarget, @@ -1774,13 +1828,13 @@ impl Session { let token = self.task_controller.get_cancellation_token(); self.task_controller .spawn_with_rt(zenoh_runtime::ZRuntime::Net, { - let state = self.state.clone(); + let session = WeakSession::new(self); #[cfg(feature = "unstable")] let zid = self.runtime.zid(); async move { tokio::select! { _ = tokio::time::sleep(timeout) => { - let mut state = zwrite!(state); + let mut state = zwrite!(session.state); if let Some(query) = state.queries.remove(&qid) { std::mem::drop(state); tracing::debug!("Timeout on query {}! Send error and close.", qid); @@ -1815,7 +1869,7 @@ impl Session { }, ); - let primitives = state.primitives.as_ref().unwrap().clone(); + let primitives = state.primitives()?; drop(state); if destination != Locality::SessionLocal { @@ -1869,7 +1923,7 @@ impl Session { #[cfg(feature = "unstable")] pub(crate) fn liveliness_query( - &self, + self: &Arc, key_expr: &KeyExpr<'_>, timeout: Duration, callback: Callback<'static, Reply>, @@ -1880,283 +1934,128 @@ impl Session { let token = self.task_controller.get_cancellation_token(); self.task_controller .spawn_with_rt(zenoh_runtime::ZRuntime::Net, { - let state = self.state.clone(); + let session = WeakSession::new(self); let zid = self.runtime.zid(); async move { tokio::select! { _ = tokio::time::sleep(timeout) => { - let mut state = zwrite!(state); + let mut state = zwrite!(session.state); if let Some(query) = state.liveliness_queries.remove(&id) { std::mem::drop(state); tracing::debug!("Timeout on liveliness query {}! Send error and close.", id); - (query.callback)(Reply { - result: Err(Value::new("Timeout", Encoding::ZENOH_STRING).into()), - #[cfg(feature = "unstable")] - replier_id: Some(zid.into()), - }); - } - } - _ = token.cancelled() => {} - } - } - }); - - tracing::trace!("Register liveliness query {}", id); - let wexpr = key_expr.to_wire(self).to_owned(); - state - .liveliness_queries - .insert(id, LivelinessQueryState { callback }); - - let primitives = state.primitives.as_ref().unwrap().clone(); - drop(state); - - primitives.send_interest(Interest { - id, - mode: InterestMode::Current, - options: InterestOptions::KEYEXPRS + InterestOptions::TOKENS, - wire_expr: Some(wexpr.clone()), - ext_qos: request::ext::QoSType::DEFAULT, - ext_tstamp: None, - ext_nodeid: request::ext::NodeIdType::DEFAULT, - }); - - Ok(()) - } - - #[allow(clippy::too_many_arguments)] - pub(crate) fn handle_query( - &self, - local: bool, - key_expr: &WireExpr, - parameters: &str, - qid: RequestId, - _target: TargetType, - _consolidation: Consolidation, - body: Option, - attachment: Option, - ) { - let (primitives, key_expr, queryables) = { - let state = zread!(self.state); - match state.wireexpr_to_keyexpr(key_expr, local) { - Ok(key_expr) => { - let queryables = state - .queryables - .iter() - .filter( - |(_, queryable)| - (queryable.origin == Locality::Any - || (local == (queryable.origin == Locality::SessionLocal))) - && - match state.local_wireexpr_to_expr(&queryable.key_expr) { - Ok(qablname) => { - qablname.intersects(&key_expr) - } - Err(err) => { - error!( - "{}. Internal error (queryable key_expr to key_expr failed).", - err - ); - false - } - } - ) - .map(|(id, qable)| (*id, qable.callback.clone())) - .collect::)>>(); - ( - state.primitives.as_ref().unwrap().clone(), - key_expr.into_owned(), - queryables, - ) - } - Err(err) => { - error!("Received Query for unknown key_expr: {}", err); - return; - } - } - }; - - let zid = self.runtime.zid(); - - let query_inner = Arc::new(QueryInner { - key_expr, - parameters: parameters.to_owned().into(), - qid, - zid: zid.into(), - primitives: if local { - Arc::new(self.clone()) - } else { - primitives - }, - }); - for (eid, callback) in queryables { - callback(Query { - inner: query_inner.clone(), - eid, - value: body.as_ref().map(|b| Value { - payload: b.payload.clone().into(), - encoding: b.encoding.clone().into(), - }), - attachment: attachment.clone(), - }); - } - } -} - -impl<'s> SessionDeclarations<'s, 'static> for Arc { - /// Create a [`Subscriber`](crate::pubsub::Subscriber) for the given key expression. - /// - /// # Arguments - /// - /// * `key_expr` - The resourkey expression to subscribe to - /// - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let subscriber = session.declare_subscriber("key/expression") - /// .await - /// .unwrap(); - /// tokio::task::spawn(async move { - /// while let Ok(sample) = subscriber.recv_async().await { - /// println!("Received: {:?}", sample); - /// } - /// }).await; - /// # } - /// ``` - fn declare_subscriber<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> SubscriberBuilder<'static, 'b, DefaultHandler> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - SubscriberBuilder { - session: SessionRef::Shared(self.clone()), - key_expr: key_expr.try_into().map_err(Into::into), - #[cfg(feature = "unstable")] - reliability: Reliability::DEFAULT, - origin: Locality::default(), - handler: DefaultHandler::default(), - } - } - - /// Create a [`Queryable`](crate::query::Queryable) for the given key expression. - /// - /// # Arguments - /// - /// * `key_expr` - The key expression matching the queries the - /// [`Queryable`](crate::query::Queryable) will reply to - /// - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let queryable = session.declare_queryable("key/expression") - /// .await - /// .unwrap(); - /// tokio::task::spawn(async move { - /// while let Ok(query) = queryable.recv_async().await { - /// query.reply( - /// "key/expression", - /// "value", - /// ).await.unwrap(); - /// } - /// }).await; - /// # } - /// ``` - fn declare_queryable<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> QueryableBuilder<'static, 'b, DefaultHandler> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - QueryableBuilder { - session: SessionRef::Shared(self.clone()), - key_expr: key_expr.try_into().map_err(Into::into), - complete: false, - origin: Locality::default(), - handler: DefaultHandler::default(), - } - } - - /// Create a [`Publisher`](crate::pubsub::Publisher) for the given key expression. - /// - /// # Arguments - /// - /// * `key_expr` - The key expression matching resources to write - /// - /// # Examples - /// ``` - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let publisher = session.declare_publisher("key/expression") - /// .await - /// .unwrap(); - /// publisher.put("value").await.unwrap(); - /// # } - /// ``` - fn declare_publisher<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> PublisherBuilder<'static, 'b> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into, - { - PublisherBuilder { - session: SessionRef::Shared(self.clone()), - key_expr: key_expr.try_into().map_err(Into::into), - encoding: Encoding::default(), - congestion_control: CongestionControl::DEFAULT, - priority: Priority::DEFAULT, - is_express: false, - destination: Locality::default(), - } - } + (query.callback)(Reply { + result: Err(Value::new("Timeout", Encoding::ZENOH_STRING).into()), + #[cfg(feature = "unstable")] + replier_id: Some(zid.into()), + }); + } + } + _ = token.cancelled() => {} + } + } + }); - /// Obtain a [`Liveliness`] struct tied to this Zenoh [`Session`]. - /// - /// # Examples - /// ``` - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let liveliness = session - /// .liveliness() - /// .declare_token("key/expression") - /// .await - /// .unwrap(); - /// # } - /// ``` - #[zenoh_macros::unstable] - fn liveliness(&'s self) -> Liveliness<'static> { - Liveliness { - session: SessionRef::Shared(self.clone()), - } + tracing::trace!("Register liveliness query {}", id); + let wexpr = key_expr.to_wire(self).to_owned(); + state + .liveliness_queries + .insert(id, LivelinessQueryState { callback }); + + let primitives = state.primitives()?; + drop(state); + + primitives.send_interest(Interest { + id, + mode: InterestMode::Current, + options: InterestOptions::KEYEXPRS + InterestOptions::TOKENS, + wire_expr: Some(wexpr.clone()), + ext_qos: request::ext::QoSType::DEFAULT, + ext_tstamp: None, + ext_nodeid: request::ext::NodeIdType::DEFAULT, + }); + + Ok(()) } - fn info(&'s self) -> SessionInfo<'static> { - SessionInfo { - session: SessionRef::Shared(self.clone()), + #[allow(clippy::too_many_arguments)] + pub(crate) fn handle_query( + self: &Arc, + local: bool, + key_expr: &WireExpr, + parameters: &str, + qid: RequestId, + _target: TargetType, + _consolidation: Consolidation, + body: Option, + attachment: Option, + ) { + let (primitives, key_expr, queryables) = { + let state = zread!(self.state); + let Ok(primitives) = state.primitives() else { + return; + }; + match state.wireexpr_to_keyexpr(key_expr, local) { + Ok(key_expr) => { + let queryables = state + .queryables + .iter() + .filter( + |(_, queryable)| + (queryable.origin == Locality::Any + || (local == (queryable.origin == Locality::SessionLocal))) + && + match state.local_wireexpr_to_expr(&queryable.key_expr) { + Ok(qablname) => { + qablname.intersects(&key_expr) + } + Err(err) => { + error!( + "{}. Internal error (queryable key_expr to key_expr failed).", + err + ); + false + } + } + ) + .map(|(id, qable)| (*id, qable.callback.clone())) + .collect::)>>(); + (primitives, key_expr.into_owned(), queryables) + } + Err(err) => { + error!("Received Query for unknown key_expr: {}", err); + return; + } + } + }; + + let zid = self.runtime.zid(); + + let query_inner = Arc::new(QueryInner { + key_expr, + parameters: parameters.to_owned().into(), + qid, + zid: zid.into(), + primitives: if local { + Arc::new(WeakSession::new(self)) + } else { + primitives + }, + }); + for (eid, callback) in queryables { + callback(Query { + inner: query_inner.clone(), + eid, + value: body.as_ref().map(|b| Value { + payload: b.payload.clone().into(), + encoding: b.encoding.clone().into(), + }), + attachment: attachment.clone(), + }); } } } -impl Primitives for Session { +impl Primitives for WeakSession { fn send_interest(&self, msg: zenoh_protocol::network::Interest) { trace!("recv Interest {} {:?}", msg.id, msg.wire_expr); } @@ -2253,6 +2152,8 @@ impl Primitives for Session { timestamp: None, qos: QoS::default(), #[cfg(feature = "unstable")] + reliability: Reliability::Reliable, + #[cfg(feature = "unstable")] source_info: SourceInfo::empty(), #[cfg(feature = "unstable")] attachment: None, @@ -2275,6 +2176,8 @@ impl Primitives for Session { ZBuf::default(), SubscriberKind::LivelinessSubscriber, #[cfg(feature = "unstable")] + Reliability::Reliable, + #[cfg(feature = "unstable")] None, ); } @@ -2305,6 +2208,8 @@ impl Primitives for Session { ZBuf::default(), SubscriberKind::LivelinessSubscriber, #[cfg(feature = "unstable")] + Reliability::Reliable, + #[cfg(feature = "unstable")] None, ); } else if m.ext_wire_expr.wire_expr != WireExpr::empty() { @@ -2327,6 +2232,8 @@ impl Primitives for Session { ZBuf::default(), SubscriberKind::LivelinessSubscriber, #[cfg(feature = "unstable")] + Reliability::Reliable, + #[cfg(feature = "unstable")] None, ); } @@ -2352,7 +2259,7 @@ impl Primitives for Session { } } - fn send_push(&self, msg: Push) { + fn send_push(&self, msg: Push, _reliability: Reliability) { trace!("recv Push {:?}", msg); match msg.payload { PushBody::Put(m) => { @@ -2370,6 +2277,8 @@ impl Primitives for Session { Some(info), m.payload, SubscriberKind::Subscriber, + #[cfg(feature = "unstable")] + _reliability, m.ext_attachment.map(Into::into), ) } @@ -2388,6 +2297,8 @@ impl Primitives for Session { Some(info), ZBuf::empty(), SubscriberKind::Subscriber, + #[cfg(feature = "unstable")] + _reliability, m.ext_attachment.map(Into::into), ) } @@ -2505,7 +2416,13 @@ impl Primitives for Session { attachment: _attachment.map(Into::into), }, }; - let sample = info.into_sample(key_expr.into_owned(), payload, attachment); + let sample = info.into_sample( + key_expr.into_owned(), + payload, + #[cfg(feature = "unstable")] + Reliability::Reliable, + attachment, + ); let new_reply = Reply { result: Ok(sample), #[cfg(feature = "unstable")] @@ -2631,174 +2548,7 @@ impl Primitives for Session { } } -impl Drop for Session { - fn drop(&mut self) { - if self.close_on_drop { - let _ = self.clone().close().wait(); - } - } -} - -impl fmt::Debug for Session { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("Session").field("id", &self.zid()).finish() - } -} - -/// Functions to create zenoh entities -/// -/// This trait contains functions to create zenoh entities like -/// [`Subscriber`](crate::pubsub::Subscriber), and -/// [`Queryable`](crate::query::Queryable) -/// -/// This trait is implemented by [`Session`](crate::session::Session) itself and -/// by wrappers [`SessionRef`](crate::session::SessionRef) and [`Arc`](std::sync::Arc) -/// -/// # Examples -/// ```no_run -/// # #[tokio::main] -/// # async fn main() { -/// use zenoh::prelude::*; -/// -/// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); -/// let subscriber = session.declare_subscriber("key/expression") -/// .await -/// .unwrap(); -/// tokio::task::spawn(async move { -/// while let Ok(sample) = subscriber.recv_async().await { -/// println!("Received: {:?}", sample); -/// } -/// }).await; -/// # } -/// ``` -pub trait SessionDeclarations<'s, 'a> { - /// Create a [`Subscriber`](crate::pubsub::Subscriber) for the given key expression. - /// - /// # Arguments - /// - /// * `key_expr` - The resourkey expression to subscribe to - /// - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let subscriber = session.declare_subscriber("key/expression") - /// .await - /// .unwrap(); - /// tokio::task::spawn(async move { - /// while let Ok(sample) = subscriber.recv_async().await { - /// println!("Received: {:?}", sample); - /// } - /// }).await; - /// # } - /// ``` - fn declare_subscriber<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> SubscriberBuilder<'a, 'b, DefaultHandler> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into; - - /// Create a [`Queryable`](crate::query::Queryable) for the given key expression. - /// - /// # Arguments - /// - /// * `key_expr` - The key expression matching the queries the - /// [`Queryable`](crate::query::Queryable) will reply to - /// - /// # Examples - /// ```no_run - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let queryable = session.declare_queryable("key/expression") - /// .await - /// .unwrap(); - /// tokio::task::spawn(async move { - /// while let Ok(query) = queryable.recv_async().await { - /// query.reply( - /// "key/expression", - /// "value", - /// ).await.unwrap(); - /// } - /// }).await; - /// # } - /// ``` - fn declare_queryable<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> QueryableBuilder<'a, 'b, DefaultHandler> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into; - - /// Create a [`Publisher`](crate::pubsub::Publisher) for the given key expression. - /// - /// # Arguments - /// - /// * `key_expr` - The key expression matching resources to write - /// - /// # Examples - /// ``` - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let publisher = session.declare_publisher("key/expression") - /// .await - /// .unwrap(); - /// publisher.put("value").await.unwrap(); - /// # } - /// ``` - fn declare_publisher<'b, TryIntoKeyExpr>( - &'s self, - key_expr: TryIntoKeyExpr, - ) -> PublisherBuilder<'a, 'b> - where - TryIntoKeyExpr: TryInto>, - >>::Error: Into; - - /// Obtain a [`Liveliness`] struct tied to this Zenoh [`Session`]. - /// - /// # Examples - /// ``` - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap().into_arc(); - /// let liveliness = session - /// .liveliness() - /// .declare_token("key/expression") - /// .await - /// .unwrap(); - /// # } - /// ``` - #[zenoh_macros::unstable] - fn liveliness(&'s self) -> Liveliness<'a>; - /// Get information about the zenoh [`Session`](Session). - /// - /// # Examples - /// ``` - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::prelude::*; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); - /// let info = session.info(); - /// # } - /// ``` - fn info(&'s self) -> SessionInfo<'a>; -} - -impl crate::net::primitives::EPrimitives for Session { +impl crate::net::primitives::EPrimitives for WeakSession { #[inline] fn send_interest(&self, ctx: crate::net::routing::RoutingContext) { (self as &dyn Primitives).send_interest(ctx.msg) @@ -2810,8 +2560,8 @@ impl crate::net::primitives::EPrimitives for Session { } #[inline] - fn send_push(&self, msg: Push) { - (self as &dyn Primitives).send_push(msg) + fn send_push(&self, msg: Push, reliability: Reliability) { + (self as &dyn Primitives).send_push(msg, reliability) } #[inline] @@ -2998,6 +2748,7 @@ impl Wait for InitBuilder { self.runtime, self.aggregated_subscribers, self.aggregated_publishers, + false, ) .wait()) } diff --git a/zenoh/src/api/subscriber.rs b/zenoh/src/api/subscriber.rs index 0e82a20331..4dd1caba38 100644 --- a/zenoh/src/api/subscriber.rs +++ b/zenoh/src/api/subscriber.rs @@ -15,25 +15,33 @@ use std::{ fmt, future::{IntoFuture, Ready}, + mem::size_of, ops::{Deref, DerefMut}, sync::Arc, }; +use tracing::error; use zenoh_core::{Resolvable, Wait}; use zenoh_protocol::network::declare::subscriber::ext::SubscriberInfo; use zenoh_result::ZResult; #[cfg(feature = "unstable")] -use {zenoh_config::wrappers::EntityGlobalId, zenoh_protocol::core::EntityGlobalIdProto}; - -use super::{ - handlers::{locked, Callback, DefaultHandler, IntoHandler}, - key_expr::KeyExpr, - sample::{Locality, Sample}, - session::{SessionRef, UndeclarableSealed}, - Id, +use { + zenoh_config::wrappers::{EntityGlobalId, ZenohId}, + zenoh_protocol::core::EntityGlobalIdProto, }; + #[cfg(feature = "unstable")] use crate::pubsub::Reliability; +use crate::{ + api::{ + handlers::{locked, Callback, DefaultHandler, IntoHandler}, + key_expr::KeyExpr, + sample::{Locality, Sample}, + session::{UndeclarableSealed, WeakSession}, + Id, + }, + Session, +}; pub(crate) struct SubscriberState { pub(crate) id: Id, @@ -52,71 +60,17 @@ impl fmt::Debug for SubscriberState { } } -/// A subscriber that provides data through a callback. -/// -/// CallbackSubscribers can be created from a zenoh [`Session`](crate::Session) -/// with the [`declare_subscriber`](crate::SessionDeclarations::declare_subscriber) function -/// and the [`callback`](SubscriberBuilder::callback) function -/// of the resulting builder. -/// -/// Subscribers are automatically undeclared when dropped. -/// -/// # Examples -/// ``` -/// # #[tokio::main] -/// # async fn main() { -/// use zenoh::prelude::*; -/// -/// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); -/// let subscriber = session -/// .declare_subscriber("key/expression") -/// .callback(|sample| { println!("Received: {} {:?}", sample.key_expr(), sample.payload()) }) -/// .await -/// .unwrap(); -/// # } -/// ``` #[derive(Debug)] -pub(crate) struct SubscriberInner<'a> { - pub(crate) session: SessionRef<'a>, +pub(crate) struct SubscriberInner { + #[cfg(feature = "unstable")] + pub(crate) session_id: ZenohId, + pub(crate) session: WeakSession, pub(crate) state: Arc, pub(crate) kind: SubscriberKind, + // Subscriber is undeclared on drop unless its handler is a ZST, i.e. it is callback-only pub(crate) undeclare_on_drop: bool, } -impl<'a> SubscriberInner<'a> { - /// Close a [`CallbackSubscriber`](CallbackSubscriber). - /// - /// `CallbackSubscribers` are automatically closed when dropped, but you may want to use this function to handle errors or - /// close the `CallbackSubscriber` asynchronously. - /// - /// # Examples - /// ``` - /// # #[tokio::main] - /// # async fn main() { - /// use zenoh::{prelude::*, sample::Sample}; - /// - /// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); - /// # fn data_handler(_sample: Sample) { }; - /// let subscriber = session - /// .declare_subscriber("key/expression") - /// .callback(data_handler) - /// .await - /// .unwrap(); - /// subscriber.undeclare().await.unwrap(); - /// # } - /// ``` - #[inline] - pub fn undeclare(self) -> SubscriberUndeclaration<'a> { - UndeclarableSealed::undeclare_inner(self, ()) - } -} - -impl<'a> UndeclarableSealed<(), SubscriberUndeclaration<'a>> for SubscriberInner<'a> { - fn undeclare_inner(self, _: ()) -> SubscriberUndeclaration<'a> { - SubscriberUndeclaration { subscriber: self } - } -} - /// A [`Resolvable`] returned when undeclaring a subscriber. /// /// # Examples @@ -134,25 +88,19 @@ impl<'a> UndeclarableSealed<(), SubscriberUndeclaration<'a>> for SubscriberInner /// # } /// ``` #[must_use = "Resolvables do nothing unless you resolve them using the `res` method from either `SyncResolve` or `AsyncResolve`"] -pub struct SubscriberUndeclaration<'a> { - subscriber: SubscriberInner<'a>, -} +pub struct SubscriberUndeclaration(Subscriber); -impl Resolvable for SubscriberUndeclaration<'_> { +impl Resolvable for SubscriberUndeclaration { type To = ZResult<()>; } -impl Wait for SubscriberUndeclaration<'_> { +impl Wait for SubscriberUndeclaration { fn wait(mut self) -> ::To { - // set the flag first to avoid double panic if this function panic - self.subscriber.undeclare_on_drop = false; - self.subscriber - .session - .undeclare_subscriber_inner(self.subscriber.state.id, self.subscriber.kind) + self.0.undeclare_impl() } } -impl IntoFuture for SubscriberUndeclaration<'_> { +impl IntoFuture for SubscriberUndeclaration { type Output = ::To; type IntoFuture = Ready<::To>; @@ -161,16 +109,6 @@ impl IntoFuture for SubscriberUndeclaration<'_> { } } -impl Drop for SubscriberInner<'_> { - fn drop(&mut self) { - if self.undeclare_on_drop { - let _ = self - .session - .undeclare_subscriber_inner(self.state.id, self.kind); - } - } -} - /// A builder for initializing a [`FlumeSubscriber`]. /// /// # Examples @@ -191,9 +129,9 @@ impl Drop for SubscriberInner<'_> { #[derive(Debug)] pub struct SubscriberBuilder<'a, 'b, Handler> { #[cfg(feature = "unstable")] - pub session: SessionRef<'a>, + pub session: &'a Session, #[cfg(not(feature = "unstable"))] - pub(crate) session: SessionRef<'a>, + pub(crate) session: &'a Session, #[cfg(feature = "unstable")] pub key_expr: ZResult>, @@ -369,7 +307,7 @@ where Handler: IntoHandler<'static, Sample> + Send, Handler::Handler: Send, { - type To = ZResult>; + type To = ZResult>; } impl<'a, Handler> Wait for SubscriberBuilder<'a, '_, Handler> @@ -382,6 +320,7 @@ where let session = self.session; let (callback, receiver) = self.handler.into_handler(); session + .0 .declare_subscriber_inner( &key_expr, self.origin, @@ -394,11 +333,14 @@ where &SubscriberInfo::default(), ) .map(|sub_state| Subscriber { - subscriber: SubscriberInner { - session, + inner: SubscriberInner { + #[cfg(feature = "unstable")] + session_id: session.zid(), + session: session.downgrade(), state: sub_state, kind: SubscriberKind::Subscriber, - undeclare_on_drop: true, + // `size_of::() == 0` means callback-only subscriber + undeclare_on_drop: size_of::() > 0, }, handler: receiver, }) @@ -421,13 +363,33 @@ where /// A subscriber that provides data through a [`Handler`](crate::handlers::IntoHandler). /// /// Subscribers can be created from a zenoh [`Session`](crate::Session) -/// with the [`declare_subscriber`](crate::session::SessionDeclarations::declare_subscriber) function +/// with the [`declare_subscriber`](crate::Session::declare_subscriber) function /// and the [`with`](SubscriberBuilder::with) function /// of the resulting builder. /// -/// Subscribers are automatically undeclared when dropped. +/// Callback subscribers will run in background until the session is closed, +/// or until it is undeclared. +/// On the other hand, subscribers with a handler are automatically undeclared when dropped. /// /// # Examples +/// +/// Using callback: +/// ```no_run +/// # #[tokio::main] +/// # async fn main() { +/// use zenoh::prelude::*; +/// +/// let session = zenoh::open(zenoh::config::peer()).await.unwrap(); +/// session +/// .declare_subscriber("key/expression") +/// .callback(|sample| { println!("Received: {} {:?}", sample.key_expr(), sample.payload()) }) +/// .await +/// .unwrap(); +/// // subscriber run in background until the session is closed +/// # } +/// ``` +/// +/// Using channel handler: /// ```no_run /// # #[tokio::main] /// # async fn main() { @@ -442,16 +404,17 @@ where /// while let Ok(sample) = subscriber.recv_async().await { /// println!("Received: {} {:?}", sample.key_expr(), sample.payload()); /// } +/// // subscriber is undeclared at the end of the scope /// # } /// ``` #[non_exhaustive] #[derive(Debug)] -pub struct Subscriber<'a, Handler> { - pub(crate) subscriber: SubscriberInner<'a>, +pub struct Subscriber { + pub(crate) inner: SubscriberInner, pub(crate) handler: Handler, } -impl<'a, Handler> Subscriber<'a, Handler> { +impl Subscriber { /// Returns the [`EntityGlobalId`] of this Subscriber. /// /// # Examples @@ -470,15 +433,15 @@ impl<'a, Handler> Subscriber<'a, Handler> { #[zenoh_macros::unstable] pub fn id(&self) -> EntityGlobalId { EntityGlobalIdProto { - zid: self.subscriber.session.zid().into(), - eid: self.subscriber.state.id, + zid: self.inner.session_id.into(), + eid: self.inner.state.id, } .into() } /// Returns the [`KeyExpr`] this Subscriber subscribes to. pub fn key_expr(&self) -> &KeyExpr<'static> { - &self.subscriber.state.key_expr + &self.inner.state.key_expr } /// Returns a reference to this subscriber's handler. @@ -495,10 +458,7 @@ impl<'a, Handler> Subscriber<'a, Handler> { &mut self.handler } - /// Close a [`Subscriber`]. - /// - /// Subscribers are automatically closed when dropped, but you may want to use this function to handle errors or - /// close the Subscriber asynchronously. + /// Undeclare the [`Subscriber`]. /// /// # Examples /// ``` @@ -514,42 +474,55 @@ impl<'a, Handler> Subscriber<'a, Handler> { /// # } /// ``` #[inline] - pub fn undeclare(self) -> SubscriberUndeclaration<'a> { - self.subscriber.undeclare() + pub fn undeclare(self) -> SubscriberUndeclaration + where + Handler: Send, + { + self.undeclare_inner(()) } - /// Make the subscriber run in background, until the session is closed. - #[inline] - #[zenoh_macros::unstable] - pub fn background(mut self) { - // It's not necessary to undeclare this resource when session close, as other sessions - // will clean all resources related to the closed one. - // So we can just never undeclare it. - self.subscriber.undeclare_on_drop = false; + fn undeclare_impl(&mut self) -> ZResult<()> { + // set the flag first to avoid double panic if this function panic + self.inner.undeclare_on_drop = false; + self.inner + .session + .undeclare_subscriber_inner(self.inner.state.id, self.inner.kind) } } -impl<'a, T> UndeclarableSealed<(), SubscriberUndeclaration<'a>> for Subscriber<'a, T> { - fn undeclare_inner(self, _: ()) -> SubscriberUndeclaration<'a> { - UndeclarableSealed::undeclare_inner(self.subscriber, ()) +impl Drop for Subscriber { + fn drop(&mut self) { + if self.inner.undeclare_on_drop { + if let Err(error) = self.undeclare_impl() { + error!(error); + } + } + } +} + +impl<'a, Handler: Send + 'a> UndeclarableSealed<()> for Subscriber { + type Undeclaration = SubscriberUndeclaration; + + fn undeclare_inner(self, _: ()) -> Self::Undeclaration { + SubscriberUndeclaration(self) } } -impl Deref for Subscriber<'_, Handler> { +impl Deref for Subscriber { type Target = Handler; fn deref(&self) -> &Self::Target { self.handler() } } -impl DerefMut for Subscriber<'_, Handler> { +impl DerefMut for Subscriber { fn deref_mut(&mut self) -> &mut Self::Target { self.handler_mut() } } /// A [`Subscriber`] that provides data through a `flume` channel. -pub type FlumeSubscriber<'a> = Subscriber<'a, flume::Receiver>; +pub type FlumeSubscriber = Subscriber>; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum SubscriberKind { diff --git a/zenoh/src/lib.rs b/zenoh/src/lib.rs index 0190acc319..aacc52eefa 100644 --- a/zenoh/src/lib.rs +++ b/zenoh/src/lib.rs @@ -197,7 +197,7 @@ pub mod session { builders::publisher::{SessionDeleteBuilder, SessionPutBuilder}, info::{PeersZenohIdBuilder, RoutersZenohIdBuilder, SessionInfo, ZenohIdBuilder}, query::SessionGetBuilder, - session::{open, OpenBuilder, Session, SessionDeclarations, SessionRef, Undeclarable}, + session::{open, OpenBuilder, Session, Undeclarable}, }; } @@ -236,7 +236,6 @@ pub mod pubsub { #[zenoh_macros::unstable] pub use crate::api::publisher::{ MatchingListener, MatchingListenerBuilder, MatchingListenerUndeclaration, MatchingStatus, - PublisherDeclarations, PublisherRef, }; pub use crate::api::{ builders::publisher::{ diff --git a/zenoh/src/net/primitives/demux.rs b/zenoh/src/net/primitives/demux.rs index 59111e5441..e4774aab4a 100644 --- a/zenoh/src/net/primitives/demux.rs +++ b/zenoh/src/net/primitives/demux.rs @@ -66,7 +66,7 @@ impl TransportPeerEventHandler for DeMux { } match msg.body { - NetworkBody::Push(m) => self.face.send_push(m), + NetworkBody::Push(m) => self.face.send_push(m, msg.reliability), NetworkBody::Declare(m) => self.face.send_declare(m), NetworkBody::Interest(m) => self.face.send_interest(m), NetworkBody::Request(m) => self.face.send_request(m), diff --git a/zenoh/src/net/primitives/mod.rs b/zenoh/src/net/primitives/mod.rs index 837571f7f6..21526466f5 100644 --- a/zenoh/src/net/primitives/mod.rs +++ b/zenoh/src/net/primitives/mod.rs @@ -18,8 +18,9 @@ use std::any::Any; pub use demux::*; pub use mux::*; -use zenoh_protocol::network::{ - interest::Interest, Declare, Push, Request, Response, ResponseFinal, +use zenoh_protocol::{ + core::Reliability, + network::{interest::Interest, Declare, Push, Request, Response, ResponseFinal}, }; use super::routing::RoutingContext; @@ -29,7 +30,7 @@ pub trait Primitives: Send + Sync { fn send_declare(&self, msg: Declare); - fn send_push(&self, msg: Push); + fn send_push(&self, msg: Push, reliability: Reliability); fn send_request(&self, msg: Request); @@ -47,7 +48,7 @@ pub(crate) trait EPrimitives: Send + Sync { fn send_declare(&self, ctx: RoutingContext); - fn send_push(&self, msg: Push); + fn send_push(&self, msg: Push, reliability: Reliability); fn send_request(&self, msg: Request); @@ -64,7 +65,7 @@ impl Primitives for DummyPrimitives { fn send_declare(&self, _msg: Declare) {} - fn send_push(&self, _msg: Push) {} + fn send_push(&self, _msg: Push, _reliability: Reliability) {} fn send_request(&self, _msg: Request) {} @@ -80,7 +81,7 @@ impl EPrimitives for DummyPrimitives { fn send_declare(&self, _ctx: RoutingContext) {} - fn send_push(&self, _msg: Push) {} + fn send_push(&self, _msg: Push, _reliability: Reliability) {} fn send_request(&self, _msg: Request) {} diff --git a/zenoh/src/net/primitives/mux.rs b/zenoh/src/net/primitives/mux.rs index bc718ba324..47067f231e 100644 --- a/zenoh/src/net/primitives/mux.rs +++ b/zenoh/src/net/primitives/mux.rs @@ -13,9 +13,12 @@ // use std::sync::OnceLock; -use zenoh_protocol::network::{ - interest::Interest, Declare, NetworkBody, NetworkMessage, Push, Request, Response, - ResponseFinal, +use zenoh_protocol::{ + core::Reliability, + network::{ + interest::Interest, Declare, NetworkBody, NetworkMessage, Push, Request, Response, + ResponseFinal, + }, }; use zenoh_transport::{multicast::TransportMulticast, unicast::TransportUnicast}; @@ -46,6 +49,7 @@ impl Primitives for Mux { fn send_interest(&self, msg: Interest) { let msg = NetworkMessage { body: NetworkBody::Interest(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -74,6 +78,7 @@ impl Primitives for Mux { fn send_declare(&self, msg: Declare) { let msg = NetworkMessage { body: NetworkBody::Declare(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -95,9 +100,10 @@ impl Primitives for Mux { } } - fn send_push(&self, msg: Push) { + fn send_push(&self, msg: Push, reliability: Reliability) { let msg = NetworkMessage { body: NetworkBody::Push(msg), + reliability, #[cfg(feature = "stats")] size: None, }; @@ -122,6 +128,7 @@ impl Primitives for Mux { fn send_request(&self, msg: Request) { let msg = NetworkMessage { body: NetworkBody::Request(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -146,6 +153,7 @@ impl Primitives for Mux { fn send_response(&self, msg: Response) { let msg = NetworkMessage { body: NetworkBody::Response(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -170,6 +178,7 @@ impl Primitives for Mux { fn send_response_final(&self, msg: ResponseFinal) { let msg = NetworkMessage { body: NetworkBody::ResponseFinal(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -201,6 +210,7 @@ impl EPrimitives for Mux { let ctx = RoutingContext { msg: NetworkMessage { body: NetworkBody::Interest(ctx.msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }, @@ -226,6 +236,7 @@ impl EPrimitives for Mux { let ctx = RoutingContext { msg: NetworkMessage { body: NetworkBody::Declare(ctx.msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }, @@ -247,9 +258,10 @@ impl EPrimitives for Mux { } } - fn send_push(&self, msg: Push) { + fn send_push(&self, msg: Push, reliability: Reliability) { let msg = NetworkMessage { body: NetworkBody::Push(msg), + reliability, #[cfg(feature = "stats")] size: None, }; @@ -274,6 +286,7 @@ impl EPrimitives for Mux { fn send_request(&self, msg: Request) { let msg = NetworkMessage { body: NetworkBody::Request(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -298,6 +311,7 @@ impl EPrimitives for Mux { fn send_response(&self, msg: Response) { let msg = NetworkMessage { body: NetworkBody::Response(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -322,6 +336,7 @@ impl EPrimitives for Mux { fn send_response_final(&self, msg: ResponseFinal) { let msg = NetworkMessage { body: NetworkBody::ResponseFinal(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -368,6 +383,7 @@ impl Primitives for McastMux { fn send_interest(&self, msg: Interest) { let msg = NetworkMessage { body: NetworkBody::Interest(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -392,6 +408,7 @@ impl Primitives for McastMux { fn send_declare(&self, msg: Declare) { let msg = NetworkMessage { body: NetworkBody::Declare(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -413,9 +430,10 @@ impl Primitives for McastMux { } } - fn send_push(&self, msg: Push) { + fn send_push(&self, msg: Push, reliability: Reliability) { let msg = NetworkMessage { body: NetworkBody::Push(msg), + reliability, #[cfg(feature = "stats")] size: None, }; @@ -440,6 +458,7 @@ impl Primitives for McastMux { fn send_request(&self, msg: Request) { let msg = NetworkMessage { body: NetworkBody::Request(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -464,6 +483,7 @@ impl Primitives for McastMux { fn send_response(&self, msg: Response) { let msg = NetworkMessage { body: NetworkBody::Response(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -488,6 +508,7 @@ impl Primitives for McastMux { fn send_response_final(&self, msg: ResponseFinal) { let msg = NetworkMessage { body: NetworkBody::ResponseFinal(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -519,6 +540,7 @@ impl EPrimitives for McastMux { let ctx = RoutingContext { msg: NetworkMessage { body: NetworkBody::Interest(ctx.msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }, @@ -544,6 +566,7 @@ impl EPrimitives for McastMux { let ctx = RoutingContext { msg: NetworkMessage { body: NetworkBody::Declare(ctx.msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }, @@ -565,9 +588,10 @@ impl EPrimitives for McastMux { } } - fn send_push(&self, msg: Push) { + fn send_push(&self, msg: Push, reliability: Reliability) { let msg = NetworkMessage { body: NetworkBody::Push(msg), + reliability, #[cfg(feature = "stats")] size: None, }; @@ -592,6 +616,7 @@ impl EPrimitives for McastMux { fn send_request(&self, msg: Request) { let msg = NetworkMessage { body: NetworkBody::Request(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -616,6 +641,7 @@ impl EPrimitives for McastMux { fn send_response(&self, msg: Response) { let msg = NetworkMessage { body: NetworkBody::Response(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; @@ -640,6 +666,7 @@ impl EPrimitives for McastMux { fn send_response_final(&self, msg: ResponseFinal) { let msg = NetworkMessage { body: NetworkBody::ResponseFinal(msg), + reliability: Reliability::Reliable, #[cfg(feature = "stats")] size: None, }; diff --git a/zenoh/src/net/routing/dispatcher/face.rs b/zenoh/src/net/routing/dispatcher/face.rs index bbc910b124..0bc14450a7 100644 --- a/zenoh/src/net/routing/dispatcher/face.rs +++ b/zenoh/src/net/routing/dispatcher/face.rs @@ -20,7 +20,7 @@ use std::{ use tokio_util::sync::CancellationToken; use zenoh_protocol::{ - core::{ExprId, WhatAmI, ZenohIdProto}, + core::{ExprId, Reliability, WhatAmI, ZenohIdProto}, network::{ interest::{InterestId, InterestMode, InterestOptions}, Mapping, Push, Request, RequestId, Response, ResponseFinal, @@ -379,16 +379,8 @@ impl Primitives for Face { } #[inline] - fn send_push(&self, msg: Push) { - full_reentrant_route_data( - &self.tables, - &self.state, - &msg.wire_expr, - msg.ext_qos, - msg.ext_tstamp, - msg.payload, - msg.ext_nodeid.node_id, - ); + fn send_push(&self, msg: Push, reliability: Reliability) { + route_data(&self.tables, &self.state, msg, reliability); } fn send_request(&self, msg: Request) { diff --git a/zenoh/src/net/routing/dispatcher/pubsub.rs b/zenoh/src/net/routing/dispatcher/pubsub.rs index 84c8433a48..7334f4f267 100644 --- a/zenoh/src/net/routing/dispatcher/pubsub.rs +++ b/zenoh/src/net/routing/dispatcher/pubsub.rs @@ -15,7 +15,7 @@ use std::{collections::HashMap, sync::Arc}; use zenoh_core::zread; use zenoh_protocol::{ - core::{key_expr::keyexpr, WhatAmI, WireExpr}, + core::{key_expr::keyexpr, Reliability, WhatAmI, WireExpr}, network::{ declare::{ext, subscriber::ext::SubscriberInfo, SubscriberId}, Push, @@ -385,42 +385,42 @@ macro_rules! inc_stats { }; } -pub fn full_reentrant_route_data( +pub fn route_data( tables_ref: &Arc, face: &FaceState, - expr: &WireExpr, - ext_qos: ext::QoSType, - ext_tstamp: Option, - mut payload: PushBody, - routing_context: NodeId, + mut msg: Push, + reliability: Reliability, ) { let tables = zread!(tables_ref.tables); - match tables.get_mapping(face, &expr.scope, expr.mapping).cloned() { + match tables + .get_mapping(face, &msg.wire_expr.scope, msg.wire_expr.mapping) + .cloned() + { Some(prefix) => { tracing::trace!( "{} Route data for res {}{}", face, prefix.expr(), - expr.suffix.as_ref() + msg.wire_expr.suffix.as_ref() ); - let mut expr = RoutingExpr::new(&prefix, expr.suffix.as_ref()); + let mut expr = RoutingExpr::new(&prefix, msg.wire_expr.suffix.as_ref()); #[cfg(feature = "stats")] let admin = expr.full_expr().starts_with("@/"); #[cfg(feature = "stats")] if !admin { - inc_stats!(face, rx, user, payload) + inc_stats!(face, rx, user, msg.payload) } else { - inc_stats!(face, rx, admin, payload) + inc_stats!(face, rx, admin, msg.payload) } if tables.hat_code.ingress_filter(&tables, face, &mut expr) { let res = Resource::get_resource(&prefix, expr.suffix); - let route = get_data_route(&tables, face, &res, &mut expr, routing_context); + let route = get_data_route(&tables, face, &res, &mut expr, msg.ext_nodeid.node_id); if !route.is_empty() { - treat_timestamp!(&tables.hlc, payload, tables.drop_future_timestamp); + treat_timestamp!(&tables.hlc, msg.payload, tables.drop_future_timestamp); if route.len() == 1 { let (outface, key_expr, context) = route.values().next().unwrap(); @@ -431,18 +431,21 @@ pub fn full_reentrant_route_data( drop(tables); #[cfg(feature = "stats")] if !admin { - inc_stats!(face, tx, user, payload) + inc_stats!(face, tx, user, msg.payload) } else { - inc_stats!(face, tx, admin, payload) + inc_stats!(face, tx, admin, msg.payload) } - outface.primitives.send_push(Push { - wire_expr: key_expr.into(), - ext_qos, - ext_tstamp, - ext_nodeid: ext::NodeIdType { node_id: *context }, - payload, - }) + outface.primitives.send_push( + Push { + wire_expr: key_expr.into(), + ext_qos: msg.ext_qos, + ext_tstamp: msg.ext_tstamp, + ext_nodeid: ext::NodeIdType { node_id: *context }, + payload: msg.payload, + }, + reliability, + ) } } else if tables.whatami == WhatAmI::Router { let route = route @@ -459,18 +462,21 @@ pub fn full_reentrant_route_data( for (outface, key_expr, context) in route { #[cfg(feature = "stats")] if !admin { - inc_stats!(face, tx, user, payload) + inc_stats!(face, tx, user, msg.payload) } else { - inc_stats!(face, tx, admin, payload) + inc_stats!(face, tx, admin, msg.payload) } - outface.primitives.send_push(Push { - wire_expr: key_expr, - ext_qos, - ext_tstamp: None, - ext_nodeid: ext::NodeIdType { node_id: context }, - payload: payload.clone(), - }) + outface.primitives.send_push( + Push { + wire_expr: key_expr, + ext_qos: msg.ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { node_id: context }, + payload: msg.payload.clone(), + }, + reliability, + ) } } else { drop(tables); @@ -483,18 +489,21 @@ pub fn full_reentrant_route_data( { #[cfg(feature = "stats")] if !admin { - inc_stats!(face, tx, user, payload) + inc_stats!(face, tx, user, msg.payload) } else { - inc_stats!(face, tx, admin, payload) + inc_stats!(face, tx, admin, msg.payload) } - outface.primitives.send_push(Push { - wire_expr: key_expr.into(), - ext_qos, - ext_tstamp: None, - ext_nodeid: ext::NodeIdType { node_id: *context }, - payload: payload.clone(), - }) + outface.primitives.send_push( + Push { + wire_expr: key_expr.into(), + ext_qos: msg.ext_qos, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { node_id: *context }, + payload: msg.payload.clone(), + }, + reliability, + ) } } } @@ -502,7 +511,11 @@ pub fn full_reentrant_route_data( } } None => { - tracing::error!("{} Route data with unknown scope {}!", face, expr.scope); + tracing::error!( + "{} Route data with unknown scope {}!", + face, + msg.wire_expr.scope + ); } } } diff --git a/zenoh/src/net/routing/dispatcher/resource.rs b/zenoh/src/net/routing/dispatcher/resource.rs index 01ff9b2817..ab84241666 100644 --- a/zenoh/src/net/routing/dispatcher/resource.rs +++ b/zenoh/src/net/routing/dispatcher/resource.rs @@ -341,6 +341,7 @@ impl Resource { r.parent.take(); r.children.clear(); r.nonwild_prefix.take(); + r.context.take(); r.session_ctxs.clear(); } diff --git a/zenoh/src/net/runtime/adminspace.rs b/zenoh/src/net/runtime/adminspace.rs index ce87d68ef0..d3e2a3c1ad 100644 --- a/zenoh/src/net/runtime/adminspace.rs +++ b/zenoh/src/net/runtime/adminspace.rs @@ -26,7 +26,7 @@ use zenoh_plugin_trait::{PluginControl, PluginStatus}; #[cfg(feature = "plugins")] use zenoh_protocol::core::key_expr::keyexpr; use zenoh_protocol::{ - core::{key_expr::OwnedKeyExpr, ExprId, WireExpr, EMPTY_EXPR_ID}, + core::{key_expr::OwnedKeyExpr, ExprId, Reliability, WireExpr, EMPTY_EXPR_ID}, network::{ declare::{ queryable::ext::QueryableInfoType, subscriber::ext::SubscriberInfo, QueryableId, @@ -375,7 +375,7 @@ impl Primitives for AdminSpace { } } - fn send_push(&self, msg: Push) { + fn send_push(&self, msg: Push, _reliability: Reliability) { trace!("recv Push {:?}", msg); { let conf = self.context.runtime.state.config.lock(); @@ -516,8 +516,8 @@ impl crate::net::primitives::EPrimitives for AdminSpace { } #[inline] - fn send_push(&self, msg: Push) { - (self as &dyn Primitives).send_push(msg) + fn send_push(&self, msg: Push, reliability: Reliability) { + (self as &dyn Primitives).send_push(msg, reliability) } #[inline] diff --git a/zenoh/src/net/runtime/mod.rs b/zenoh/src/net/runtime/mod.rs index 9abb01b94e..9f56f4f720 100644 --- a/zenoh/src/net/runtime/mod.rs +++ b/zenoh/src/net/runtime/mod.rs @@ -279,13 +279,10 @@ impl Runtime { // the task responsible for resource clean up was aborted earlier than expected. // This should be resolved by identfying correspodning task, and placing // cancellation token manually inside it. - self.router() - .tables - .tables - .write() - .unwrap() - .root_res - .close(); + let router = self.router(); + let mut tables = router.tables.tables.write().unwrap(); + tables.root_res.close(); + tables.faces.clear(); Ok(()) } diff --git a/zenoh/src/net/tests/tables.rs b/zenoh/src/net/tests/tables.rs index 5fd8a49261..bab638af83 100644 --- a/zenoh/src/net/tests/tables.rs +++ b/zenoh/src/net/tests/tables.rs @@ -26,7 +26,7 @@ use zenoh_protocol::{ EMPTY_EXPR_ID, }, network::{ - declare::subscriber::ext::SubscriberInfo, ext, Declare, DeclareBody, DeclareKeyExpr, + declare::subscriber::ext::SubscriberInfo, ext, Declare, DeclareBody, DeclareKeyExpr, Push, }, zenoh::{PushBody, Put}, }; @@ -534,7 +534,7 @@ impl Primitives for ClientPrimitives { } } - fn send_push(&self, msg: zenoh_protocol::network::Push) { + fn send_push(&self, msg: zenoh_protocol::network::Push, _reliability: Reliability) { *zlock!(self.data) = Some(msg.wire_expr.to_owned()); } @@ -563,7 +563,7 @@ impl EPrimitives for ClientPrimitives { } } - fn send_push(&self, msg: zenoh_protocol::network::Push) { + fn send_push(&self, msg: zenoh_protocol::network::Push, _reliability: Reliability) { *zlock!(self.data) = Some(msg.wire_expr.to_owned()); } @@ -736,23 +736,26 @@ fn client_test() { primitives1.clear_data(); primitives2.clear_data(); - full_reentrant_route_data( + route_data( &tables, &face0.upgrade().unwrap(), - &"test/client/z1_wr1".into(), - ext::QoSType::DEFAULT, - None, - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::empty(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - ext_attachment: None, - }), - 0, + Push { + wire_expr: "test/client/z1_wr1".into(), + ext_qos: ext::QoSType::DEFAULT, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { node_id: 0 }, + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::empty(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + ext_attachment: None, + }), + }, + Reliability::Reliable, ); // functional check @@ -770,23 +773,26 @@ fn client_test() { primitives0.clear_data(); primitives1.clear_data(); primitives2.clear_data(); - full_reentrant_route_data( + route_data( &router.tables, &face0.upgrade().unwrap(), - &WireExpr::from(11).with_suffix("/z1_wr2"), - ext::QoSType::DEFAULT, - None, - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::empty(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - ext_attachment: None, - }), - 0, + Push { + wire_expr: WireExpr::from(11).with_suffix("/z1_wr2"), + ext_qos: ext::QoSType::DEFAULT, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { node_id: 0 }, + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::empty(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + ext_attachment: None, + }), + }, + Reliability::Reliable, ); // functional check @@ -804,23 +810,26 @@ fn client_test() { primitives0.clear_data(); primitives1.clear_data(); primitives2.clear_data(); - full_reentrant_route_data( + route_data( &router.tables, &face1.upgrade().unwrap(), - &"test/client/**".into(), - ext::QoSType::DEFAULT, - None, - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::empty(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - ext_attachment: None, - }), - 0, + Push { + wire_expr: "test/client/**".into(), + ext_qos: ext::QoSType::DEFAULT, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { node_id: 0 }, + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::empty(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + ext_attachment: None, + }), + }, + Reliability::Reliable, ); // functional check @@ -838,23 +847,26 @@ fn client_test() { primitives0.clear_data(); primitives1.clear_data(); primitives2.clear_data(); - full_reentrant_route_data( + route_data( &router.tables, &face0.upgrade().unwrap(), - &12.into(), - ext::QoSType::DEFAULT, - None, - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::empty(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - ext_attachment: None, - }), - 0, + Push { + wire_expr: 12.into(), + ext_qos: ext::QoSType::DEFAULT, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { node_id: 0 }, + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::empty(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + ext_attachment: None, + }), + }, + Reliability::Reliable, ); // functional check @@ -872,23 +884,26 @@ fn client_test() { primitives0.clear_data(); primitives1.clear_data(); primitives2.clear_data(); - full_reentrant_route_data( + route_data( &router.tables, &face1.upgrade().unwrap(), - &22.into(), - ext::QoSType::DEFAULT, - None, - PushBody::Put(Put { - timestamp: None, - encoding: Encoding::empty(), - ext_sinfo: None, - #[cfg(feature = "shared-memory")] - ext_shm: None, - ext_unknown: vec![], - payload: ZBuf::empty(), - ext_attachment: None, - }), - 0, + Push { + wire_expr: 22.into(), + ext_qos: ext::QoSType::DEFAULT, + ext_tstamp: None, + ext_nodeid: ext::NodeIdType { node_id: 0 }, + payload: PushBody::Put(Put { + timestamp: None, + encoding: Encoding::empty(), + ext_sinfo: None, + #[cfg(feature = "shared-memory")] + ext_shm: None, + ext_unknown: vec![], + payload: ZBuf::empty(), + ext_attachment: None, + }), + }, + Reliability::Reliable, ); // functional check diff --git a/zenoh/src/prelude.rs b/zenoh/src/prelude.rs index 373d56c65a..022a2d63cb 100644 --- a/zenoh/src/prelude.rs +++ b/zenoh/src/prelude.rs @@ -25,8 +25,6 @@ //! ``` mod _prelude { - #[zenoh_macros::unstable] - pub use crate::api::publisher::PublisherDeclarations; #[zenoh_macros::unstable] pub use crate::api::selector::ZenohParameters; pub use crate::{ @@ -34,7 +32,7 @@ mod _prelude { builders::sample::{ EncodingBuilderTrait, QoSBuilderTrait, SampleBuilderTrait, TimestampBuilderTrait, }, - session::{SessionDeclarations, Undeclarable}, + session::Undeclarable, }, config::ValidatedMap, Error as ZError, Resolvable, Resolve, Result as ZResult, diff --git a/zenoh/tests/events.rs b/zenoh/tests/events.rs index 11a6e18b53..e3a4d61656 100644 --- a/zenoh/tests/events.rs +++ b/zenoh/tests/events.rs @@ -52,7 +52,6 @@ async fn close_session(session: Session) { #[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn zenoh_events() { - use zenoh::prelude::SessionDeclarations; let session = open_session(&["tcp/127.0.0.1:18447"], &[]).await; let zid = session.zid(); let sub1 = diff --git a/zenoh/tests/liveliness.rs b/zenoh/tests/liveliness.rs index 4d964cc1cf..c67a1deb6d 100644 --- a/zenoh/tests/liveliness.rs +++ b/zenoh/tests/liveliness.rs @@ -19,7 +19,7 @@ use zenoh_core::ztimeout; async fn test_liveliness_subscriber_clique() { use std::time::Duration; - use zenoh::{config, prelude::*, sample::SampleKind}; + use zenoh::{config, sample::SampleKind}; use zenoh_config::WhatAmI; use zenoh_link::EndPoint; const TIMEOUT: Duration = Duration::from_secs(60); @@ -83,7 +83,7 @@ async fn test_liveliness_subscriber_clique() { async fn test_liveliness_query_clique() { use std::time::Duration; - use zenoh::{config, prelude::*, sample::SampleKind}; + use zenoh::{config, sample::SampleKind}; use zenoh_config::WhatAmI; use zenoh_link::EndPoint; const TIMEOUT: Duration = Duration::from_secs(60); @@ -140,7 +140,7 @@ async fn test_liveliness_query_clique() { async fn test_liveliness_subscriber_brokered() { use std::time::Duration; - use zenoh::{config, prelude::*, sample::SampleKind}; + use zenoh::{config, sample::SampleKind}; use zenoh_config::WhatAmI; use zenoh_link::EndPoint; @@ -219,7 +219,7 @@ async fn test_liveliness_subscriber_brokered() { async fn test_liveliness_query_brokered() { use std::time::Duration; - use zenoh::{config, prelude::*, sample::SampleKind}; + use zenoh::{config, sample::SampleKind}; use zenoh_config::WhatAmI; use zenoh_link::EndPoint; const TIMEOUT: Duration = Duration::from_secs(60); @@ -290,7 +290,7 @@ async fn test_liveliness_query_brokered() { async fn test_liveliness_subscriber_local() { use std::time::Duration; - use zenoh::{config, prelude::*, sample::SampleKind}; + use zenoh::{config, sample::SampleKind}; use zenoh_config::WhatAmI; const TIMEOUT: Duration = Duration::from_secs(60); const SLEEP: Duration = Duration::from_secs(1); @@ -333,7 +333,7 @@ async fn test_liveliness_subscriber_local() { async fn test_liveliness_query_local() { use std::time::Duration; - use zenoh::{config, prelude::*, sample::SampleKind}; + use zenoh::{config, sample::SampleKind}; use zenoh_config::WhatAmI; const TIMEOUT: Duration = Duration::from_secs(60); const SLEEP: Duration = Duration::from_secs(1); diff --git a/zenoh/tests/routing.rs b/zenoh/tests/routing.rs index 07971b7853..1023584c70 100644 --- a/zenoh/tests/routing.rs +++ b/zenoh/tests/routing.rs @@ -56,7 +56,7 @@ enum Task { impl Task { async fn run( &self, - session: Arc, + session: Session, remaining_checkpoints: Arc, token: CancellationToken, ) -> Result<()> { @@ -386,7 +386,7 @@ impl Recipe { // In case of client can't connect to some peers/routers loop { if let Ok(session) = ztimeout!(zenoh::open(config.clone())) { - break session.into_arc(); + break session; } else { tokio::time::sleep(Duration::from_secs(1)).await; } @@ -421,7 +421,7 @@ impl Recipe { // node_task_tracker.wait().await; // Close the session once all the task associated with the node are done. - ztimeout!(Arc::try_unwrap(session).unwrap().close())?; + ztimeout!(session.close())?; println!("Node: {} is closed.", &node.name); Result::Ok(())