We encountered a wild issue today. I’m not sure I can solve it in code, but thought I’d make others aware.
In threads we use CriticalSections, but since we don’t want them hung up in case of exceptions or other early exits, we created a “holder” class. When the holder object goes out of scope, it calls Leave on the CriticalSection it’s holding. The code that instantiates it might look something like this:
MyCriticalSectionProp.Enter
dim holder as new CSHolder( MyCriticalSectionProp )
// Some code
holder = nil // Will call Leave; explicitly setting it to nil is usually optional
In case of exception or other early exit, we know that our CriticalSection will be released. This works great.
Now enter the Runtime.ObjectIterator. We have code that can run in either its own Thread or the main thread that will gather and print information about all the objects in memory. That works great too, except
Thread A creates a holder and is still using it while Thread B accesses the ObjectIterator. While Thread B is creating the report, Thread A nils the holder which should release the CriticalSection. But it doesn’t because Thread B still has a reference in the ObjectIterator. Instead, when done, Thread B releases the holder which, in turn, calls CS.Leave. But now it’s a different thread! Thread B throws an exception which is handled, and the CriticalSection that should have been released by Thread A has not been released, blocking all further access to that CriticalSection. At that point, there is nothing left to do but restart the app.
Our solution for now is to stop using that report.
Interesting. It’s an entirely logical situation, but I can see why it’s problematic.
Is it feasible that you could avoid yielding while holding on to the ObjectIterator? Perhaps create an array of objects from the ObjectIterator, filtering out your CSHolder instances, and then process the array?
It won’t be able to because the loop wouldn’t yield. For example:
Dim objects() As Object
// Atomically gather all of the objects meeting a certain criteria
If True Then
#Pragma BackgroundTasks False
Dim iter As Introspection.ObjectIterator = Runtime.IterateObjects
While iter.Next
If Not (iter.Current IsA CSHolder) Then
objects.Append(iter.Current)
End If
Wend
#Pragma BackgroundTasks True
End If
// Now do something with the objects
...
I’ll try it. But might it not yield within iter.Next or iter.Current?
The other drawback is that this code won’t be particularly portable, and will exhibit the same behavior where other destructor-reliant objects are concerned.
[quote=246171:@Michael Diehr]that doesn’t work due to these problems:
<https://xojo.com/issue/36511>
[/quote]
This doesn’t make it “not work” - it uses more cpu than necessary while suspended