Context switches with Threads / WebTreads & weakref race condition memory leaks

  1. 5 days ago

    Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    The manual states:

    Threads can yield time to other threads and other applications each time they execute a looping construct such as For, While, and Do. However, a thread does not necessarily yield time at every loop boundary even though it has an opportunity to do so. A thread actually yields to another thread when the Thread Scheduler decides that its timeslice has expired. Context switches are expensive, so the Thread Scheduler always tries to avoid them.

    I need to test a weakref to make sure the object to which it refers hasn't gone out of scope (not Nil) to avoid a race condition. Don't want to do that every time the object is called. So, is it safe to test once at the top of the loop boundary?

  2. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    And also after every loop in case it switches out immediately after the bottom loop boundary. Anywhere else?

    Need to know because no error is raised when a nil weakref object is encountered - so when a race condition occurs the thread hangs if it's in a Webthread at a CriticalSection being the property of the nil object. This causes a memory leak since the thread is waiting for entry into the nil CriticalSection so never exits and is never collected. That's what seems to be happening anyway.

    I have plugged much of this by testing for nil object at loop boundaries which has fixed most of it but the app is still leaking somewhere and I need to be able to understand the possibilities...

  3. Christian S

    Jun 29 Testers, Xojo Pro, XDC Speakers, Third Party Store Germany

    Well, calling a method may free the object, so there is no easy way to cache the status.
    Just check when you need the weak ref.

  4. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    Thanks Christian, that's the right approach I think but there are many lines of code working with the reference so I only want to test after a context switch could occur which could have nilled the object out. That seems to be at the top of any loop and immediately after the exit of any loop in the thread. Was wondering if there were any other possibilities or should I be looking for the leak somewhere else completely? (It's a biggish app with many threads so the call stack often doesn't give much of a clue).

  5. Markus R

    Jun 29 Testers, Xojo Pro Europe / Germany / Lower Saxon...

    if you add a shared property you could count the object instances in the constructor/destructor.
    you should avoid removing objects if they are in use elsewhere.
    using AddHandler is better with WeakAddressOf but also use the RemoveHandler.
    tracking resources i do with Runtime
    if the runtime data looks good then a memory leaks by framework can be rarely possibly.

  6. Christian S

    Jun 29 Testers, Xojo Pro, XDC Speakers, Third Party Store Germany

    You may just resolve the weak reference to put it in a local variable on the start of the method.
    Then you have a reference for the whole method.

  7. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    Nice tip about Runtime Markus... will check it out. However testing for nil in a weakref is better than checking for a constructor/destructor count because at zero count the object will already be destroyed by Xojo. Hence my wish to test for its non-existence to stop the thread hanging when using that nil object's CriticalSection for regulating access to a resource. As I would rather not refactor that part of the app, the question is, where to test for nil object?

  8. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    Christian,the weak reference is assigned to a Computed Property of the Thread that is set to the class type. This is set from outside the Thread before it was run. The Property's Get provides access to another property similar to what you suggested locally with a variable. However this did not make the weak ref strong again - you only seem to get a reference to what was in the chain of previous assignments nothing more.

    Trying to kill the thread from the referred object by adding a quit method works to get the thread out of the Globals / Threads list but the leak continued, indicating the thread still wasn't exiting properly (don't know why). In fact the leak always occurs/occurred despite the list showing the thread's instances were all gone...

  9. Markus R

    Jun 29 Testers, Xojo Pro Europe / Germany / Lower Saxon...
    Edited 5 days ago

    @Eric W However testing for nil in a weakref is better than checking for a constructor/destructor count because at zero count the object will already be destroyed by Xojo.

    because you speak about a memory leak the idea was to count the global usage of the class instances similar the runtime object count.

    instance count

    Public Shared Property Count as Integer = 0

    +1

    Public Sub Constructor()
      Class1.Count = Class1.Count +1
    End Sub
    
    -1
    Public Sub Destructor()
      Class1.Count = Class1.Count -1
    End Sub

    test

    Var a As New Class1
    Var b As New Class1
    Var c As New Class1
    Var d As New Class1
    d = Nil
    
    System.DebugLog Class1.Count.ToString
  10. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    Thanks for that Markus. I understand what you said now.

    I restarted the IDE (which is on Linux) and the problem seems to have stopped. Probably I missed a step somewhere...

    The slow leak is still there though so I'll keep hunting!

  11. Markus R

    Jun 29 Testers, Xojo Pro Europe / Germany / Lower Saxon...
    Edited 5 days ago

    for this i add a extra debug window so i can see what happend and check the object count after i clicked something.
    based on the example at Runtime.

  12. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    Nope I was wrong, I didn't rem something out making me think it was all OK. The issue is real and nil object seems to be always detected at the top of the out-most loop - never switches context deeper in. So hopefully that's all I need to test? If I used the shared property method instead of nil that would still not answer the question of where the test/s need to be done to detect the context switch...

  13. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 5 days ago

    OK, here's a workaround:

    At the end of the Thread's run method:

    Exception Err as NilObjectException
    if Obj <> Nil then
       stdErr.write "Something went wrong."
    else
      stdErr.write "It's OK the object wasn't there anyway."
    end if

    And just to be sure, before each CriticalSection entry:

    if Obj = nil then
      raise new NilObjectException
    end if

    This won't work in the Linux IDE (it doesn't do Try/Catch either) but will when compiled and run in a terminal.

    Edit: Drats, no luck! Memory still leaks.

  14. James S

    Jun 29 Testers, Xojo Pro

    I did some testing some time ago and the overhead for getting the object value of a weak ref is very low. So that may not actually be a problem. Once you resolve the weakRef though now you have a real reference to the object and it won’t go out of scope for the duration of the loop. If you just want to make sure that the object stays in scope then setting it to a local variable at the top of the function will do that. If you want it to be able to go out of scope somewhere in the middle as the user closes something or cancels something and then stop your loop then you really don’t have any choice but to release the local reference once in a while so that it even can go out of scope.

    You don’t need to nil the local reference every single time through the loop if the cost of resolving the ref is too high. You could keep the local variable for some number of iterations of the loop and then say every 100 times through your loop you could resolve it, yield to next thread so that it would go out of scope if it was going to, and then try to resolve the weak ref again. If it returns nil there then you know the object is gone and you can just exit your thread.

  15. Eric W

    Jun 29 Testers, Xojo Pro

    Thanks James, my work around didn't work so I'll give what you and Christian say a go.

  16. James S

    Jun 29 Testers, Xojo Pro

    Actually, as I think about that, I wouldn’t let the object going out of scope be the sign that you should stop servicing it. Obviously I don’t know what you’re really doing so this may not be applicable at all and you may have to do it that way. I would just add a local variable to the class for “deleted” or “stopProcessing” or something and then keep a real reference to the object in my thread. When you need the thread to stop processing the object set that variable to true and check that at the top of each loop in the thread and know then to drop out. That way you know the object won’t disappear while you’re working on it, but you can specifically know that it is supposed to be let go and give it up at some point in the loop that makes sense.

  17. Eric W

    Jun 29 Testers, Xojo Pro
    Edited 4 days ago

    Assigning the object to a local variable did make a difference, since the IDE picked up a nil object exception on the object's CriticalSection property that it did not detect before. This is a good sign that the object's properties are now within the scope of the thread's error checking for nil objects. I will have to debug that issue before I can see if the memory leak is resolved by the change of scope also.

    Thanks for your help everyone. Will let you know how I go tomorrow...

or Sign Up to reply!