LCOV - code coverage report
Current view: top level - gio - gmemorymonitorpoll.c (source / functions) Coverage Total Hit
Test: unnamed Lines: 77.6 % 98 76
Test Date: 2026-02-10 05:15:13 Functions: 92.9 % 14 13
Branches: - 0 0

             Branch data     Line data    Source code
       1                 :             : /* GIO - GLib Input, Output and Streaming Library
       2                 :             :  *
       3                 :             :  * Copyright 2025 Red Hat, Inc.
       4                 :             :  *
       5                 :             :  * SPDX-License-Identifier: LGPL-2.1-or-later
       6                 :             :  *
       7                 :             :  * This library is free software; you can redistribute it and/or
       8                 :             :  * modify it under the terms of the GNU Lesser General Public
       9                 :             :  * License as published by the Free Software Foundation; either
      10                 :             :  * version 2.1 of the License, or (at your option) any later version.
      11                 :             :  *
      12                 :             :  * This library is distributed in the hope that it will be useful,
      13                 :             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :             :  * Lesser General Public License for more details.
      16                 :             :  *
      17                 :             :  * You should have received a copy of the GNU Lesser General
      18                 :             :  * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
      19                 :             :  */
      20                 :             : #include "config.h"
      21                 :             : 
      22                 :             : #include "gcancellable.h"
      23                 :             : #include "gdbusnamewatching.h"
      24                 :             : #include "gdbusproxy.h"
      25                 :             : #include "ginitable.h"
      26                 :             : #include "gioerror.h"
      27                 :             : #include "giomodule-priv.h"
      28                 :             : #include "glib/gstdio.h"
      29                 :             : #include "glib/glib-private.h"
      30                 :             : #include "glibintl.h"
      31                 :             : #include "gmemorymonitor.h"
      32                 :             : #include "gmemorymonitorpoll.h"
      33                 :             : 
      34                 :             : #include <fcntl.h>
      35                 :             : #include <unistd.h>
      36                 :             : 
      37                 :             : /**
      38                 :             :  * GMemoryMonitorPoll:
      39                 :             :  *
      40                 :             :  * A [iface@Gio.MemoryMonitor] which polls the system free/used
      41                 :             :  * memory ratio on a fixed timer.
      42                 :             :  *
      43                 :             :  * It polls, for example, every 10 seconds, and emits different
      44                 :             :  * [signal@Gio.MemoryMonitor::low-memory-warning] signals if it falls below several
      45                 :             :  * ‘low’ thresholds.
      46                 :             :  *
      47                 :             :  * The system free/used memory ratio is queried using [`sysinfo()`](man:sysinfo(2)).
      48                 :             :  *
      49                 :             :  * This is intended as a fallback implementation of [iface@Gio.MemoryMonitor] in case
      50                 :             :  * other, more performant, implementations are not supported on the system.
      51                 :             :  *
      52                 :             :  * Since: 2.86
      53                 :             :  */
      54                 :             : 
      55                 :             : typedef enum {
      56                 :             :   PROP_MEM_FREE_RATIO = 1,
      57                 :             :   PROP_POLL_INTERVAL_MS,
      58                 :             : } GMemoryMonitorPollProperty;
      59                 :             : 
      60                 :             : #define G_MEMORY_MONITOR_POLL_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
      61                 :             : 
      62                 :             : static void g_memory_monitor_poll_iface_init (GMemoryMonitorInterface *iface);
      63                 :             : static void g_memory_monitor_poll_initable_iface_init (GInitableIface *iface);
      64                 :             : 
      65                 :             : struct _GMemoryMonitorPoll
      66                 :             : {
      67                 :             :   GMemoryMonitorBase parent_instance;
      68                 :             : 
      69                 :             :   GMainContext *worker;  /* (unowned) */
      70                 :             :   GSource *source_timeout;  /* (owned) */
      71                 :             : 
      72                 :             :   /* it overrides the default timeout when running the test */
      73                 :             :   unsigned int poll_interval_ms;  /* zero to use the default */
      74                 :             :   gdouble mem_free_ratio;
      75                 :             : };
      76                 :             : 
      77                 :             : /* the default monitor timeout */
      78                 :             : #define G_MEMORY_MONITOR_PSI_DEFAULT_SEC 10
      79                 :             : 
      80                 :         297 : G_DEFINE_TYPE_WITH_CODE (GMemoryMonitorPoll, g_memory_monitor_poll, G_TYPE_MEMORY_MONITOR_BASE,
      81                 :             :                          G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
      82                 :             :                                                 g_memory_monitor_poll_initable_iface_init)
      83                 :             :                          G_IMPLEMENT_INTERFACE (G_TYPE_MEMORY_MONITOR,
      84                 :             :                                                 g_memory_monitor_poll_iface_init)
      85                 :             :                          _g_io_modules_ensure_extension_points_registered ();
      86                 :             :                          g_io_extension_point_implement (G_MEMORY_MONITOR_EXTENSION_POINT_NAME,
      87                 :             :                                                          g_define_type_id,
      88                 :             :                                                          "poll",
      89                 :             :                                                          10))
      90                 :             : 
      91                 :             : static void
      92                 :           4 : g_memory_monitor_poll_init (GMemoryMonitorPoll *mem_poll)
      93                 :             : {
      94                 :           4 :   mem_poll->mem_free_ratio = -1.0;
      95                 :           4 : }
      96                 :             : 
      97                 :             : static void
      98                 :           8 : g_memory_monitor_poll_set_property (GObject      *object,
      99                 :             :                                     guint         prop_id,
     100                 :             :                                     const GValue *value,
     101                 :             :                                     GParamSpec   *pspec)
     102                 :             : {
     103                 :           8 :   GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object);
     104                 :             : 
     105                 :           8 :   switch ((GMemoryMonitorPollProperty) prop_id)
     106                 :             :     {
     107                 :           4 :     case PROP_MEM_FREE_RATIO:
     108                 :           4 :       monitor->mem_free_ratio = g_value_get_double (value);
     109                 :           4 :       break;
     110                 :           4 :     case PROP_POLL_INTERVAL_MS:
     111                 :           4 :       monitor->poll_interval_ms = g_value_get_uint (value);
     112                 :           4 :       break;
     113                 :           0 :     default:
     114                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     115                 :           0 :       break;
     116                 :             :     }
     117                 :           8 : }
     118                 :             : 
     119                 :             : static void
     120                 :           0 : g_memory_monitor_poll_get_property (GObject    *object,
     121                 :             :                                     guint       prop_id,
     122                 :             :                                     GValue     *value,
     123                 :             :                                     GParamSpec *pspec)
     124                 :             : {
     125                 :           0 :   GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object);
     126                 :             : 
     127                 :           0 :   switch ((GMemoryMonitorPollProperty) prop_id)
     128                 :             :     {
     129                 :           0 :     case PROP_MEM_FREE_RATIO:
     130                 :           0 :       g_value_set_double (value, monitor->mem_free_ratio);
     131                 :           0 :       break;
     132                 :           0 :     case PROP_POLL_INTERVAL_MS:
     133                 :           0 :       g_value_set_uint (value, monitor->poll_interval_ms);
     134                 :           0 :       break;
     135                 :           0 :     default:
     136                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     137                 :           0 :       break;
     138                 :             :     }
     139                 :           0 : }
     140                 :             : 
     141                 :             : typedef struct
     142                 :             : {
     143                 :             :   GWeakRef monitor_weak;  /* (element-type GMemoryMonitorPoll) */
     144                 :             : } CallbackData;
     145                 :             : 
     146                 :             : static CallbackData *
     147                 :           4 : callback_data_new (GMemoryMonitorPoll *monitor)
     148                 :             : {
     149                 :           4 :   CallbackData *data = g_new0 (CallbackData, 1);
     150                 :           4 :   g_weak_ref_init (&data->monitor_weak, monitor);
     151                 :           4 :   return g_steal_pointer (&data);
     152                 :             : }
     153                 :             : 
     154                 :             : static void
     155                 :           3 : callback_data_free (CallbackData *data)
     156                 :             : {
     157                 :           3 :   g_weak_ref_clear (&data->monitor_weak);
     158                 :           3 :   g_free (data);
     159                 :           3 : }
     160                 :             : 
     161                 :             : static gboolean
     162                 :           3 : g_memory_monitor_mem_ratio_cb (gpointer user_data)
     163                 :             : {
     164                 :           3 :   CallbackData *data = user_data;
     165                 :           3 :   GMemoryMonitorPoll *monitor = NULL;
     166                 :             :   gdouble mem_ratio;
     167                 :           3 :   GMemoryMonitorLowMemoryLevel warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID;
     168                 :             : 
     169                 :             :   /* It’s possible for the dispatch of this callback to race with finalising
     170                 :             :    * the `GMemoryMonitorPoll`, hence the use of a thread-safe weak ref. */
     171                 :           3 :   monitor = g_weak_ref_get (&data->monitor_weak);
     172                 :           3 :   if (monitor == NULL)
     173                 :           0 :     return G_SOURCE_REMOVE;
     174                 :             : 
     175                 :             :   /* Should be executed in the worker context */
     176                 :           3 :   g_assert (g_main_context_is_owner (monitor->worker));
     177                 :             : 
     178                 :           3 :   mem_ratio = g_memory_monitor_base_query_mem_ratio ();
     179                 :             : 
     180                 :             :   /* free ratio override */
     181                 :           3 :   if (monitor->mem_free_ratio >= 0.0)
     182                 :           3 :     mem_ratio = monitor->mem_free_ratio;
     183                 :             : 
     184                 :           3 :   if (mem_ratio < 0.0)
     185                 :             :     {
     186                 :           0 :       g_clear_object (&monitor);
     187                 :           0 :       return G_SOURCE_REMOVE;
     188                 :             :     }
     189                 :             : 
     190                 :           3 :   if (mem_ratio > 0.5)
     191                 :             :     {
     192                 :           0 :       g_clear_object (&monitor);
     193                 :           0 :       return G_SOURCE_CONTINUE;
     194                 :             :     }
     195                 :             : 
     196                 :           3 :   g_debug ("memory free ratio %f", mem_ratio);
     197                 :             : 
     198                 :           3 :   if (mem_ratio < 0.2)
     199                 :           1 :     warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_CRITICAL;
     200                 :           2 :   else if (mem_ratio < 0.3)
     201                 :           1 :     warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_MEDIUM;
     202                 :           1 :   else if (mem_ratio < 0.4)
     203                 :           1 :     warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_LOW;
     204                 :             : 
     205                 :           3 :   if (warning_level != G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID)
     206                 :           3 :     g_memory_monitor_base_send_event_to_user (G_MEMORY_MONITOR_BASE (monitor), warning_level);
     207                 :             : 
     208                 :           3 :   g_clear_object (&monitor);
     209                 :             : 
     210                 :           3 :   return G_SOURCE_CONTINUE;
     211                 :             : }
     212                 :             : 
     213                 :             : static gboolean
     214                 :           4 : g_memory_monitor_poll_initable_init (GInitable     *initable,
     215                 :             :                                      GCancellable  *cancellable,
     216                 :             :                                      GError       **error)
     217                 :             : {
     218                 :           4 :   GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (initable);
     219                 :             :   GSource *source;
     220                 :             : 
     221                 :           4 :   if (monitor->poll_interval_ms > 0)
     222                 :             :     {
     223                 :           3 :       if (monitor->poll_interval_ms < G_TIME_SPAN_MILLISECOND)
     224                 :           3 :         source = g_timeout_source_new (monitor->poll_interval_ms);
     225                 :             :       else
     226                 :           0 :         source = g_timeout_source_new_seconds (monitor->poll_interval_ms / G_TIME_SPAN_MILLISECOND);
     227                 :             :     }
     228                 :             :   else
     229                 :             :     {
     230                 :             :       /* default 10 second */
     231                 :           1 :       source = g_timeout_source_new_seconds (G_MEMORY_MONITOR_PSI_DEFAULT_SEC);
     232                 :             :     }
     233                 :             : 
     234                 :           4 :   g_source_set_callback (source, g_memory_monitor_mem_ratio_cb,
     235                 :           4 :                          callback_data_new (monitor), (GDestroyNotify) callback_data_free);
     236                 :           4 :   monitor->worker = GLIB_PRIVATE_CALL (g_get_worker_context) ();
     237                 :           4 :   g_source_attach (source, monitor->worker);
     238                 :           4 :   monitor->source_timeout = g_steal_pointer (&source);
     239                 :             : 
     240                 :           4 :   return TRUE;
     241                 :             : }
     242                 :             : 
     243                 :             : static void
     244                 :           3 : g_memory_monitor_poll_finalize (GObject *object)
     245                 :             : {
     246                 :           3 :   GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object);
     247                 :             : 
     248                 :           3 :   g_source_destroy (monitor->source_timeout);
     249                 :           3 :   g_source_unref (monitor->source_timeout);
     250                 :             : 
     251                 :           3 :   G_OBJECT_CLASS (g_memory_monitor_poll_parent_class)->finalize (object);
     252                 :           3 : }
     253                 :             : 
     254                 :             : static void
     255                 :           1 : g_memory_monitor_poll_class_init (GMemoryMonitorPollClass *klass)
     256                 :             : {
     257                 :           1 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     258                 :             : 
     259                 :           1 :   object_class->set_property = g_memory_monitor_poll_set_property;
     260                 :           1 :   object_class->get_property = g_memory_monitor_poll_get_property;
     261                 :           1 :   object_class->finalize = g_memory_monitor_poll_finalize;
     262                 :             : 
     263                 :             :   /**
     264                 :             :    * GMemoryMonitorPoll:mem-free-ratio:
     265                 :             :    *
     266                 :             :    * Override the memory free ratio
     267                 :             :    *
     268                 :             :    * Since: 2.86
     269                 :             :    */
     270                 :           1 :   g_object_class_install_property (object_class,
     271                 :             :                                    PROP_MEM_FREE_RATIO,
     272                 :             :                                    g_param_spec_double ("mem-free-ratio", NULL, NULL,
     273                 :             :                                                         -1.0, 1.0,
     274                 :             :                                                         -1.0,
     275                 :             :                                                         G_PARAM_READWRITE |
     276                 :             :                                                         G_PARAM_CONSTRUCT_ONLY |
     277                 :             :                                                         G_PARAM_STATIC_STRINGS));
     278                 :             : 
     279                 :             :   /**
     280                 :             :    * GMemoryMonitorPoll:poll-interval-ms:
     281                 :             :    *
     282                 :             :    * Override the poll interval for monitoring the memory usage.
     283                 :             :    *
     284                 :             :    * The interval is in milliseconds. Zero means to use the default interval.
     285                 :             :    *
     286                 :             :    * Since: 2.86
     287                 :             :    */
     288                 :           1 :   g_object_class_install_property (object_class,
     289                 :             :                                    PROP_POLL_INTERVAL_MS,
     290                 :             :                                    g_param_spec_uint ("poll-interval-ms", NULL, NULL,
     291                 :             :                                                       0, G_MAXUINT,
     292                 :             :                                                       0,
     293                 :             :                                                       G_PARAM_READWRITE |
     294                 :             :                                                       G_PARAM_CONSTRUCT_ONLY |
     295                 :             :                                                       G_PARAM_STATIC_STRINGS));
     296                 :             : 
     297                 :           1 : }
     298                 :             : 
     299                 :             : static void
     300                 :           1 : g_memory_monitor_poll_iface_init (GMemoryMonitorInterface *monitor_iface)
     301                 :             : {
     302                 :           1 : }
     303                 :             : 
     304                 :             : static void
     305                 :           1 : g_memory_monitor_poll_initable_iface_init (GInitableIface *iface)
     306                 :             : {
     307                 :           1 :   iface->init = g_memory_monitor_poll_initable_init;
     308                 :           1 : }
     309                 :             : 
        

Generated by: LCOV version 2.0-1