From 4161f5163b2915dac39e2f14ca7dbb526bc1f257 Mon Sep 17 00:00:00 2001
From: Duncan Coutts <duncan@well-typed.com>
Date: Mon, 9 Jan 2023 01:18:13 +0000
Subject: [PATCH] Add an IOManager API for scavenging TSO blocked_info

When the GC scavenges a TSO it needs to scavenge the tso->blocked_info
but the blocked_info is a big union and what lives there depends on the
two->why_blocked, which for I/O-related reasons is something that in
principle is the responsibility of the I/O manager and not the GC. So
the right thing to do is for the GC to ask the I/O manager to sscavenge
the blocked_info if it encounters any I/O-related why_blocked reasons.

So we add scavengeTSOIOManager in IOManager.{h,c} with the usual style.

Now as it happens, right now, there is no special scavenging to do, so
the implementation of scavengeTSOIOManager is a fancy no-op. That's
because the select I/O manager uses only the fd and target members,
which are not GC pointers, and the win32-legacy I/O manager _ought_ to
be using GC-managed heap objects for the StgAsyncIOResult but it is
actually usingthe C heap, so again no GC pointers. If the win32-legacy
were doing this more sensibly, then scavengeTSOIOManager would be the
right place to do the GC magic.

Future I/O managers will need GC heap objects in the tso->blocked_info
and will make use of this functionality.
---
 rts/IOManager.c | 26 ++++++++++++++++++++++++++
 rts/IOManager.h |  5 +++++
 rts/sm/Scav.c   |  7 +++++++
 3 files changed, 38 insertions(+)

diff --git a/rts/IOManager.c b/rts/IOManager.c
index ee110d34f903..0c0b1819fcc8 100644
--- a/rts/IOManager.c
+++ b/rts/IOManager.c
@@ -480,6 +480,32 @@ void markCapabilityIOManager(evac_fn       evac,
 }
 
 
+void scavengeTSOIOManager(StgTSO *tso)
+{
+    switch (iomgr_type) {
+
+            /* case IO_MANAGER_SELECT:
+             * BlockedOn{Read,Write} uses block_info.fd
+             * BlockedOnDelay        uses block_info.target
+             * both of these are not GC pointers, so there is nothing to do.
+             */
+
+            /* case IO_MANAGER_WIN32_LEGACY:
+             * BlockedOn{Read,Write,DoProc} uses block_info.async_result
+             * The StgAsyncIOResult async_result is allocated on the C heap.
+             * It'd probably be better if it used the GC heap. If it did we'd
+             * scavenge it here.
+             */
+
+        default:
+            /* All the other I/O managers do not use I/O-related why_blocked
+             * reasons, so there are no cases to handle.
+             */
+            break;
+    }
+}
+
+
 /* Declared in rts/IOInterface.h. Used only by the MIO threaded I/O manager on
  * Unix platforms.
  */
diff --git a/rts/IOManager.h b/rts/IOManager.h
index 803960b0c236..7d9b793e31c6 100644
--- a/rts/IOManager.h
+++ b/rts/IOManager.h
@@ -272,6 +272,11 @@ void wakeupIOManager(void);
 void markCapabilityIOManager(evac_fn evac, void *user, CapIOManager *iomgr);
 
 
+/* GC hook: scavenge I/O related tso->block_info. Used by scavengeTSO.
+ */
+void scavengeTSOIOManager(StgTSO *tso);
+
+
 /* Several code paths are almost identical between read and write paths. In
  * such cases we use a shared code path with an enum to say which we're doing.
  */
diff --git a/rts/sm/Scav.c b/rts/sm/Scav.c
index ca40a17632f5..59465e7bd185 100644
--- a/rts/sm/Scav.c
+++ b/rts/sm/Scav.c
@@ -61,6 +61,7 @@
 #include "Trace.h"
 #include "Sanity.h"
 #include "Capability.h"
+#include "IOManager.h"
 #include "LdvProfile.h"
 #include "HeapUtils.h"
 #include "Hash.h"
@@ -145,6 +146,12 @@ scavengeTSO (StgTSO *tso)
     case NotBlocked:
         evacuate(&tso->block_info.closure);
         break;
+    case BlockedOnRead:
+    case BlockedOnWrite:
+    case BlockedOnDelay:
+    case BlockedOnDoProc:
+        scavengeTSOIOManager(tso);
+        break;
     default:
 #if defined(THREADED_RTS)
     // in the THREADED_RTS, block_info.closure must always point to a
-- 
GitLab