From a5b0dcd6e1ffa9f667ee66033609f06f96c71d7c Mon Sep 17 00:00:00 2001 From: Kotauskas Date: Sun, 24 Mar 2024 15:16:15 +0300 Subject: [PATCH] `from_handle_and_options()` --- src/os/windows/named_pipe/listener.rs | 25 ++++++++++- src/os/windows/named_pipe/listener/options.rs | 26 +++++------ src/os/windows/named_pipe/tokio/listener.rs | 45 ++++++++++++++++--- 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/src/os/windows/named_pipe/listener.rs b/src/os/windows/named_pipe/listener.rs index 483e6ae..a8c401a 100644 --- a/src/os/windows/named_pipe/listener.rs +++ b/src/os/windows/named_pipe/listener.rs @@ -22,7 +22,7 @@ use std::{ }; use windows_sys::Win32::{Foundation::ERROR_PIPE_CONNECTED, System::Pipes::ConnectNamedPipe}; -// TODO add conversion from handle after all; allow passing assumed config +// TODO add conversion from handle after all /// The server for a named pipe, listening for connections to clients and producing pipe streams. /// @@ -60,12 +60,14 @@ impl PipeListener { Ok(PipeStream::new(raw)) } + /// Creates an iterator which accepts connections from clients, blocking each time `next()` is /// called until one connects. - #[inline(always)] + #[inline] pub fn incoming(&self) -> Incoming<'_, Rm, Sm> { Incoming(self) } + /// Enables or disables the nonblocking mode for all existing instances of the listener and /// future ones. By default, it is disabled. /// @@ -86,6 +88,24 @@ impl PipeListener { Ok(()) } + /// Creates a listener from a handle and a [`PipeListenerOptions`] table with the assumption + /// that the handle was created with those options. + /// + /// The options are necessary to provide because the listener needs to create new instances of + /// the named pipe server in `.accept()`. + // TODO mention TryFrom here + pub fn from_handle_and_options( + handle: OwnedHandle, + options: PipeListenerOptions<'static>, + ) -> Self { + Self { + nonblocking: AtomicBool::new(options.nonblocking), + config: options, + stored_instance: Mutex::new(FileHandle::from(handle)), + _phantom: PhantomData, + } + } + fn create_instance(&self, nonblocking: bool) -> io::Result { self.config .create_instance(false, nonblocking, false, Self::STREAM_ROLE, Rm::MODE) @@ -101,6 +121,7 @@ impl Debug for PipeListener { .finish() } } + impl From> for OwnedHandle { fn from(p: PipeListener) -> Self { p.stored_instance.into_inner().expect(LOCK_POISON).into() diff --git a/src/os/windows/named_pipe/listener/options.rs b/src/os/windows/named_pipe/listener/options.rs index ed35643..10daf8e 100644 --- a/src/os/windows/named_pipe/listener/options.rs +++ b/src/os/windows/named_pipe/listener/options.rs @@ -39,19 +39,19 @@ pub struct PipeListenerOptions<'path> { pub nonblocking: bool, /// Specifies the maximum amount of instances of the pipe which can be created, i.e. how many /// clients can be communicated with at once. If set to 1, trying to create multiple instances - /// at the same time will return an error. If set to `None`, no limit is applied. The value 255 - /// is not allowed because of Windows limitations. + /// at the same time will return an error (in fact, this breaks `.accept()`). If set to `None`, + /// no limit is applied. The value 255 is not allowed because it is the underlying Windows API's + /// sentinel for not having a limit. pub instance_limit: Option, /// Enables write-through mode, which applies only to network connections to the pipe. If /// enabled, sending to the pipe will always block until all data is delivered to the other end /// instead of piling up in the kernel's network buffer until a certain amount of data - /// accamulates or a certain period of time passes, which is when the system actually sends the + /// accumulates or a certain period of time passes, which is when the system actually sends the /// contents of the buffer over the network. /// - /// Not required for pipes which are restricted to local connections only. If debug assertions - /// are enabled, setting this parameter on a local-only pipe will cause a panic when the pipe is - /// created; in release builds, creation will successfully complete without any errors and the - /// flag will be completely ignored. + /// If debug assertions are enabled, setting this parameter on a local-only pipe will cause a + /// panic when the pipe is created; in release builds, creation will successfully complete + /// without any errors and the flag will be completely ignored. pub write_through: bool, /// Enables remote machines to connect to the named pipe over the network. pub accept_remote: bool, @@ -165,14 +165,12 @@ impl<'path> PipeListenerOptions<'path> { pub fn create(&self) -> io::Result> { let (owned_config, instance) = self._create(PipeListener::::STREAM_ROLE, Rm::MODE)?; - let nonblocking = owned_config.nonblocking.into(); - Ok(PipeListener { - config: owned_config, - nonblocking, - stored_instance: Mutex::new(instance), - _phantom: PhantomData, - }) + Ok(PipeListener::from_handle_and_options( + instance.into(), + owned_config, + )) } + /// Alias for [`.create()`](Self::create) with the same `Rm` and `Sm`. #[inline] pub fn create_duplex(&self) -> io::Result> { diff --git a/src/os/windows/named_pipe/tokio/listener.rs b/src/os/windows/named_pipe/tokio/listener.rs index 8851d5c..f3c700c 100644 --- a/src/os/windows/named_pipe/tokio/listener.rs +++ b/src/os/windows/named_pipe/tokio/listener.rs @@ -127,6 +127,43 @@ impl PipeListener { Ok(PipeStream::new(raw)) } + /// Creates a listener from a [corresponding Tokio object](TokioNPServer) and a + /// [`PipeListenerOptions`] table with the assumption that the handle was created with those + /// options. + /// + /// The options are necessary to provide because the listener needs to create new instances of + /// the named pipe server in `.accept()`. + // TODO mention TryFrom here + pub fn from_tokio_and_options( + tokio_object: TokioNPServer, + options: PipeListenerOptions<'static>, + ) -> Self { + Self { + config: options, + stored_instance: Mutex::new(tokio_object), + _phantom: PhantomData, + } + } + + /// Creates a listener from a handle and a [`PipeListenerOptions`] table with the assumption + /// that the handle was created with those options. + /// + /// The options are necessary to provide because the listener needs to create new instances of + /// the named pipe server in `.accept()`. + /// + /// # Errors + /// Returns an error if called outside a Tokio runtime. + // TODO mention TryFrom here + pub fn from_handle_and_options( + handle: OwnedHandle, + options: PipeListenerOptions<'static>, + ) -> io::Result { + Ok(Self::from_tokio_and_options( + npserver_from_handle(handle)?, + options, + )) + } + fn create_instance(&self) -> io::Result { self.config .create_instance(false, false, true, Self::STREAM_ROLE, Rm::MODE) @@ -177,11 +214,7 @@ impl PipeListenerOptionsExt for PipeListenerOptions<'_> { fn create_tokio(&self) -> io::Result> { let (owned_config, instance) = _create_tokio(self, PipeListener::::STREAM_ROLE, Rm::MODE)?; - Ok(PipeListener { - config: owned_config, - stored_instance: Mutex::new(instance), - _phantom: PhantomData, - }) + Ok(PipeListener::from_tokio_and_options(instance, owned_config)) } } impl Sealed for PipeListenerOptions<'_> {} @@ -197,7 +230,7 @@ fn _create_tokio( config.nonblocking = false; let instance = config - .create_instance(true, config.nonblocking, true, role, recv_mode) + .create_instance(true, false, true, role, recv_mode) .and_then(npserver_from_handle)?; Ok((config, instance))