""" Java java.lang.ref reference APIs implemented in Python. java.lang.ref package - Reference, ReferenceQueue, SoftReference, WeakReference, PhantomReference, Finalizer. Wraps Python weakref module. """ from __future__ import annotations from .._core import Stub, AndroidOnlyError import weakref as _weakref import threading as _threading import gc as _gc __all__ = [ "Reference", "SoftReference", "WeakReference", "PhantomReference", "ReferenceQueue", "Finalizer", "FinalizerReference", "FinalizationWatchdog", ] # ============================================================ # ReferenceQueue # ============================================================ class ReferenceQueue(Stub): """ java.lang.ref.ReferenceQueue A queue that References are appended to when they are enqueued. """ def __init__(self): super().__init__() self._queue: list = [] self._lock = _threading.Lock() self._cond = _threading.Condition(self._lock) def poll(self): """ Polls to see if a reference object is available. Returns the reference if available, None otherwise. """ with self._lock: if self._queue: return self._queue.pop(0) return None def remove(self, timeout: int = 0): """ Removes the next reference object from this queue, blocking until one is available or the timeout expires. Args: timeout: timeout in milliseconds (0 = indefinite) Returns: The reference, or None if timeout expired. """ with self._lock: if self._queue: return self._queue.pop(0) if timeout == 0: self._cond.wait() if self._queue: return self._queue.pop(0) return None else: self._cond.wait(timeout / 1000.0) if self._queue: return self._queue.pop(0) return None def enqueue(self, ref: "Reference") -> bool: """ Enqueues a reference onto this queue. Returns True if successful. """ with self._lock: self._queue.append(ref) self._cond.notify() return True def __len__(self): return len(self._queue) # ============================================================ # Reference (abstract base) # ============================================================ class Reference(Stub): """ java.lang.ref.Reference Abstract base class for reference objects. """ def __init__(self, referent=None, queue: ReferenceQueue = None): super().__init__() self._referent = referent self._queue = queue self._enqueued = False # Underlying weakref to track referent lifetime self._weak = None if referent is not None: self._weak = _weakref.ref( referent, self._on_referent_collected ) def _on_referent_collected(self, weakref_obj): """Called when the referent is garbage collected.""" if self._queue is not None and not self._enqueued: self._enqueued = True self._queue.enqueue(self) def get(self): """ Returns this reference object's referent. If the referent has been garbage collected, returns None. """ if self._weak is not None: return self._weak() return self._referent def clear(self): """Clears this reference object.""" self._referent = None if self._weak is not None: self._weak = None def isEnqueued(self) -> bool: """Returns whether this reference object has been enqueued.""" return self._enqueued def enqueue(self) -> bool: """Enqueues this reference object.""" if self._queue is not None and not self._enqueued: self._enqueued = True return self._queue.enqueue(self) return False def clone(self): """Creates a shallow copy of this reference.""" return self.__class__(self.get(), self._queue) def toString(self) -> str: return f"{self.__class__.__name__}@{hex(id(self))}[referent={self.get()}]" def equals(self, other) -> bool: if not isinstance(other, Reference): return False return self.get() is other.get() def hashCode(self) -> int: r = self.get() return hash(r) & 0x7FFFFFFF if r is not None else 0 # ============================================================ # SoftReference # ============================================================ class SoftReference(Reference): """ java.lang.ref.SoftReference Soft reference objects, cleared at the discretion of the GC. """ def __init__(self, referent=None, queue: ReferenceQueue = None): super().__init__(referent, queue) self._clock = 0 def get(self): """Returns the referent; on desktop, soft references are more resilient.""" return super().get() def enqueue(self) -> bool: return super().enqueue() def clear(self): super().clear() def toString(self) -> str: return f"SoftReference@{hex(id(self))}[referent={self.get()}]" # ============================================================ # WeakReference # ============================================================ class WeakReference(Reference): """ java.lang.ref.WeakReference Weak reference objects, cleared automatically by the GC. """ def __init__(self, referent=None, queue: ReferenceQueue = None): super().__init__(referent, queue) def toString(self) -> str: return f"WeakReference@{hex(id(self))}[referent={self.get()}]" # ============================================================ # PhantomReference # ============================================================ class PhantomReference(Reference): """ java.lang.ref.PhantomReference Phantom reference objects. get() always returns None. """ def __init__(self, referent=None, queue: ReferenceQueue = None): super().__init__(referent, queue) def get(self): """ Phantom references always return None. The referent is phantom-reachable and thus not accessible. """ return None def toString(self) -> str: return f"PhantomReference@{hex(id(self))}[referent=phantom]" # ============================================================ # FinalizerReference # ============================================================ class FinalizerReference(Reference): """ java.lang.ref.FinalizerReference Represents a finalizer reference used by the VM's finalization system. """ def __init__(self, referent=None, queue: ReferenceQueue = None): super().__init__(referent, queue) self._finalized = False self._finalize_method = None if referent is not None and hasattr(referent, "finalize"): self._finalize_method = getattr(referent, "finalize", None) def finalize(self): """Invokes the referent's finalize method if it exists.""" if not self._finalized and self._finalize_method is not None: try: self._finalize_method() except Exception: pass finally: self._finalized = True def isFinalized(self) -> bool: return self._finalized def toString(self) -> str: return f"FinalizerReference@{hex(id(self))}[finalized={self._finalized}]" # ============================================================ # FinalizationWatchdog # ============================================================ class FinalizationWatchdog(Stub): """ java.lang.ref.FinalizationWatchdog A watchdog thread that ensures finalizations proceed. Desktop stub implementation. """ _instance = None _running = False _thread = None def __init__(self): super().__init__() self._timeout = 10000 # 10 seconds @classmethod def getInstance(cls): if cls._instance is None: cls._instance = FinalizationWatchdog() return cls._instance def start(self): """Starts the watchdog thread.""" if not self._running: self._running = True self._thread = _threading.Thread(target=self._run, daemon=True) self._thread.start() def stop(self): """Stops the watchdog thread.""" self._running = False def _run(self): """Watchdog loop.""" while self._running: _gc.collect() import time time.sleep(1) def setEnabled(self, enabled: bool): if enabled: self.start() else: self.stop() def toString(self) -> str: return f"FinalizationWatchdog@{hex(id(self))}[running={self._running}]" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF # ============================================================ # Finalizer # ============================================================ class Finalizer(Stub): """ java.lang.ref.Finalizer Manages object finalization. Provides methods to run finalization. """ _instance = None def __init__(self): super().__init__() @classmethod def runFinalization(cls): """ Runs the finalization methods of any objects pending finalization. Equivalent to calling System.runFinalization(). """ _gc.collect() for obj in _gc.get_objects(): if hasattr(obj, "finalize"): try: obj.finalize() except Exception: pass @classmethod def runFinalizersOnExit(cls, value: bool): """ Sets whether finalization is enabled on exit. Deprecated in Java. Desktop stub. """ if value: import atexit atexit.register(cls._finalize_all) @classmethod def _finalize_all(cls): """Finalize all objects on exit.""" for obj in _gc.get_objects(): if hasattr(obj, "finalize"): try: obj.finalize() except Exception: pass @classmethod def finalizeAllEnqueued(cls): """ Finalizes all objects that have been enqueued for finalization. """ _gc.collect() for obj in _gc.get_objects(): if hasattr(obj, "finalize"): try: obj.finalize() except Exception: pass def toString(self) -> str: return f"Finalizer@{hex(id(self))}" def equals(self, other) -> bool: return self is other def hashCode(self) -> int: return hash(self) & 0x7FFFFFFF