Skip to content

Commit d500890

Browse files
[Python][builder mode] Replace __del__ methods with weakref.finalize (#3350)
This issue primarily affects Docker environments where Python module teardown order is less predictable than in wheel installations. The weakref.finalize() approach is recommended by Python documentation as more reliable than __del__ methods during interpreter shutdown. --------- Signed-off-by: Pradnya Khalate <[email protected]>
1 parent 3588f65 commit d500890

File tree

2 files changed

+32
-13
lines changed

2 files changed

+32
-13
lines changed

python/cudaq/kernel/captured_data.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
# ============================================================================ #
88

99
import uuid
10-
import numpy as np
10+
import weakref
1111

12+
import numpy as np
1213
from cudaq.mlir._mlir_libs._quakeDialects import cudaq_runtime
1314
from cudaq.mlir.dialects import arith, cc, func
1415
from cudaq.mlir.ir import (ComplexType, F32Type, F64Type, FlatSymbolRefAttr,
@@ -34,20 +35,29 @@ def __init__(self, **kwargs):
3435
self.loc = kwargs['loc'] if 'loc' in kwargs else None
3536
self.name = kwargs['name'] if 'name' in kwargs else None
3637
self.module = kwargs['module'] if 'module' in kwargs else None
38+
self._finalizer = weakref.finalize(self, CapturedDataStorage._cleanup,
39+
self.cudaqStateIDs, self.arrayIDs)
40+
41+
@staticmethod
42+
def _cleanup(state_ids, array_ids):
43+
"""
44+
Safely clean up resources associated with captured data during garbage collection.
45+
This method is to be used with `weakref.finalize()` as an alternative to `__del__`,
46+
such that it handles Python interpreter shutdown gracefully, catching exceptions
47+
that occur when modules are unloaded before they are cleaned up.
48+
"""
49+
try:
50+
cudaq_runtime.deletePointersToCudaqState(state_ids)
51+
cudaq_runtime.deletePointersToStateData(array_ids)
52+
except (ImportError, AttributeError):
53+
pass
3754

3855
def setKernelContext(self, ctx, loc, name, module):
3956
self.ctx = ctx
4057
self.loc = loc
4158
self.name = name
4259
self.module = module
4360

44-
def __del__(self):
45-
"""
46-
Remove pointers to stored data for the current kernel.
47-
"""
48-
cudaq_runtime.deletePointersToCudaqState(self.cudaqStateIDs)
49-
cudaq_runtime.deletePointersToStateData(self.arrayIDs)
50-
5161
def getIntegerAttr(self, type, value):
5262
"""
5363
Return an MLIR Integer Attribute of the given IntegerType.

python/cudaq/kernel/kernel_builder.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
# the terms of the Apache License 2.0 which accompanies this distribution. #
77
# ============================================================================ #
88

9-
import numpy as np
109
import random
1110
import re
1211
import string
12+
import weakref
1313
from functools import partialmethod
1414
from typing import get_origin
1515

16+
import numpy as np
1617
from cudaq.mlir.ir import (
1718
BoolAttr,
1819
Block,
@@ -316,12 +317,20 @@ def __init__(self, argTypeList):
316317

317318
self.insertPoint = InsertionPoint.at_block_begin(e)
318319

319-
def __del__(self):
320+
self._finalizer = weakref.finalize(self, PyKernel._cleanup,
321+
self.capturedDataStorage)
322+
323+
@staticmethod
324+
def _cleanup(capturedDataStorage):
320325
"""
321-
When a kernel builder is deleted we need to clean up
322-
any state data if there is any.
326+
Cleanup function to be called when the `PyKernel` instance is garbage
327+
collected. This resource management method is used with `weakref.finalize()`
328+
to ensure proper cleanup of resources. Note that this method is intentionally
329+
empty since `CapturedDataStorage` has its own `finalizer`. However, it is still
330+
included for maintaining the reference to `CapturedDataStorage` until the
331+
`PyKernel` instance is garbage collected ensuring proper cleanup order.
323332
"""
324-
self.capturedDataStorage.__del__()
333+
pass
325334

326335
def __processArgType(self, ty):
327336
"""

0 commit comments

Comments
 (0)