libevent 0.2.0

Rust bindings to the libevent async I/O framework
Documentation
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
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
#![allow(dead_code)]

use bitflags::bitflags;
use std::io;
use std::os::raw::{c_int, c_short, c_void};
use std::ptr::NonNull;
use std::time::Duration;

use super::event::*;
use crate::EventCallbackWrapper;

/// A file descriptor in libevent.
pub type EvutilSocket = c_int;

/// The event callback function in libevent.
pub type EventCallbackFn = extern "C" fn(EvutilSocket, EventCallbackFlags, EventCallbackCtx);

/// The event callback's raw context type (void pointer).
pub type EventCallbackCtx = *mut c_void;

/// The event callback's raw flags type.
pub type EventCallbackFlags = c_short;

/// Convenience function for mapping Rust's `Duration` to libevent's `timeval`.
fn to_timeval(duration: Duration) -> libevent_sys::timeval {
    libevent_sys::timeval {
        tv_sec: duration.as_secs() as _,
        tv_usec: duration.subsec_micros() as _,
    }
}

/// Wrapper for libevent's `event_base` which is responsible for executing
/// associated events.
pub struct Base {
    base: NonNull<libevent_sys::event_base>,
}

/// The handle that abstracts over libevent's API in Rust.
impl Base {
    /// Creates a new instance of `Base`.
    pub fn new() -> Result<Self, io::Error> {
        let base = unsafe { libevent_sys::event_base_new() };

        if let Some(base) = NonNull::new(base) {
            Ok(unsafe { Self::from_raw(base) })
        } else {
            Err(io::Error::new(
                io::ErrorKind::Other,
                "Failed to create libevent base",
            ))
        }
    }

    /// Creates a new instance of `Base` using a raw, non-null `event_base`
    /// pointer.
    ///
    /// # Safety
    ///
    /// This function expects a non-null pointer, and thus does no such checks
    /// internally. Thus the caller is responsible for checking the
    /// `event_base` validity.
    pub unsafe fn from_raw(base: NonNull<libevent_sys::event_base>) -> Self {
        Base { base }
    }

    /// Exposes the raw, non-null `event_base` pointer.
    ///
    /// # Safety
    ///
    /// This function returns a valid, non-null `event_base` pointer which by
    /// itself is safe. However, this function serves as an escape hatch to do
    /// unsafe things.
    pub unsafe fn as_raw(&self) -> NonNull<libevent_sys::event_base> {
        self.base
    }

    /// Wrapper for libevent's `event_base_loop`, which runs the event loop in
    /// a manner defined by the `LoopFlags` input.
    pub fn loop_(&self, flags: LoopFlags) -> ExitReason {
        let exit_code = unsafe {
            libevent_sys::event_base_loop(self.base.as_ptr(), flags.bits() as i32) as i32
        };

        match exit_code {
            0 => {
                unsafe {
                    // Technically mutually-exclusive from `got_break`, but
                    // the check in `event_base_loop` comes first, so the logic
                    // here matches.
                    if libevent_sys::event_base_got_exit(self.base.as_ptr()) != 0i32 {
                        ExitReason::GotExit
                    } else if libevent_sys::event_base_got_break(self.base.as_ptr()) != 0i32 {
                        ExitReason::GotBreak
                    } else {
                        // TODO: This should match flags for `EVLOOP_ONCE`, `_NONBLOCK`, etc.
                        ExitReason::Unknown { flags, exit_code }
                    }
                }
            }
            -1 => ExitReason::Error,
            1 => ExitReason::NoPendingEvents,
            _ => ExitReason::Unknown { flags, exit_code },
        }
    }

    /// Wrapper for libevent's `event_base_loopexit`, which tells the running
    /// event loop to exit after a specified `Duration`.
    pub fn loopexit(&self, timeout: Duration) -> i32 {
        let tv = to_timeval(timeout);
        unsafe {
            let tv_cast = &tv as *const libevent_sys::timeval;
            libevent_sys::event_base_loopexit(self.as_raw().as_ptr(), tv_cast) as i32
        }
    }

    /// Wrapper for libevent's `event_base_loopbreak`, which tells the running
    /// event loop to break immediately.
    pub fn loopbreak(&self) -> i32 {
        unsafe { libevent_sys::event_base_loopbreak(self.as_raw().as_ptr()) as i32 }
    }

    /// Wrapper for libevent's `event_base_loopcontinue`, which tells the
    /// running event loop to resume searching for active events.
    pub fn loopcontinue(&self) -> i32 {
        unsafe { libevent_sys::event_base_loopcontinue(self.as_raw().as_ptr()) as i32 }
    }

    /// Wrapper for libevent's `event_new`, which allocates and initializes a
    /// new `event` with the given parameters.
    pub fn event_new(
        &mut self,
        fd: Option<EvutilSocket>,
        flags: EventFlags,
        callback: EventCallbackFn,
        callback_ctx: Option<EventCallbackCtx>,
    ) -> Option<NonNull<libevent_sys::event>> {
        let fd: EvutilSocket = if let Some(fd) = fd {
            // Actual fd
            fd
        } else {
            // Timer
            -1
        };

        let callback_ctx = if let Some(ctx) = callback_ctx {
            ctx
        } else {
            std::ptr::null::<c_void>() as *mut std::ffi::c_void
        };

        let inner = unsafe {
            libevent_sys::event_new(
                self.as_raw().as_ptr(),
                fd,
                flags.bits() as c_short,
                Some(callback),
                callback_ctx,
            )
        };

        NonNull::new(inner)
    }

    /// Wrapper for libevent's `event_new`, which initializes a pre-allocated
    /// `event` with the given parameters.
    pub fn event_assign(
        &mut self,
        ev: NonNull<libevent_sys::event>,
        fd: Option<EvutilSocket>,
        flags: EventFlags,
        callback: EventCallbackFn,
        callback_ctx: Option<EventCallbackCtx>,
    ) -> c_int {
        let fd: EvutilSocket = if let Some(fd) = fd {
            // Actual fd
            fd
        } else {
            // Timer
            -1
        };

        let callback_ctx = if let Some(ctx) = callback_ctx {
            ctx
        } else {
            std::ptr::null::<c_void>() as *mut std::ffi::c_void
        };

        unsafe {
            libevent_sys::event_assign(
                ev.as_ptr(),
                self.as_raw().as_ptr(),
                fd,
                flags.bits() as c_short,
                Some(callback),
                callback_ctx,
            )
        }
    }

    /// Wrapper for libevent's `event_add`, which activates an initialized
    /// `event` for a pre-defined `Base` and a given timeout interval.
    pub fn event_add(
        &self,
        event: NonNull<libevent_sys::event>,
        timeout: Option<Duration>,
    ) -> c_int {
        unsafe {
            let p = event.as_ptr();
            if let Some(tv) = timeout {
                libevent_sys::event_add(p, &to_timeval(tv))
            } else {
                // null timeout means no timeout to libevent
                libevent_sys::event_add(p, std::ptr::null())
            }
        }
    }
}

unsafe impl Send for Base {}

impl<S, T: Exec<S, F>, F> EventCallbackWrapper<S, T, F> {
    pub fn new(inner: F, event: Event<S>) -> Box<Self> {
        Box::new(Self {
            inner,
            event: Some(event),
            _phantom: std::marker::PhantomData::default(),
        })
    }
}

/// Handles freeing the callback wrapper memory.
///
/// It is set up in a fashion similar to `handle_wrapped_callback` so that
/// callers do not need to know the type parameters, only that they have a
/// function pointer to a monomorphized function that is valid for a given
/// event context.
pub(crate) unsafe extern "C" fn finalize_wrapped_callback<S, T, F>(
    event: *mut libevent_sys::event,
    ctx: EventCallbackCtx,
) {
    // Wrapper was allocated with Box, now free it with Drop.
    let cb: *mut EventCallbackWrapper<S, T, F> = ctx as *mut EventCallbackWrapper<S, T, F>;
    let owned_cb = Box::from_raw(cb);
    drop(owned_cb);

    // Now clear the event's ctx pointer field.
    let null_ctx = std::ptr::null::<c_void>() as *mut std::ffi::c_void;
    (*event).ev_evcallback.evcb_arg = null_ctx;
}

/// Acts as a C-compatible trampoline for the user-defined callback closure.
pub(crate) extern "C" fn handle_wrapped_callback<S, T, F>(
    fd: EvutilSocket,
    event: std::os::raw::c_short,
    ctx: EventCallbackCtx,
) where
    T: Exec<S, F>,
{
    let cb_ref = unsafe {
        let cb: *mut EventCallbackWrapper<S, T, F> = ctx as *mut EventCallbackWrapper<S, T, F>;
        let _cb_ref: &mut EventCallbackWrapper<S, T, F> = &mut *cb;
        _cb_ref
    };

    let flags = EventFlags::from_bits_truncate(event as u32);
    let ev = cb_ref.event.as_mut().expect("Missing event for callback");

    ev.set_in_callback(true);
    <T as Exec<S, F>>::exec(ev, fd, flags, &mut cb_ref.inner);
    ev.set_in_callback(false);

    // row, row, row your boat..
    if ev.stopped() {
        let event = cb_ref.event.take().expect("Missing event for drop");
        drop(event)
    }
}

impl Base {
    /// Helper for creating a raw event from Inactive.
    fn new_event_raw<S, T: Exec<S, F>, F>(
        &mut self,
        ev: &Event<Inactive<T>>,
    ) -> Option<NonNull<libevent_sys::event>> {
        self.event_new(
            ev.inactive_fd(),
            ev.inactive_flags(),
            handle_wrapped_callback::<S, T, F>,
            None,
        )
    }

    /// Helper for spawning with all necessary components.
    fn assign_event_raw<S, T: Exec<S, F>, F>(
        &mut self,
        ev: &Event<Inactive<T>>,
        raw_ev: NonNull<libevent_sys::event>,
        cb_wrapped: Box<EventCallbackWrapper<S, T, F>>,
    ) -> c_int {
        // Leak the callback wrapper so we can store it as ctx.
        let ctx_ptr = NonNull::from(Box::leak(cb_wrapped));

        self.event_assign(
            raw_ev,
            ev.inactive_fd(),
            ev.inactive_flags(),
            handle_wrapped_callback::<S, T, F>,
            Some(ctx_ptr.as_ptr() as EventCallbackCtx),
        )
    }

    /// Activates a given inactive `Event` with no handle sharing.
    ///
    /// Control of the event via the `Event` handle is relegated only from
    /// within the closure `F`, which means that no synchronization wrappers
    /// are required for operation.
    pub fn spawn<T: Exec<Internal<T>, F>, F>(
        &mut self,
        ev: Event<Inactive<T>>,
        cb: F,
    ) -> io::Result<()> {
        // First allocate the event with no context, then apply the reference
        // to the closure (and itself) later on.
        let raw_ev = self
            .new_event_raw::<Internal<T>, T, F>(&ev)
            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to allocate event"))?;

        let event: Event<Internal<T>> =
            EventInner::from_raw(raw_ev, Some(finalize_wrapped_callback::<Internal<T>, T, F>))
                .into();

        let cb_wrapped = EventCallbackWrapper::new(cb, event);

        // Now we can apply the closure + handle to self.
        if self.assign_event_raw(&ev, raw_ev, cb_wrapped) != 0 {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "Failed to assign event",
            ));
        }

        if self.event_add(raw_ev, ev.inactive_timeout()) != 0 {
            return Err(io::Error::new(io::ErrorKind::Other, "Failed to add event"));
        }

        Ok(())
    }

    /// Activates a given inactive `Event` with thread-local sharing.
    ///
    /// Control of the event via the `Event` handle is shared between the
    /// closure `F` as well as the returned `Event`, which internally uses an
    /// `Rc<RefCell>`.
    pub fn spawn_local<T: Exec<LocalWeak<T>, F>, F>(
        &mut self,
        ev: Event<Inactive<T>>,
        cb: F,
    ) -> io::Result<Event<Local<T>>> {
        // First allocate the event with no context, then apply the reference
        // to the closure (and itself) later on.
        let raw_ev = self
            .new_event_raw::<LocalWeak<T>, T, F>(&ev)
            .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to allocate event"))?;

        let event: Event<Local<T>> = EventInner::from_raw(
            raw_ev,
            Some(finalize_wrapped_callback::<LocalWeak<T>, T, F>),
        )
        .into();
        let closure_event = event.downgrade();

        let cb_wrapped = EventCallbackWrapper::new(cb, closure_event);

        // Now we can apply the closure + handle to self.
        if self.assign_event_raw(&ev, raw_ev, cb_wrapped) != 0 {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                "Failed to assign event",
            ));
        }

        if self.event_add(raw_ev, ev.inactive_timeout()) != 0 {
            return Err(io::Error::new(io::ErrorKind::Other, "Failed to add event"));
        }

        Ok(event)
    }
}

/// Enumerates all possible reasons that the event loop may have stopped
/// running.
pub enum ExitReason {
    GotExit,
    GotBreak,
    Error,
    NoPendingEvents,
    Unknown { flags: LoopFlags, exit_code: i32 },
}

bitflags! {
    /// Flags given to the event loop to alter its behavior.
    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
    pub struct LoopFlags: u32 {
        const ONCE = libevent_sys::EVLOOP_ONCE;
        const NONBLOCK = libevent_sys::EVLOOP_NONBLOCK;
        const NO_EXIT_ON_EMPTY = libevent_sys::EVLOOP_NO_EXIT_ON_EMPTY;
    }
}

bitflags! {
    /// Flags used both as inputs to define activation characteristics of an event,
    /// as well as an output given in the callback as to what triggered event
    /// activation.
    #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
    pub struct EventFlags: u32 {
        const TIMEOUT = libevent_sys::EV_TIMEOUT;
        const READ = libevent_sys::EV_READ;
        const WRITE = libevent_sys::EV_WRITE;
        const SIGNAL = libevent_sys::EV_SIGNAL;
        const PERSIST = libevent_sys::EV_PERSIST;
        const ET = libevent_sys::EV_ET;
        const FINALIZE = libevent_sys::EV_FINALIZE;
        const CLOSED = libevent_sys::EV_CLOSED;
    }
}