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