SD_EVENT_GET_FD(3)

πŸ’… SD_EVENT_GET_FD β€” okay bestie, let's get you that file descriptor

So this is the sd_event_get_fd page from the sd-event corner of the systemd manual universe, and honestly? It's a little bop. Let's go.


✨ NAME ✨

sd_event_get_fd β€” obtain a file descriptor to poll for event loop events πŸŽ€


πŸ¦‹ SYNOPSIS πŸ¦‹

#include <systemd/sd-event.h>

int sd_event_get_fd(sd_event *event);

🌸 DESCRIPTION 🌸

Okay so picture this: sd_event_get_fd() hands you back the file descriptor that an event loop object (the kind you got from sd_event_new(3)) uses to wait for events. That's it, that's the whole vibe. ✨

And the iconic part? You can poll that file descriptor itself for POLLIN/EPOLLIN events. Which means β€” drumroll β€” you can literally embed an sd-event(3) event loop inside another event loop, even a foreign one. Crossover episode fr fr. 🫢

Now, heads up: the file descriptor you get back refers to an epoll(7) object. And the recommendation is don't go altering it by invoking epoll_ctl(2) on it, because you'll mess with the event loop's inner logic and all of its little assumptions. Not the move. πŸ™ˆ


πŸ’– RETURN VALUE πŸ’–

On success, sd_event_get_fd() returns a non-negative file descriptor. Slay. On failure, it returns a negative errno-style error code. 😭

🌈 Errors

Returned errors may indicate the following problems:

  • -EINVAL β€” event is not a valid pointer to an sd_event structure. Like, not a real one. πŸ’€
  • -ECHILD β€” The event loop has been created in a different process, library or module instance. Wrong family, bestie.

πŸŽ€ EXAMPLES πŸŽ€

Example 1. Integration in the GLib event loop β€” the crossover nobody asked for but everybody needed πŸ”₯

/* SPDX-License-Identifier: MIT-0 */

#include <stdlib.h>
#include <glib.h>
#include <systemd/sd-event.h>

typedef struct SDEventSource {
  GSource source;
  GPollFD pollfd;
  sd_event *event;
} SDEventSource;

static gboolean event_prepare(GSource *source, gint *timeout_) {
  sd_event *event = ((SDEventSource *) source)->event;

  /* GLib does not guarantee that check() is always called between two
   * consecutive prepare() invocations. For example, if another thread
   * attaches/removes a source with poll fds to this GMainContext while
   * the main thread is inside poll(), g_main_context_check_unlocked()
   * returns immediately without calling any source's check().
   *
   * If check() was skipped, the sd_event is still in SD_EVENT_ARMED
   * state.  Return FALSE to let the normal poll() -> check() -> dispatch()
   * path complete the cycle. */
  if (sd_event_get_state(event) == SD_EVENT_ARMED)
    return FALSE;

  return sd_event_prepare(event) > 0;
}

static gboolean event_check(GSource *source) {
  return sd_event_wait(((SDEventSource *)source)->event, 0) > 0;
}

static gboolean event_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) {
  return sd_event_dispatch(((SDEventSource *)source)->event) > 0;
}

static void event_finalize(GSource *source) {
  sd_event_unref(((SDEventSource *)source)->event);
}

static GSourceFuncs event_funcs = {
  .prepare = event_prepare,
  .check = event_check,
  .dispatch = event_dispatch,
  .finalize = event_finalize,
};

GSource *g_sd_event_create_source(sd_event *event) {
  SDEventSource *source;

  source = (SDEventSource *)g_source_new(&event_funcs, sizeof(SDEventSource));

  source->event = sd_event_ref(event);
  source->pollfd.fd = sd_event_get_fd(event);
  source->pollfd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;

  g_source_add_poll((GSource *)source, &source->pollfd);

  return (GSource *)source;
}

🧚 NOTES 🧚

The functions described here are available as a shared library, which you can compile against and link to with the libsystemd pkg-config(1) file. Very demure, very linkable. πŸ‘‘

All functions listed here are thread-agnostic, and only a single thread may operate on a given object at any given time. Different threads may access the same object at different times. Multiple independent objects may be used from different threads in parallel. It's giving "sharing is caring but read the room." πŸ’…

⭐ Real talk though: the code described here uses getenv(3), which is declared to be not multi-thread-safe. Which means any code calling the functions described here must not call setenv(3) from a parallel thread. The recommendation is to only do your setenv() calls from an early phase of the program, when no other threads have been started yet. Do it before the group chat gets going. 🫢

πŸ•°οΈ HISTORY πŸ•°οΈ

sd_event_get_fd() was added in version 217. An icon, a legend, a moment in time. ✨


πŸ‘€ SEE ALSO πŸ‘€

sd-event(3), sd_event_new(3), sd_event_wait(3), epoll_ctl(2), epoll(7)


Okay that's the whole page, you ate it up, no crumbs. πŸ’– This one's from the systemd 261.3 era β€” go forth and poll responsibly, bestie. πŸ¦‹πŸŒΈ

πŸ“¦ where this page comes from