diff --git a/gio/giomodule.c b/gio/giomodule.c index 7491fd433..38761e4fd 100644 --- a/gio/giomodule.c +++ b/gio/giomodule.c @@ -55,6 +55,9 @@ #ifdef __linux__ #include "gmemorymonitorpsi.h" #endif +#ifdef HAVE_SYSINFO +#include "gmemorymonitorpoll.h" +#endif #include "gpowerprofilemonitor.h" #include "gpowerprofilemonitordbus.h" #include "gpowerprofilemonitorportal.h" @@ -1087,6 +1090,9 @@ extern GType g_memory_monitor_dbus_get_type (void); #ifdef __linux__ extern GType g_memory_monitor_psi_get_type (void); #endif +#ifdef HAVE_SYSINFO +extern GType g_memory_monitor_poll_get_type (void); +#endif extern GType g_memory_monitor_portal_get_type (void); extern GType g_memory_monitor_win32_get_type (void); extern GType g_power_profile_monitor_dbus_get_type (void); @@ -1369,6 +1375,9 @@ _g_io_modules_ensure_loaded (void) g_type_ensure (g_memory_monitor_dbus_get_type ()); #ifdef __linux__ g_type_ensure (g_memory_monitor_psi_get_type ()); +#endif +#ifdef HAVE_SYSINFO + g_type_ensure (g_memory_monitor_poll_get_type ()); #endif g_type_ensure (g_memory_monitor_portal_get_type ()); g_type_ensure (g_network_monitor_portal_get_type ()); diff --git a/gio/gmemorymonitorpoll.c b/gio/gmemorymonitorpoll.c new file mode 100644 index 000000000..d5d4e75f5 --- /dev/null +++ b/gio/gmemorymonitorpoll.c @@ -0,0 +1,273 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2025 Red Hat, 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 . + */ +#include "config.h" + +#include "gcancellable.h" +#include "gdbusnamewatching.h" +#include "gdbusproxy.h" +#include "ginitable.h" +#include "gioerror.h" +#include "giomodule-priv.h" +#include "glib/gstdio.h" +#include "glib/glib-private.h" +#include "glibintl.h" +#include "gmemorymonitor.h" +#include "gmemorymonitorpoll.h" + +#include +#include + +/** + * GMemoryMonitorPoll: + * + * A [iface@Gio.MemoryMonitor] which polls the system free/used + * memory ratio on a fixed timer. + * + * It polls, for example, every 10 seconds, and emits different + * [signal@Gio.MemoryMonitor::low-memory-warning] signals if it falls below several + * ‘low’ thresholds. + * + * The system free/used memory ratio is queried using [`sysinfo()`](man:sysinfo(2)). + * + * This is intended as a fallback implementation of [iface@Gio.MemoryMonitor] in case + * other, more performant, implementations are not supported on the system. + * + * Since: 2.86 + */ + +typedef enum { + PROP_MEM_FREE_RATIO = 1, + PROP_POLL_INTERVAL_MS, +} GMemoryMonitorPollProperty; + +#define G_MEMORY_MONITOR_POLL_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable)) + +static void g_memory_monitor_poll_iface_init (GMemoryMonitorInterface *iface); +static void g_memory_monitor_poll_initable_iface_init (GInitableIface *iface); + +struct _GMemoryMonitorPoll +{ + GMemoryMonitorBase parent_instance; + + GMainContext *worker; /* (unowned) */ + GSource *source_timeout; /* (owned) */ + + /* it overrides the default timeout when running the test */ + unsigned int poll_interval_ms; /* zero to use the default */ + gdouble mem_free_ratio; +}; + +/* the default monitor timeout */ +#define G_MEMORY_MONITOR_PSI_DEFAULT_SEC 10 + +G_DEFINE_TYPE_WITH_CODE (GMemoryMonitorPoll, g_memory_monitor_poll, G_TYPE_MEMORY_MONITOR_BASE, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, + g_memory_monitor_poll_initable_iface_init) + G_IMPLEMENT_INTERFACE (G_TYPE_MEMORY_MONITOR, + g_memory_monitor_poll_iface_init) + _g_io_modules_ensure_extension_points_registered (); + g_io_extension_point_implement (G_MEMORY_MONITOR_EXTENSION_POINT_NAME, + g_define_type_id, + "poll", + 10)) + +static void +g_memory_monitor_poll_init (GMemoryMonitorPoll *mem_poll) +{ + mem_poll->mem_free_ratio = -1.0; +} + +static void +g_memory_monitor_poll_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object); + + switch ((GMemoryMonitorPollProperty) prop_id) + { + case PROP_MEM_FREE_RATIO: + monitor->mem_free_ratio = g_value_get_double (value); + break; + case PROP_POLL_INTERVAL_MS: + monitor->poll_interval_ms = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +g_memory_monitor_poll_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object); + + switch ((GMemoryMonitorPollProperty) prop_id) + { + case PROP_MEM_FREE_RATIO: + g_value_set_double (value, monitor->mem_free_ratio); + break; + case PROP_POLL_INTERVAL_MS: + g_value_set_uint (value, monitor->poll_interval_ms); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +g_memory_monitor_mem_ratio_cb (gpointer data) +{ + GMemoryMonitorPoll *monitor = (GMemoryMonitorPoll *) data; + gdouble mem_ratio; + GMemoryMonitorLowMemoryLevel warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID; + + /* Should be executed in the worker context */ + g_assert (g_main_context_is_owner (monitor->worker)); + + mem_ratio = g_memory_monitor_base_query_mem_ratio (); + + if (mem_ratio < 0.0) + return G_SOURCE_REMOVE; + + if (mem_ratio > 0.5) + return G_SOURCE_CONTINUE; + + /* free ratio override */ + if (monitor->mem_free_ratio >= 0.0) + mem_ratio = monitor->mem_free_ratio; + + g_debug ("memory free ratio %f", mem_ratio); + + if (mem_ratio < 0.2) + warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_CRITICAL; + else if (mem_ratio < 0.3) + warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_MEDIUM; + else if (mem_ratio < 0.4) + warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_LOW; + + if (warning_level != G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID) + g_memory_monitor_base_send_event_to_user (G_MEMORY_MONITOR_BASE (monitor), warning_level); + + return G_SOURCE_CONTINUE; +} + +static gboolean +g_memory_monitor_poll_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (initable); + GSource *source; + + if (monitor->poll_interval_ms > 0) + { + if (monitor->poll_interval_ms < G_TIME_SPAN_MILLISECOND) + source = g_timeout_source_new (monitor->poll_interval_ms); + else + source = g_timeout_source_new_seconds (monitor->poll_interval_ms / G_TIME_SPAN_MILLISECOND); + } + else + { + /* default 10 second */ + source = g_timeout_source_new_seconds (G_MEMORY_MONITOR_PSI_DEFAULT_SEC); + } + + g_source_set_callback (source, g_memory_monitor_mem_ratio_cb, monitor, NULL); + monitor->worker = GLIB_PRIVATE_CALL (g_get_worker_context) (); + g_source_attach (source, monitor->worker); + monitor->source_timeout = g_steal_pointer (&source); + + return TRUE; +} + +static void +g_memory_monitor_poll_finalize (GObject *object) +{ + GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object); + + g_source_destroy (monitor->source_timeout); + g_source_unref (monitor->source_timeout); + + G_OBJECT_CLASS (g_memory_monitor_poll_parent_class)->finalize (object); +} + +static void +g_memory_monitor_poll_class_init (GMemoryMonitorPollClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->set_property = g_memory_monitor_poll_set_property; + object_class->get_property = g_memory_monitor_poll_get_property; + object_class->finalize = g_memory_monitor_poll_finalize; + + /** + * GMemoryMonitorPoll:mem-free-ratio: + * + * Override the memory free ratio + * + * Since: 2.86 + */ + g_object_class_install_property (object_class, + PROP_MEM_FREE_RATIO, + g_param_spec_double ("mem-free-ratio", NULL, NULL, + -1.0, 1.0, + -1.0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * GMemoryMonitorPoll:poll-interval-ms: + * + * Override the poll interval for monitoring the memory usage. + * + * The interval is in milliseconds. Zero means to use the default interval. + * + * Since: 2.86 + */ + g_object_class_install_property (object_class, + PROP_POLL_INTERVAL_MS, + g_param_spec_uint ("poll-interval-ms", NULL, NULL, + 0, G_MAXUINT, + 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + +} + +static void +g_memory_monitor_poll_iface_init (GMemoryMonitorInterface *monitor_iface) +{ +} + +static void +g_memory_monitor_poll_initable_iface_init (GInitableIface *iface) +{ + iface->init = g_memory_monitor_poll_initable_init; +} + diff --git a/gio/gmemorymonitorpoll.h b/gio/gmemorymonitorpoll.h new file mode 100644 index 000000000..6d086aa6d --- /dev/null +++ b/gio/gmemorymonitorpoll.h @@ -0,0 +1,38 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright 2025 Red Hat, 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 . + */ + +#ifndef __G_MEMORY_MONITOR_POLL_H__ +#define __G_MEMORY_MONITOR_POLL_H__ + +#include "gmemorymonitorbase.h" + +#include + +G_BEGIN_DECLS + +#define G_TYPE_MEMORY_MONITOR_POLL (g_memory_monitor_poll_get_type ()) +G_DECLARE_FINAL_TYPE (GMemoryMonitorPoll, g_memory_monitor_poll, G, MEMORY_MONITOR_POLL, GMemoryMonitorBase) + +GIO_AVAILABLE_IN_2_86 +GType g_memory_monitor_poll_get_type (void); + +G_END_DECLS + +#endif /* __G_MEMORY_MONITOR_PSI_H__ */ diff --git a/gio/meson.build b/gio/meson.build index fe442e393..20a22aa0b 100644 --- a/gio/meson.build +++ b/gio/meson.build @@ -391,6 +391,12 @@ if host_system != 'windows' 'gunixoutputstream.h', ) + if glib_conf.has('HAVE_SYSINFO') + unix_sources += files( + 'gmemorymonitorpoll.c', + ) + endif + if glib_have_cocoa settings_sources += files('gnextstepsettingsbackend.m') contenttype_sources += files('gcontenttype-osx.m')