backports, local-alloc: Provide a backport of g_autofd

The functionality that was prototyped in libglnx as glnx_fd_close
and then glnx_autofd was later added to GLib as g_autofd.

glnx_close_fd() doesn't have a direct equivalent in GLib, so keep
it intact, but using the backported _glnx_clear_fd_ignore_error as
its implementation. g_clear_fd() is the closest thing in GLib, but
g_clear_fd() guarantees to set errno on failure (making it useful for
error-checking), whereas glnx_close_fd() guarantees to leave errno
untouched, making it more useful for cleanup code paths that recover
from a syscall or similar function that sets errno:

    int fd = ...;

    success = (fsync (fd) == 0);
    glnx_close_fd (&fd);
    return success;    /* if false, errno indicates why fsync failed */

(Of course in many cases, including this simple example, it would have
been easier to use g_autofd.)

glnx_autofd and glnx_fd_close are now equivalent to the backport of
g_autofd, so document them as deprecated. There's essentially no cost to
retaining them, so don't apply deprecation attributes.

Signed-off-by: Simon McVittie <smcv@collabora.com>
This commit is contained in:
Simon McVittie
2026-03-05 13:20:41 +00:00
parent aae4505722
commit 1c391d734c
2 changed files with 29 additions and 23 deletions

View File

@@ -82,6 +82,27 @@ g_clear_fd (int *fd_ptr,
}
#endif
/* This is part of the backport of g_autofd, but we define it
* unconditionally because it's also used to implement glnx_close_fd() */
static inline void
_glnx_clear_fd_ignore_error (int *fd_ptr)
{
/* Don't overwrite thread-local errno if closing the fd fails */
int errsv = errno;
if (!g_clear_fd (fd_ptr, NULL))
{
/* Do nothing: we ignore all errors, except for EBADF which
* is a programming error, checked for by g_close(). */
}
errno = errsv;
}
#if !GLIB_CHECK_VERSION(2, 76, 0)
#define g_autofd __attribute__((cleanup(_glnx_clear_fd_ignore_error)))
#endif
#if !GLIB_CHECK_VERSION(2, 40, 0)
#define g_info(...) g_log (G_LOG_DOMAIN, G_LOG_LEVEL_INFO, __VA_ARGS__)
#endif

View File

@@ -51,38 +51,23 @@ glnx_local_obj_unref (void *v)
* glnx_close_fd:
* @fdp: Pointer to fd
*
* Effectively `close (g_steal_fd (&fd))`. Also
* asserts that `close()` did not raise `EBADF` - encountering
* that error is usually a critical bug in the program.
* Same as `g_clear_fd()`, but ignoring the error (if any) and making sure
* not to alter `errno`. As a result, this function can be used for cleanup
* in contexts where `errno` needs to be preserved.
*/
static inline void
glnx_close_fd (int *fdp)
{
int errsv;
g_assert (fdp);
int fd = g_steal_fd (fdp);
if (fd >= 0)
{
errsv = errno;
if (close (fd) < 0)
g_assert (errno != EBADF);
errno = errsv;
}
}
#define glnx_close_fd _glnx_clear_fd_ignore_error
/**
* glnx_fd_close:
*
* Deprecated in favor of `glnx_autofd`.
* Deprecated in favor of `g_autofd`.
*/
#define glnx_fd_close __attribute__((cleanup(glnx_close_fd)))
#define glnx_fd_close g_autofd
/**
* glnx_autofd:
*
* Call close() on a variable location when it goes out of scope.
* Deprecated in favor of `g_autofd`.
*/
#define glnx_autofd __attribute__((cleanup(glnx_close_fd)))
#define glnx_autofd g_autofd
G_END_DECLS