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 : :
21 : : #include "config.h"
22 : :
23 : : #include "gcancellable.h"
24 : : #include "ginitable.h"
25 : : #include "gioerror.h"
26 : : #include "giomodule-priv.h"
27 : : #include "glib/gstdio.h"
28 : : #include "glibintl.h"
29 : : #include "gmemorymonitor.h"
30 : : #include "gmemorymonitorbase.h"
31 : :
32 : : #ifdef HAVE_SYSINFO
33 : : #include <sys/sysinfo.h>
34 : : #elif defined(HAVE_UNISTD_H)
35 : : #include <unistd.h>
36 : : #endif
37 : :
38 : : /**
39 : : * GMemoryMonitorBase:
40 : : *
41 : : * An abstract base class for implementations of [iface@Gio.MemoryMonitor] which
42 : : * provides several defined warning levels (`GLowMemoryLevel`) and tracks how
43 : : * often they are notified to the user via [signal@Gio.MemoryMonitor::low-memory-warning]
44 : : * to limit the number of signal emissions to one every 15 seconds for each level.
45 : : * [method@Gio.MemoryMonitorBase.send_event_to_user] is provided for this purpose.
46 : : */
47 : :
48 : : /* The interval between sending a signal in second */
49 : : #define RECOVERY_INTERVAL_SEC 15
50 : :
51 : : #define G_MEMORY_MONITOR_BASE_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, GInitable))
52 : :
53 : : static void g_memory_monitor_base_iface_init (GMemoryMonitorInterface *iface);
54 : : static void g_memory_monitor_base_initable_iface_init (GInitableIface *iface);
55 : :
56 : : typedef struct
57 : : {
58 : : GObject parent_instance;
59 : :
60 : : guint64 last_trigger_us[G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_COUNT];
61 : : } GMemoryMonitorBasePrivate;
62 : :
63 : :
64 : 433 : G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GMemoryMonitorBase, g_memory_monitor_base, G_TYPE_OBJECT,
65 : : G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
66 : : g_memory_monitor_base_initable_iface_init)
67 : : G_IMPLEMENT_INTERFACE (G_TYPE_MEMORY_MONITOR,
68 : : g_memory_monitor_base_iface_init)
69 : : G_ADD_PRIVATE (GMemoryMonitorBase))
70 : :
71 : : gdouble
72 : 6 : g_memory_monitor_base_query_mem_ratio (void)
73 : : {
74 : : #ifdef HAVE_SYSINFO
75 : : struct sysinfo info;
76 : :
77 : 6 : if (sysinfo (&info))
78 : 0 : return -1.0;
79 : :
80 : 6 : if (info.totalram == 0)
81 : 0 : return -1.0;
82 : :
83 : 6 : return (gdouble) ((gdouble) info.freeram / (gdouble) info.totalram);
84 : : #elif defined(_SC_PHYS_PAGES) && defined(_SC_AVPHYS_PAGES)
85 : : /* Implementation for Solaris, where sysinfo() does not return RAM usage information */
86 : : long totalram = sysconf (_SC_PHYS_PAGES);
87 : : long freeram = sysconf (_SC_AVPHYS_PAGES);
88 : :
89 : : if (totalram <= 0 || freeram < 0)
90 : : return -1.0;
91 : :
92 : : return (gdouble) ((gdouble) freeram / (gdouble) totalram);
93 : : #else
94 : : return -1.0;
95 : : #endif
96 : : }
97 : :
98 : : GMemoryMonitorWarningLevel
99 : 6 : g_memory_monitor_base_level_enum_to_byte (GMemoryMonitorLowMemoryLevel level)
100 : : {
101 : 6 : const GMemoryMonitorWarningLevel level_bytes[G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_COUNT] = {
102 : : [G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_LOW] = 50,
103 : : [G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_MEDIUM] = 100,
104 : : [G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_CRITICAL] = 255
105 : : };
106 : :
107 : 6 : if ((int) level < G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID ||
108 : : (int) level >= G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_COUNT)
109 : : g_assert_not_reached ();
110 : :
111 : 6 : if (level == G_MEMORY_MONITOR_LOW_MEMORY_LEVEL_INVALID)
112 : 0 : return 0;
113 : :
114 : 6 : return level_bytes[level];
115 : : }
116 : :
117 : : typedef struct
118 : : {
119 : : GWeakRef monitor_weak;
120 : : GMemoryMonitorWarningLevel level;
121 : : } SendEventData;
122 : :
123 : : static void
124 : 5 : send_event_data_free (SendEventData *data)
125 : : {
126 : 5 : g_weak_ref_clear (&data->monitor_weak);
127 : 5 : g_free (data);
128 : 5 : }
129 : :
130 : : /* Invoked in the global default main context */
131 : : static gboolean
132 : 5 : send_event_cb (void *user_data)
133 : : {
134 : 5 : SendEventData *data = user_data;
135 : 5 : GMemoryMonitor *monitor = g_weak_ref_get (&data->monitor_weak);
136 : :
137 : 5 : if (monitor != NULL)
138 : 5 : g_signal_emit_by_name (monitor, "low-memory-warning", data->level);
139 : :
140 : 5 : g_clear_object (&monitor);
141 : :
142 : 5 : return G_SOURCE_REMOVE;
143 : : }
144 : :
145 : : void
146 : 6 : g_memory_monitor_base_send_event_to_user (GMemoryMonitorBase *monitor,
147 : : GMemoryMonitorLowMemoryLevel warning_level)
148 : : {
149 : : gint64 current_time;
150 : 6 : GMemoryMonitorBasePrivate *priv = g_memory_monitor_base_get_instance_private (monitor);
151 : :
152 : 6 : current_time = g_get_monotonic_time ();
153 : :
154 : 6 : if (priv->last_trigger_us[warning_level] == 0 ||
155 : 0 : (current_time - priv->last_trigger_us[warning_level]) > (RECOVERY_INTERVAL_SEC * G_USEC_PER_SEC))
156 : : {
157 : 6 : SendEventData *data = NULL;
158 : :
159 : 6 : g_debug ("Send low memory signal with warning level %u", warning_level);
160 : :
161 : : /* The signal has to be emitted in the global default main context,
162 : : * because the `GMemoryMonitor` is a singleton which may have been created
163 : : * in an arbitrary thread, or which may be calling this function from the
164 : : * GLib worker thread. */
165 : 6 : data = g_new0 (SendEventData, 1);
166 : 6 : g_weak_ref_init (&data->monitor_weak, monitor);
167 : 6 : data->level = g_memory_monitor_base_level_enum_to_byte (warning_level);
168 : 6 : g_main_context_invoke_full (NULL, G_PRIORITY_DEFAULT, send_event_cb,
169 : : g_steal_pointer (&data), (GDestroyNotify) send_event_data_free);
170 : 6 : priv->last_trigger_us[warning_level] = current_time;
171 : : }
172 : 6 : }
173 : :
174 : : static gboolean
175 : 0 : g_memory_monitor_base_initable_init (GInitable *initable,
176 : : GCancellable *cancellable,
177 : : GError **error)
178 : : {
179 : 0 : return TRUE;
180 : : }
181 : :
182 : : static void
183 : 5 : g_memory_monitor_base_init (GMemoryMonitorBase *monitor)
184 : : {
185 : 5 : }
186 : :
187 : : static void
188 : 2 : g_memory_monitor_base_class_init (GMemoryMonitorBaseClass *klass)
189 : : {
190 : 2 : }
191 : :
192 : : static void
193 : 2 : g_memory_monitor_base_iface_init (GMemoryMonitorInterface *monitor_iface)
194 : : {
195 : 2 : }
196 : :
197 : : static void
198 : 2 : g_memory_monitor_base_initable_iface_init (GInitableIface *iface)
199 : : {
200 : 2 : iface->init = g_memory_monitor_base_initable_init;
201 : 2 : }
|