File: PC-Phone USB Sync/code/PC-Phone USB Sync--source/purgatory.py

class Purgatory:

    """
    ------------------------------------------------------------------------------
    [1.5] A simple fixed-length queue of 'numsouls' objects.
    Part of program PC-Phone USB Sync, with same terms of use.

    Used to temporarily retain Python objects sent back to Java via pyjnius
    on Android, per a theory that they might be garbage collected on the
    Python side before used by Java and thus trigger crashes.  Referencing 
    objects in the queue postpones their Python garbage collection.

    This could explain a Java exception that occurred for an insets object, 
    which is passed from Java to the Python pyjnius edge-to-edge callback 
    onApplyWindowInsets and then returned as the callback's result to Java. 
    In some cases (e.g., screen switches on a Fold7), there are three such 
    callbacks in rapid succession, so simply saving a single object in self 
    is not enough.  The Java info:

       java.lang.NullPointerException: Attempt to invoke virtual method 
       'boolean android.view.WindowInsets.isConsumed()' on a null object 
       reference" [in java.lang.reflect.Method.invoke(Native Method)]

    This also may be entirely moot.  Two such crashes were observed in the 
    logcat within 9 minutes of each other and the app did seem to vanish during 
    a screen switch, but the history on this is impossible to reconstruct.  There 
    was also unrelated prototyping in the same timeframe that crashed in the insets
    callback for known unrelated coding errors, and inadvertently pressing Android 
    Back twice while juggling the device to switch screens can also exit the app.

    Moreover, the crashes could not be recreated, and no others have been seen 
    or reported, even though insets callbacks have been used for 6 months since 
    1.4.0.  In addition, pyjnius' docs state that the callback function itself 
    must have a retained reference but say nothing about function return values, 
    and pyjnius' return-value code seems sound on first survey.  A general web 
    search was ambiguous, and an AI result advised saves - perhaps stupidly.

    But race conditions are sporadic by nature, threads may happen on either 
    side of the Python/Java fence, and the new Splitter max_size update on insets
    callbacks could conceivably aggravate a looming threat.  Hence, this queue 
    is used as a just-in-case safety measure.  It's harmless and trivial if it's
    unnecessary, but vanishing apps are a Bad Thing.  

    Update: it's now nearly certain that the crashes this module addresses were 
    due to unrelated errors or inadvertent Back taps during screen changes, not 
    garbage collection.  A later app vanish yielded a normal exit for Back tap 
    in the logcat.  Another 1.5 mod now pauses the app on Android Back instead
    of closing it, so the app will be suspended in Recents if (when) this recurs. 
    ------------------------------------------------------------------------------
    """

    def __init__(self, numsouls=6):
        self.holding = [0] * numsouls

    def retain(self, object, trace=False):
        self.holding.pop(0)                      # may garbage collect now
        self.holding.append(object)              # stage next soul in queue
        if trace: 
            print(f'Purgatory: {self.holding}')

if __name__ == '__main__':
   retainer = Purgatory()
   for object in 'Race Condition!':
       retainer.retain(object, True)