1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
use super::{options::ListenerOptions, r#trait};
use crate::local_socket::{ListenerNonblockingMode, Stream};
#[cfg(unix)]
use crate::os::unix::uds_local_socket as uds_impl;
#[cfg(windows)]
use crate::os::windows::named_pipe::local_socket as np_impl;
use std::{io, iter::FusedIterator};

impmod! {local_socket::dispatch_sync as dispatch}

mkenum!(
/// Local socket server, listening for connections.
///
/// This struct is created by [`ListenerOptions`](super::options::ListenerOptions).
///
/// # Name reclamation
/// *This section only applies to Unix domain sockets.*
///
/// When a Unix domain socket listener is closed, its associated socket file is not automatically
/// deleted. Instead, it remains on the filesystem in a zombie state, neither accepting connections
/// nor allowing a new listener to reuse it – [`create_sync()`] will return
/// [`AddrInUse`](io::ErrorKind::AddrInUse) unless it is deleted manually.
///
/// Interprocess implements *automatic name reclamation* via: when the local socket listener is
/// dropped, it performs [`std::fs::remove_file()`] (i.e. `unlink()`) with the path that was
/// originally passed to [`create_sync()`], allowing for subsequent reuse of the local socket name.
///
/// If the program crashes in a way that doesn't unwind the stack, the deletion will not occur and
/// the socket file will linger on the filesystem, in which case manual deletion will be necessary.
/// Identially, the automatic name reclamation mechanism can be opted out of via
/// [`.do_not_reclaim_name_on_drop()`](trait::Listener::do_not_reclaim_name_on_drop) on the listener
/// or [`.reclaim_name(false)`](super::options::ListenerOptions::reclaim_name) on the builder.
///
/// Note that the socket file can be unlinked by other programs at any time, retaining the inode the
/// listener is bound to but making it inaccessible to peers if it was at its last hardlink. If that
/// happens and another listener takes the same path before the first one performs name reclamation,
/// the socket file deletion wouldn't correspond to the listener being closed, instead deleting the
/// socket file of the second listener. If the second listener also performs name reclamation, the
/// ensuing deletion will silently fail. Due to the awful design of Unix, this issue cannot be
/// mitigated.
///
/// [`create_sync()`]: super::options::ListenerOptions::create_sync
///
/// # Examples
///
/// ## Basic server
/// ```no_run
#[doc = doctest_file::include_doctest!("examples/local_socket/sync/listener.rs")]
/// ```
Listener);

impl r#trait::Listener for Listener {
	type Stream = Stream;

	#[inline]
	fn from_options(options: ListenerOptions<'_>) -> io::Result<Self> {
		dispatch::from_options(options)
	}
	#[inline]
	fn accept(&self) -> io::Result<Stream> {
		dispatch!(Self: x in self => x.accept()).map(Stream::from)
	}
	#[inline]
	fn set_nonblocking(&self, nonblocking: ListenerNonblockingMode) -> io::Result<()> {
		dispatch!(Self: x in self => x.set_nonblocking(nonblocking))
	}
	#[inline]
	fn do_not_reclaim_name_on_drop(&mut self) {
		dispatch!(Self: x in self => x.do_not_reclaim_name_on_drop())
	}
}
impl Iterator for Listener {
	type Item = io::Result<Stream>;
	#[inline(always)]
	fn next(&mut self) -> Option<Self::Item> {
		Some(r#trait::Listener::accept(self))
	}
}
impl FusedIterator for Listener {}