[glib: 3/4] gio/win32: add GMemoryMonitorWin32




commit bb1b9d90ec2141c1690e222edc4b26427cd8a26c
Author: Marc-André Lureau <marcandre lureau redhat com>
Date:   Mon Jan 24 00:36:56 2022 +0400

    gio/win32: add GMemoryMonitorWin32
    
    Windows has CreateMemoryResourceNotification() API:
    
    https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-creatememoryresourcenotification
    
    It only notifies whether "Available physical memory is running low."
    
    Signed-off-by: Marc-André Lureau <marcandre lureau redhat com>

 gio/giomodule.c           |   2 +
 gio/gmemorymonitorwin32.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++
 gio/meson.build           |   1 +
 3 files changed, 264 insertions(+)
---
diff --git a/gio/giomodule.c b/gio/giomodule.c
index d34037a45..90af75118 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -1080,6 +1080,7 @@ extern GType _g_network_monitor_nm_get_type (void);
 
 extern GType g_memory_monitor_dbus_get_type (void);
 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);
 
 #ifdef G_OS_UNIX
@@ -1315,6 +1316,7 @@ _g_io_modules_ensure_loaded (void)
 #ifdef G_OS_WIN32
       g_type_ensure (g_win32_notification_backend_get_type ());
       g_type_ensure (_g_winhttp_vfs_get_type ());
+      g_type_ensure (g_memory_monitor_win32_get_type ());
 #endif
       g_type_ensure (_g_local_vfs_get_type ());
       g_type_ensure (_g_dummy_proxy_resolver_get_type ());
diff --git a/gio/gmemorymonitorwin32.c b/gio/gmemorymonitorwin32.c
new file mode 100644
index 000000000..c0e09a5bf
--- /dev/null
+++ b/gio/gmemorymonitorwin32.c
@@ -0,0 +1,261 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2022 Red Hat, Inc.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include "gmemorymonitor.h"
+#include "gioerror.h"
+#include "ginitable.h"
+#include "giomodule-priv.h"
+#include "glibintl.h"
+#include "glib/gstdio.h"
+#include "gcancellable.h"
+
+#include <windows.h>
+
+#define G_TYPE_MEMORY_MONITOR_WIN32 (g_memory_monitor_win32_get_type ())
+G_DECLARE_FINAL_TYPE (GMemoryMonitorWin32, g_memory_monitor_win32, G, MEMORY_MONITOR_WIN32, GObject)
+
+#define G_MEMORY_MONITOR_WIN32_GET_INITABLE_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), G_TYPE_INITABLE, 
GInitable))
+
+static void g_memory_monitor_win32_iface_init (GMemoryMonitorInterface *iface);
+static void g_memory_monitor_win32_initable_iface_init (GInitableIface *iface);
+
+struct _GMemoryMonitorWin32
+{
+  GObject parent_instance;
+
+  HANDLE event;
+  HANDLE mem;
+  HANDLE thread;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GMemoryMonitorWin32, g_memory_monitor_win32, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+                                                g_memory_monitor_win32_initable_iface_init)
+                         G_IMPLEMENT_INTERFACE (G_TYPE_MEMORY_MONITOR,
+                                                g_memory_monitor_win32_iface_init)
+                         _g_io_modules_ensure_extension_points_registered ();
+                         g_io_extension_point_implement (G_MEMORY_MONITOR_EXTENSION_POINT_NAME,
+                                                         g_define_type_id,
+                                                         "win32",
+                                                         30))
+
+static void
+g_memory_monitor_win32_init (GMemoryMonitorWin32 *win32)
+{
+}
+
+static gboolean
+watch_handler (gpointer user_data)
+{
+  GMemoryMonitorWin32 *win32 = user_data;
+
+  g_signal_emit_by_name (win32, "low-memory-warning",
+                         G_MEMORY_MONITOR_WARNING_LEVEL_LOW);
+
+  return G_SOURCE_REMOVE;
+}
+
+/* Thread which watches for win32 memory resource events */
+static DWORD WINAPI
+watch_thread_function (LPVOID parameter)
+{
+  GWeakRef *weak_ref = parameter;
+  GMemoryMonitorWin32 *win32 = NULL;
+  HANDLE handles[2] = { 0, };
+  DWORD result;
+  BOOL low_memory_state;
+
+  win32 = g_weak_ref_get (weak_ref);
+  if (!win32)
+    goto end;
+
+  if (!DuplicateHandle (GetCurrentProcess (),
+                        win32->event,
+                        GetCurrentProcess (),
+                        &handles[0],
+                        0,
+                        FALSE,
+                        DUPLICATE_SAME_ACCESS))
+    {
+      gchar *emsg;
+
+      emsg = g_win32_error_message (GetLastError ());
+      g_debug ("DuplicateHandle failed: %s", emsg);
+      g_free (emsg);
+      goto end;
+    }
+
+  if (!DuplicateHandle (GetCurrentProcess (),
+                        win32->mem,
+                        GetCurrentProcess (),
+                        &handles[1],
+                        0,
+                        FALSE,
+                        DUPLICATE_SAME_ACCESS))
+    {
+      gchar *emsg;
+
+      emsg = g_win32_error_message (GetLastError ());
+      g_debug ("DuplicateHandle failed: %s", emsg);
+      g_free (emsg);
+      goto end;
+    }
+
+  g_clear_object (&win32);
+
+  while (1)
+    {
+      if (!QueryMemoryResourceNotification (handles[1], &low_memory_state))
+        {
+          gchar *emsg;
+
+          emsg = g_win32_error_message (GetLastError ());
+          g_debug ("QueryMemoryResourceNotification failed: %s", emsg);
+          g_free (emsg);
+          break;
+        }
+
+      win32 = g_weak_ref_get (weak_ref);
+      if (!win32)
+        break;
+
+      if (low_memory_state)
+        {
+          g_idle_add_full (G_PRIORITY_DEFAULT,
+                           watch_handler,
+                           g_steal_pointer (&win32),
+                           g_object_unref);
+          /* throttle a bit the loop */
+          g_usleep (G_USEC_PER_SEC);
+          continue;
+        }
+
+      g_clear_object (&win32);
+
+      result = WaitForMultipleObjects (G_N_ELEMENTS (handles), handles, FALSE, INFINITE);
+      switch (result)
+        {
+          case WAIT_OBJECT_0 + 1:
+            continue;
+
+          case WAIT_FAILED:
+            {
+              gchar *emsg;
+
+              emsg = g_win32_error_message (GetLastError ());
+              g_debug ("WaitForMultipleObjects failed: %s", emsg);
+              g_free (emsg);
+            }
+            G_GNUC_FALLTHROUGH;
+          default:
+            goto end;
+        }
+    }
+
+end:
+  if (handles[0])
+    CloseHandle (handles[0]);
+  if (handles[1])
+    CloseHandle (handles[1]);
+  g_clear_object (&win32);
+  g_weak_ref_clear (weak_ref);
+  g_free (weak_ref);
+  return 0;
+}
+
+static gboolean
+g_memory_monitor_win32_initable_init (GInitable     *initable,
+                                      GCancellable  *cancellable,
+                                      GError       **error)
+{
+  GMemoryMonitorWin32 *win32 = G_MEMORY_MONITOR_WIN32 (initable);
+  GWeakRef *weak_ref = NULL;
+
+  win32->event = CreateEvent (NULL, FALSE, FALSE, NULL);
+  if (win32->event == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+                           "Failed to create event");
+      return FALSE;
+    }
+
+  win32->mem = CreateMemoryResourceNotification (LowMemoryResourceNotification);
+  if (win32->mem == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+                           "Failed to create resource notification handle");
+      return FALSE;
+    }
+
+  weak_ref = g_new0 (GWeakRef, 1);
+  g_weak_ref_init (weak_ref, win32);
+  /* Use CreateThread (not GThread) with a small stack to make it more lightweight. */
+  win32->thread = CreateThread (NULL, 1024, watch_thread_function, weak_ref, 0, NULL);
+  if (win32->thread == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (GetLastError ()),
+                           "Failed to create memory resource notification thread");
+      g_weak_ref_clear (weak_ref);
+      g_free (weak_ref);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static void
+g_memory_monitor_win32_finalize (GObject *object)
+{
+  GMemoryMonitorWin32 *win32 = G_MEMORY_MONITOR_WIN32 (object);
+
+  if (win32->thread)
+    {
+      SetEvent (win32->event);
+      WaitForSingleObject (win32->thread, INFINITE);
+      CloseHandle (win32->thread);
+    }
+
+  if (win32->event)
+    CloseHandle (win32->event);
+
+  if (win32->mem)
+    CloseHandle (win32->mem);
+
+  G_OBJECT_CLASS (g_memory_monitor_win32_parent_class)->finalize (object);
+}
+
+static void
+g_memory_monitor_win32_class_init (GMemoryMonitorWin32Class *nl_class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (nl_class);
+
+  gobject_class->finalize = g_memory_monitor_win32_finalize;
+}
+
+static void
+g_memory_monitor_win32_iface_init (GMemoryMonitorInterface *monitor_iface)
+{
+}
+
+static void
+g_memory_monitor_win32_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = g_memory_monitor_win32_initable_init;
+}
diff --git a/gio/meson.build b/gio/meson.build
index dbe903db2..43c9c41cc 100644
--- a/gio/meson.build
+++ b/gio/meson.build
@@ -423,6 +423,7 @@ else
   platform_deps += uwp_gio_deps
 
   win32_sources += files(
+    'gmemorymonitorwin32.c',
     'gwin32registrykey.c',
     'gwin32mount.c',
     'gwin32volumemonitor.c',


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]