shell: Add ShellTimeChangeSource to track system clock changes
This is a `GSource` which is dispatched when the offset of the system real/wall clock changes with respect to the system monotonic clock. This can happen when the user explicitly changes the system clock, or when there’s an NTP sync after a prolonged period offline. The source can’t tell you *what* the offset change was, just that there was one. This will be used in an upcoming commit. Signed-off-by: Philip Withnall <pwithnall@gnome.org> Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3397>
This commit is contained in:
parent
3bc3a4da68
commit
1ceb803686
@ -122,6 +122,7 @@ libshell_public_headers = [
|
||||
'shell-screenshot.h',
|
||||
'shell-square-bin.h',
|
||||
'shell-stack.h',
|
||||
'shell-time-change-source.h',
|
||||
'shell-util.h',
|
||||
'shell-window-preview.h',
|
||||
'shell-window-preview-layout.h',
|
||||
@ -177,6 +178,7 @@ libshell_sources = [
|
||||
'shell-secure-text-buffer.h',
|
||||
'shell-square-bin.c',
|
||||
'shell-stack.c',
|
||||
'shell-time-change-source.c',
|
||||
'shell-util.c',
|
||||
'shell-window-preview.c',
|
||||
'shell-window-preview-layout.c',
|
||||
|
172
src/shell-time-change-source.c
Normal file
172
src/shell-time-change-source.c
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2024 GNOME Foundation, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Philip Withnall <pwithnall@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#include "shell-time-change-source.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSource source;
|
||||
|
||||
int fd; /* (owned) (nullable) */
|
||||
void *tag; /* (owned) (nullable) */
|
||||
} ShellTimeChangeSource;
|
||||
|
||||
static int
|
||||
arm_timerfd (int fd)
|
||||
{
|
||||
struct itimerspec its = {
|
||||
/* Get the biggest value we can in the `time_t`, as the timerfd will fire
|
||||
* spuriously when that time is reached. Unfortunately there is no
|
||||
* `TIME_T_MAX`. */
|
||||
.it_value.tv_sec = (sizeof (time_t) >= 8) ? UINT64_MAX : UINT32_MAX,
|
||||
};
|
||||
int flags = TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET;
|
||||
|
||||
if (timerfd_settime (fd, flags, &its, NULL) == 0)
|
||||
return 0;
|
||||
|
||||
if (errno != EINVAL)
|
||||
return -1;
|
||||
|
||||
/* Try again with a smaller timeout. It’s possible that libc supports
|
||||
* 64-bit time while the kernel doesn’t. */
|
||||
its.it_value.tv_sec = UINT32_MAX;
|
||||
return timerfd_settime (fd, flags, &its, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
shell_time_change_source_cleanup_fd (ShellTimeChangeSource *self)
|
||||
{
|
||||
/* Make sure the FD is closed. */
|
||||
if (self->tag != NULL)
|
||||
{
|
||||
g_source_remove_unix_fd ((GSource *) self, self->tag);
|
||||
self->tag = NULL;
|
||||
}
|
||||
|
||||
g_clear_fd (&self->fd, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
shell_time_change_source_dispatch (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
ShellTimeChangeSource *self = (ShellTimeChangeSource *) source;
|
||||
|
||||
if (callback == NULL)
|
||||
{
|
||||
g_warning ("ShellTimeChangeSource dispatched without callback. "
|
||||
"You must call g_source_set_callback().");
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
if (callback (user_data))
|
||||
{
|
||||
/* The timerfd_settime() call can’t really fail in this situation.
|
||||
* The man page says it can return ECANCELED, but will still be re-armed. */
|
||||
int retval = arm_timerfd (self->fd);
|
||||
int errsv = errno;
|
||||
g_assert (retval == 0 ||
|
||||
(retval < 0 && errsv == ECANCELED));
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
/* Clean up the source’s resources early, as FDs are precious, and the user
|
||||
* might leave the source hanging around for a long time before finalising it. */
|
||||
shell_time_change_source_cleanup_fd (self);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static void
|
||||
shell_time_change_source_finalize (GSource *source)
|
||||
{
|
||||
ShellTimeChangeSource *self = (ShellTimeChangeSource *) source;
|
||||
|
||||
shell_time_change_source_cleanup_fd (self);
|
||||
}
|
||||
|
||||
static const GSourceFuncs shell_time_change_source_funcs = {
|
||||
NULL, /* prepare */
|
||||
NULL, /* check */
|
||||
shell_time_change_source_dispatch,
|
||||
shell_time_change_source_finalize,
|
||||
NULL, NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* shell_time_change_source_new:
|
||||
* @error: return location for a #GError, or %NULL
|
||||
*
|
||||
* Creates a #GSource which is dispatched every time the system realtime clock
|
||||
* changes relative to the monotonic clock.
|
||||
*
|
||||
* This typically happens after NTP synchronisation.
|
||||
*
|
||||
* On error, a #GFileError will be returned. This happens if a timerfd cannot be
|
||||
* created.
|
||||
*
|
||||
* Any callback attached to the returned #GSource must have type
|
||||
* #GSourceFunc.
|
||||
*
|
||||
* Returns: (transfer full): the newly created #GSource, or %NULL on error
|
||||
*/
|
||||
GSource *
|
||||
shell_time_change_source_new (GError **error)
|
||||
{
|
||||
ShellTimeChangeSource *self;
|
||||
g_autoptr(GSource) source = NULL;
|
||||
g_autofd int fd = -1;
|
||||
|
||||
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
|
||||
|
||||
/* Create a timerfd with the maximum possible timeout, but set
|
||||
* `TFD_TIMER_CANCEL_ON_SET` so that it fires if the realtime clock changes
|
||||
* relative to the monotonic clock.
|
||||
*
|
||||
* This is a one-shot source: it’ll need to be recreated after that. */
|
||||
fd = timerfd_create (CLOCK_REALTIME, TFD_NONBLOCK | TFD_CLOEXEC);
|
||||
if (fd < 0 || arm_timerfd (fd) < 0)
|
||||
{
|
||||
int errsv = errno;
|
||||
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errsv),
|
||||
"Error creating timerfd: %s", g_strerror (errsv));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
source = g_source_new ((GSourceFuncs *) &shell_time_change_source_funcs,
|
||||
sizeof (ShellTimeChangeSource));
|
||||
self = (ShellTimeChangeSource *) source;
|
||||
|
||||
self->tag = g_source_add_unix_fd (source, fd, G_IO_IN);
|
||||
self->fd = g_steal_fd (&fd);
|
||||
|
||||
return g_steal_pointer (&source);
|
||||
}
|
33
src/shell-time-change-source.h
Normal file
33
src/shell-time-change-source.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2024 GNOME Foundation, Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General
|
||||
* Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Philip Withnall <pwithnall@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __SHELL_TIME_CHANGE_SOURCE_H__
|
||||
#define __SHELL_TIME_CHANGE_SOURCE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GSource *shell_time_change_source_new (GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __SHELL_TIME_CHANGE_SOURCE_H__ */
|
Loading…
x
Reference in New Issue
Block a user