atap 0.1.0

Threadsafe futureless async runtime for macOS
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
//! # Unix socket
//! Unix connections, listeners and datagram sockets, and the
//! tasks each of them starts

use crate::{
    RuntimeError,
    futures::{
        net::stream::{FinishTask, Pipe, RecvTask, SendTask, Source},
        unix::{
            path::Bound,
            unix_task::{UnixAcceptTask, UnixRecvFromTask, UnixSendToTask},
        },
    },
    modules::{fd::Fd, int_check::IntCheck},
};

/// Who is at the other end of a Unix connection
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Credentials {
    uid: u32,
    gid: u32,
}

impl Credentials {
    /// The user id
    pub fn uid(&self) -> u32 {
        self.uid
    }

    /// The group id
    pub fn gid(&self) -> u32 {
        self.gid
    }
}
use std::{
    fmt,
    path::{Path, PathBuf},
    sync::Arc,
};

/// What every copy of one connection shares
struct UnixStream {
    /// The socket, and what the last receive read past its end
    pipe: Pipe,

    /// The path it was made to, or accepted on
    path: PathBuf,
}

/// An open Unix connection
///
/// ## Behaviour
/// The same as a TCP [`Connection`], between two programs on this
/// machine. It comes back from [`Unix::connect`] or
/// [`UnixListener::accept`], and its methods build the same send
/// and receive tasks
///
/// ```no_run
/// # use atap::{Runtime, unix::Unix};
/// # fn main() -> Result<(), atap::RuntimeError> {
/// let conn = Runtime::block(Unix::connect("/tmp/app.sock"))?;
///
/// Runtime::block(conn.send(b"status\n".as_slice()))?;
/// let line = Runtime::block(conn.recv_until(b"\n", 1024))?;
/// # Ok(())
/// # }
/// ```
///
/// ## Closing
/// The socket closes when the **last** reference to it goes:
/// every clone, every task using it, and every task handle whose
/// output holds one
///
/// #### Note
/// Two receives on one connection at once each get part of what
/// arrives, in no useful order. Run them one after the other
///
/// [`Connection`]: crate::tcp::Connection
/// [`Unix::connect`]: crate::unix::Unix::connect
#[derive(Clone)]
pub struct UnixConnection {
    stream: Arc<UnixStream>,
}

impl UnixConnection {
    /// Takes over a connected socket
    pub(crate) fn new(fd: Fd, path: PathBuf) -> Self {
        Self {
            stream: Arc::new(UnixStream {
                pipe: Pipe::new(fd),
                path,
            }),
        }
    }

    /// The stream the send and receive tasks work on
    #[inline(always)]
    pub(crate) fn pipe(&self) -> &Pipe {
        &self.stream.pipe
    }

    /// This connection, as something a stream task can hold
    #[inline(always)]
    fn source(&self) -> Source {
        Source::Unix(self.clone())
    }

    /// Sends every byte of `data`
    ///
    /// ## Behaviour
    /// Waits whenever the connection can't take any more, without
    /// holding a thread
    ///
    /// ## Returns
    /// The number of bytes sent, which is always all of them
    /// whenever this isn't an error
    ///
    /// #### Note
    /// A send that times out or is cancelled may already have sent
    /// part of `data`, and that part can't be taken back
    pub fn send(&self, data: impl Into<Arc<[u8]>>) -> SendTask {
        SendTask::new(self.source(), data.into())
    }

    /// Receives whatever has arrived, up to `max` bytes
    ///
    /// ## Returns
    /// Between one and `max` bytes. **An empty `Vec` means the
    /// other side closed the connection**
    pub fn recv(&self, max: usize) -> RecvTask {
        RecvTask::some(self.source(), max)
    }

    /// Receives exactly `len` bytes
    ///
    /// ## Returns
    /// All `len` of them, or [`RuntimeError::Closed`] if the other
    /// side closed first. Whatever had arrived by then is put back
    ///
    /// [`RuntimeError::Closed`]: crate::RuntimeError::Closed
    pub fn recv_exact(&self, len: usize) -> RecvTask {
        RecvTask::exact(self.source(), len)
    }

    /// Receives up to and including `delimiter`
    ///
    /// ## Returns
    /// Everything up to and including the delimiter.
    /// [`RuntimeError::TooLong`] if `max` bytes go by without it,
    /// and [`RuntimeError::Closed`] if the connection ends first.
    /// Either way what was read is put back
    ///
    /// [`RuntimeError::TooLong`]: crate::RuntimeError::TooLong
    /// [`RuntimeError::Closed`]: crate::RuntimeError::Closed
    pub fn recv_until(&self, delimiter: &[u8], max: usize) -> RecvTask {
        RecvTask::until(self.source(), Arc::from(delimiter), max)
    }

    /// Receives everything until the other side closes the
    /// connection
    pub fn recv_to_end(&self) -> RecvTask {
        RecvTask::to_end(self.source())
    }

    /// The path this connection was made to, or accepted on
    #[inline(always)]
    pub fn path(&self) -> &Path {
        &self.stream.path
    }

    /// Who is at the other end
    ///
    /// ## Returns
    /// The user and group of the process that made the other end
    pub fn peer_credentials(&self) -> Result<Credentials, RuntimeError> {
        let (mut uid, mut gid) = (0, 0);

        unsafe { libc::getpeereid(self.pipe().fd(), &mut uid, &mut gid) }.check()?;

        Ok(Credentials { uid, gid })
    }

    /// Tells the other side this one will send no more
    ///
    /// ## Behaviour
    /// Ends the sending half of the connection. This side can
    /// still receive, and the other side's reads end once it has
    /// everything
    pub fn finish(&self) -> FinishTask {
        FinishTask::new(self.source())
    }

    /// Lets go of this handle on the connection
    ///
    /// ## Behaviour
    /// The socket closes once nothing else holds it. A task still
    /// using it runs to its end, and an output already received
    /// stays readable
    pub fn close(self) {
        drop(self);
    }
}

impl fmt::Debug for UnixConnection {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("UnixConnection")
            .field("path", &self.stream.path)
            .finish()
    }
}

/// What every copy of one listener shares
///
/// The socket is closed before its file is removed, by field
/// order
struct UnixListening {
    /// The socket
    fd: Fd,

    /// The file the bind made
    bound: Bound,
}

/// A Unix socket waiting for connections
///
/// ## Behaviour
/// Comes back from [`Unix::listen`]. Each
/// [`UnixListener::accept`] takes the next connection. Cloning it
/// is cheap and every clone is the same socket
///
/// ## Closing
/// The socket closes once the last reference to it goes, and
/// **its socket file is removed** then too. A file somebody else
/// has since put at the same path is left alone
///
/// [`Unix::listen`]: crate::unix::Unix::listen
#[derive(Clone)]
pub struct UnixListener {
    socket: Arc<UnixListening>,
}

impl UnixListener {
    /// Takes over a bound, listening socket
    pub(crate) fn new(fd: Fd, bound: Bound) -> Self {
        Self {
            socket: Arc::new(UnixListening { fd, bound }),
        }
    }

    /// The socket, for handing to a syscall
    #[inline(always)]
    pub(crate) fn fd(&self) -> libc::c_int {
        self.socket.fd.raw()
    }

    /// Takes the next connection
    ///
    /// ## Behaviour
    /// Waits for one to arrive without holding a thread
    ///
    /// ## Returns
    /// The connection
    pub fn accept(&self) -> UnixAcceptTask {
        UnixAcceptTask::new(self.clone())
    }

    /// The path it is listening on
    #[inline(always)]
    pub fn path(&self) -> &Path {
        self.socket.bound.path()
    }

    /// Lets go of this handle on the listener
    ///
    /// ## Behaviour
    /// The socket closes, and its file is removed, once nothing
    /// else holds it
    pub fn close(self) {
        drop(self);
    }
}

impl fmt::Debug for UnixListener {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("UnixListener")
            .field("path", &self.path())
            .finish()
    }
}

/// What every copy of one datagram socket shares
///
/// The socket is closed before its file is removed, by field
/// order
struct UnixDatagrams {
    /// The socket
    fd: Fd,

    /// The file the bind made
    bound: Bound,
}

/// A Unix datagram socket, bound to a path
///
/// ## Behaviour
/// Comes back from [`Unix::bind`]. Like a [`UdpSocket`], between
/// programs on this machine: each send names the path it goes
/// to, and each receive says which path it came from
///
/// ```no_run
/// # use atap::{Runtime, unix::Unix};
/// # fn main() -> Result<(), atap::RuntimeError> {
/// let socket = Runtime::block(Unix::bind("/tmp/me.sock"))?;
///
/// Runtime::block(socket.send_to("/tmp/them.sock", b"ping".as_slice()))?;
/// let (reply, from) = Runtime::block(socket.recv_from())?;
/// # Ok(())
/// # }
/// ```
///
/// ## Datagrams
/// Every send is one datagram and every receive takes one, whole.
/// Unlike UDP, nothing is lost or reordered on the way
///
/// ## Closing
/// The socket closes once the last reference to it goes, and
/// **its socket file is removed** then too
///
/// #### Note
/// macOS caps a Unix datagram at 2048 bytes by default, set by
/// `net.local.dgram.maxdgram`. A larger one is refused as
/// `CheckError(Some(EMSGSIZE))`
///
/// [`Unix::bind`]: crate::unix::Unix::bind
/// [`UdpSocket`]: crate::udp::UdpSocket
#[derive(Clone)]
pub struct UnixDatagram {
    socket: Arc<UnixDatagrams>,
}

impl UnixDatagram {
    /// Takes over a bound socket
    pub(crate) fn new(fd: Fd, bound: Bound) -> Self {
        Self {
            socket: Arc::new(UnixDatagrams { fd, bound }),
        }
    }

    /// The socket, for handing to a syscall
    #[inline(always)]
    pub(crate) fn fd(&self) -> libc::c_int {
        self.socket.fd.raw()
    }

    /// Sends `data` to the socket bound at `path`, as one datagram
    ///
    /// ## Returns
    /// The number of bytes sent, which is all of them. Nobody bound
    /// at the path is `CheckError(Some(ENOENT))`, or
    /// `ECONNREFUSED` for a file left behind with nobody on it
    ///
    /// #### Note
    /// A receiver with no room left is an error rather than a wait
    pub fn send_to(&self, path: impl AsRef<Path>, data: impl Into<Arc<[u8]>>) -> UnixSendToTask {
        UnixSendToTask::new(self.clone(), path.as_ref().to_path_buf(), data.into())
    }

    /// Receives the next datagram
    ///
    /// ## Behaviour
    /// Waits for one to arrive without holding a thread
    ///
    /// ## Returns
    /// The whole datagram, and the path of the socket that sent
    /// it. `None` for a sender that isn't bound to one
    pub fn recv_from(&self) -> UnixRecvFromTask {
        UnixRecvFromTask::new(self.clone())
    }

    /// The path it is bound to
    #[inline(always)]
    pub fn path(&self) -> &Path {
        self.socket.bound.path()
    }

    /// Lets go of this handle on the socket
    ///
    /// ## Behaviour
    /// The socket closes, and its file is removed, once nothing
    /// else holds it
    pub fn close(self) {
        drop(self);
    }
}

impl fmt::Debug for UnixDatagram {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("UnixDatagram")
            .field("path", &self.path())
            .finish()
    }
}