Coverage Report

Created: 2025-07-23 08:13

/src/pango/subprojects/glib/gio/gmemorymonitorpoll.c
Line
Count
Source (jump to first uncovered line)
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
0
#define G_MEMORY_MONITOR_PSI_DEFAULT_SEC 10
79
80
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
g_memory_monitor_poll_init (GMemoryMonitorPoll *mem_poll)
93
0
{
94
0
  mem_poll->mem_free_ratio = -1.0;
95
0
}
96
97
static void
98
g_memory_monitor_poll_set_property (GObject      *object,
99
                                    guint         prop_id,
100
                                    const GValue *value,
101
                                    GParamSpec   *pspec)
102
0
{
103
0
  GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object);
104
105
0
  switch ((GMemoryMonitorPollProperty) prop_id)
106
0
    {
107
0
    case PROP_MEM_FREE_RATIO:
108
0
      monitor->mem_free_ratio = g_value_get_double (value);
109
0
      break;
110
0
    case PROP_POLL_INTERVAL_MS:
111
0
      monitor->poll_interval_ms = g_value_get_uint (value);
112
0
      break;
113
0
    default:
114
0
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
115
0
      break;
116
0
    }
117
0
}
118
119
static void
120
g_memory_monitor_poll_get_property (GObject    *object,
121
                                    guint       prop_id,
122
                                    GValue     *value,
123
                                    GParamSpec *pspec)
124
0
{
125
0
  GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object);
126
127
0
  switch ((GMemoryMonitorPollProperty) prop_id)
128
0
    {
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
0
    }
139
0
}
140
141
static gboolean
142
g_memory_monitor_mem_ratio_cb (gpointer data)
143
0
{
144
0
  GMemoryMonitorPoll *monitor = (GMemoryMonitorPoll *) data;
145
0
  gdouble mem_ratio;
146
0
  GMemoryMonitorLowMemoryLevel warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID;
147
148
  /* Should be executed in the worker context */
149
0
  g_assert (g_main_context_is_owner (monitor->worker));
150
151
0
  mem_ratio = g_memory_monitor_base_query_mem_ratio ();
152
153
0
  if (mem_ratio < 0.0)
154
0
    return G_SOURCE_REMOVE;
155
156
0
  if (mem_ratio > 0.5)
157
0
    return G_SOURCE_CONTINUE;
158
159
  /* free ratio override */
160
0
  if (monitor->mem_free_ratio >= 0.0)
161
0
    mem_ratio = monitor->mem_free_ratio;
162
163
0
  g_debug ("memory free ratio %f", mem_ratio);
164
165
0
  if (mem_ratio < 0.2)
166
0
    warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_CRITICAL;
167
0
  else if (mem_ratio < 0.3)
168
0
    warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_MEDIUM;
169
0
  else if (mem_ratio < 0.4)
170
0
    warning_level = G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_LOW;
171
172
0
  if (warning_level != G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID)
173
0
    g_memory_monitor_base_send_event_to_user (G_MEMORY_MONITOR_BASE (monitor), warning_level);
174
175
0
  return G_SOURCE_CONTINUE;
176
0
}
177
178
static gboolean
179
g_memory_monitor_poll_initable_init (GInitable     *initable,
180
                                     GCancellable  *cancellable,
181
                                     GError       **error)
182
0
{
183
0
  GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (initable);
184
0
  GSource *source;
185
186
0
  if (monitor->poll_interval_ms > 0)
187
0
    {
188
0
      if (monitor->poll_interval_ms < G_TIME_SPAN_MILLISECOND)
189
0
        source = g_timeout_source_new (monitor->poll_interval_ms);
190
0
      else
191
0
        source = g_timeout_source_new_seconds (monitor->poll_interval_ms / G_TIME_SPAN_MILLISECOND);
192
0
    }
193
0
  else
194
0
    {
195
      /* default 10 second */
196
0
      source = g_timeout_source_new_seconds (G_MEMORY_MONITOR_PSI_DEFAULT_SEC);
197
0
    }
198
199
0
  g_source_set_callback (source, g_memory_monitor_mem_ratio_cb, monitor, NULL);
200
0
  monitor->worker = GLIB_PRIVATE_CALL (g_get_worker_context) ();
201
0
  g_source_attach (source, monitor->worker);
202
0
  monitor->source_timeout = g_steal_pointer (&source);
203
204
0
  return TRUE;
205
0
}
206
207
static void
208
g_memory_monitor_poll_finalize (GObject *object)
209
0
{
210
0
  GMemoryMonitorPoll *monitor = G_MEMORY_MONITOR_POLL (object);
211
212
0
  g_source_destroy (monitor->source_timeout);
213
0
  g_source_unref (monitor->source_timeout);
214
215
0
  G_OBJECT_CLASS (g_memory_monitor_poll_parent_class)->finalize (object);
216
0
}
217
218
static void
219
g_memory_monitor_poll_class_init (GMemoryMonitorPollClass *klass)
220
0
{
221
0
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
222
223
0
  object_class->set_property = g_memory_monitor_poll_set_property;
224
0
  object_class->get_property = g_memory_monitor_poll_get_property;
225
0
  object_class->finalize = g_memory_monitor_poll_finalize;
226
227
  /**
228
   * GMemoryMonitorPoll:mem-free-ratio:
229
   *
230
   * Override the memory free ratio
231
   *
232
   * Since: 2.86
233
   */
234
0
  g_object_class_install_property (object_class,
235
0
                                   PROP_MEM_FREE_RATIO,
236
0
                                   g_param_spec_double ("mem-free-ratio", NULL, NULL,
237
0
                                                        -1.0, 1.0,
238
0
                                                        -1.0,
239
0
                                                        G_PARAM_READWRITE |
240
0
                                                        G_PARAM_CONSTRUCT_ONLY |
241
0
                                                        G_PARAM_STATIC_STRINGS));
242
243
  /**
244
   * GMemoryMonitorPoll:poll-interval-ms:
245
   *
246
   * Override the poll interval for monitoring the memory usage.
247
   *
248
   * The interval is in milliseconds. Zero means to use the default interval.
249
   *
250
   * Since: 2.86
251
   */
252
0
  g_object_class_install_property (object_class,
253
0
                                   PROP_POLL_INTERVAL_MS,
254
0
                                   g_param_spec_uint ("poll-interval-ms", NULL, NULL,
255
0
                                                      0, G_MAXUINT,
256
0
                                                      0,
257
0
                                                      G_PARAM_READWRITE |
258
0
                                                      G_PARAM_CONSTRUCT_ONLY |
259
0
                                                      G_PARAM_STATIC_STRINGS));
260
261
0
}
262
263
static void
264
g_memory_monitor_poll_iface_init (GMemoryMonitorInterface *monitor_iface)
265
0
{
266
0
}
267
268
static void
269
g_memory_monitor_poll_initable_iface_init (GInitableIface *iface)
270
0
{
271
0
  iface->init = g_memory_monitor_poll_initable_init;
272
0
}
273