From ddd5225c9d23e451d6975d87018505f8a72d75e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Mon, 25 Mar 2024 08:33:42 +0100 Subject: [PATCH] Avoid possible GC allocations in MongoCursor.~this. Fixes #2793. This adds a check to the destructor to avoid running into an InvalidMemoryOperationError and instead outputs a more descriptive error message without crashing. This also swaps back the order of operations in killCursors() so that no connection to the server gets allocated by the call if the cursor has already been killed. --- mongodb/vibe/db/mongo/cursor.d | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/mongodb/vibe/db/mongo/cursor.d b/mongodb/vibe/db/mongo/cursor.d index ff855e9f1..79d1203e4 100644 --- a/mongodb/vibe/db/mongo/cursor.d +++ b/mongodb/vibe/db/mongo/cursor.d @@ -119,9 +119,22 @@ struct MongoCursor(DocType = Bson) { ~this() @safe { + import core.memory : GC; + if( m_data && --m_data.refCount == 0 ){ try { - m_data.killCursors(); + // avoid InvalidMemoryOperation errors in case the cursor was + // leaked to the GC + if (m_data.alive && GC.inFinalizer) { + logError("MongoCursor instance that has not been fully processed leaked to the GC!"); + try throw new Exception(""); + catch (Exception e) { + try () @trusted { logError("%s", e.info); } (); + catch (Exception e2) logError(" ... failed to generate stack trace"); + } + } else { + m_data.killCursors(); + } } catch (MongoException e) { logWarn("MongoDB failed to kill cursors: %s", e.msg); logDiagnostic("%s", (() @trusted => e.toString)()); @@ -288,6 +301,7 @@ struct MongoCursor(DocType = Bson) { /// interface because we still have code for legacy (<3.6) MongoDB servers, /// which may still used with the old legacy overloads. private interface IMongoCursorData(DocType) { + @property bool alive() @safe nothrow; bool empty() @safe; /// Range implementation long index() @safe; /// Range implementation DocType front() @safe; /// Range implementation @@ -326,6 +340,8 @@ private deprecated abstract class LegacyMongoCursorData(DocType) : IMongoCursorD long m_limit = 0; } + @property bool alive() @safe nothrow { return m_cursor != 0; } + final bool empty() @safe { if (!m_iterationStarted) startIterating(); @@ -390,8 +406,8 @@ private deprecated abstract class LegacyMongoCursorData(DocType) : IMongoCursorD final void killCursors() @safe { - auto conn = m_client.lockConnection(); if (m_cursor == 0) return; + auto conn = m_client.lockConnection(); conn.killCursors(m_collection, () @trusted { return (&m_cursor)[0 .. 1]; } ()); m_cursor = 0; } @@ -448,6 +464,8 @@ private class MongoFindCursor(DocType) : IMongoCursorData!DocType { m_database = command["$db"].opt!string; } + @property bool alive() @safe nothrow { return m_cursor != 0; } + bool empty() @safe { if (!m_iterationStarted) startIterating(); @@ -515,8 +533,8 @@ private class MongoFindCursor(DocType) : IMongoCursorData!DocType { final void killCursors() @safe { - auto conn = m_client.lockConnection(); if (m_cursor == 0) return; + auto conn = m_client.lockConnection(); conn.killCursors(m_ns, () @trusted { return (&m_cursor)[0 .. 1]; } ()); m_cursor = 0; }