Some bindings lose connection

May 24, 2013 at 11:06 AM
I have a problem with UpdateControls that in random situations some bindings lose connection in WPF application.

I have a property "Result" in model which is updated every second from timer tick. Model is exposed through ViewModel to WPF View.
"Result" is exposed in two ways:
1) Directly as a single result
2) Indirectly as a element of results list.
Sometimes, especialy when I make a little more performance expensive operation, like change one ViewModel to another, paint a chart etc., then single result ("directly way") stop changing, but result on list of results (dataGrid) is still changed ("indirectly way").

When I check in Debbuger Watch window in correct state _result.IndependentSentry.UsedBy has 5 dependents (+self). (probably - Directly: result, result per minute, ListElement: description, result, results per minute)
When I check the same in incorrect state has only 3 dependents (+self) - only ListElement (...).

To be clearly understood:
"Directly way" means:
View (uses) ViewModel (uses) Model.Result
View (uses) ViewModel (uses) Model.ResultPerMinute (uses) Model.Result
"Indirectly way" means:
View (uses) ViewModel (uses) Model.ResultsList.Element (uses):
--- Model.Result
--- Model.ResultPerMinute (uses) Model.Result
--- Model.Description (uses) (Model.Result (and) Model.ResultPerMinute)


Could you give me any tips, how can I debug to see what is happend?
What could be the reason that when processor is a little more used, then some dependents connection/references are lost?
Is there any way to define something (flag, property...) to say UpdateControl or GarbageCollector (i don't know which breaks that bindings) that "this" binding connection can't be remove?
Coordinator
May 24, 2013 at 4:26 PM
This sounds like an issue with weak references. I have a test project that I used to work through these issues, but it doesn't reproduce the problem.

Which version are you running? Which platform? And how long does it take for the issue to arise?

You can try building from source and replacing the weak references with strong ones (Precedent>DependentNode). While that may lead to memory leaks, it would at least tell us if that truly is the problem.
May 24, 2013 at 5:51 PM
I use 2.2.1.1 version, from "NET40" folder, Win7x64, CPU i5 2.40GHz, 4GB RAM. Application use less than 0.5% of CPU, but rarely there are moments up to 5%.

Usually it takes less than 1 minute to arise the issue, especially if I not only observe the GUI, but I use it (as I have written) - then it is rather a few seconds.

What should I change to replace weak references with strong ones?
Coordinator
May 24, 2013 at 6:40 PM
Edited May 24, 2013 at 6:41 PM
First change the WeakReference to a Dependent in Precedent.cs:
    public abstract class Precedent
    {
        internal class DependentNode
        {
            public Dependent Dependent;
            public DependentNode Next;
        }
    }
Then follow the compile errors. One will be the creation of DependentNode. Change it to:
_firstDependent = new DependentNode { Dependent = update, Next = _firstDependent };
Another will be in Delete. Change the condition to:
if (current.Dependent == dependent)
The rest will be Dependent.Target. Change those to just Dependent.


Looking over the code again, I suspect that the problem is WeakReferenceToSelf. Tell me how this data point goes, and we might try another change.
Coordinator
May 24, 2013 at 7:42 PM
Something else to look at is locking around your reads and writes of Model.Result. If you are reading the value to update a dependent on the foreground thread, and you write a new value at the same time on the background thread, then you could mess up the internal data structures. Since you would need to protect shared state yourself anyway, Update Controls does not do its own locking. It assumes that you are doing that.

You mentioned that it is updated from a timer tick. Based on the type of timer you use, this could either be executed on the foreground thread or a thread pool thread.

BTW: I'm running the test right now. It has been going for an hour and a half with no error. If can send me source code that repros the problem, I'll be glad to take a look. A public GitHub repo would be best.
Coordinator
May 27, 2013 at 10:27 PM
Thanks for sending me the repro. I was able to track it down to the WeakReferenceToSelf property.

The Dependent class defined a property called WeakReferenceToSelf that was intended to optimize the use of weak references. This property cached a single WeakReference to the Dependent so that the Precedents would be able to share. This was to save memory and CPU cycles.

Unfortunately, the cached weak reference would sometimes go dead during GC while the Dependent was out of date. When it was brought back up to date, the Predecent would grab the cached dead reference instead of creating a new live one.

I fixed the problem by removing the WeakReferenceToSelf property and letting the Precedent simply create new WeakReferences as needed. This resolve the problem. The optimization that was lost would only become significant once a Dependent has an exceedingly large number of Precedents. But if this occurs, your app has bigger problems than extra WeakReference instances.
Coordinator
May 30, 2013 at 11:47 AM
Thanks for your patience. There was one final issue related to garbage collection. Your observation that the bug manifests while using deep paths really narrowed down the possibilities for me. Please get the latest MSI or NuGet package.

Thanks!
May 31, 2013 at 6:54 AM
I installed new version 2.2.5.0 and test and it looks like the issue is solved. Thanks a lot!
Developer
Aug 21, 2013 at 8:47 PM
Edited Aug 21, 2013 at 8:50 PM
WeakReferenceToSelf was a worthwhile optimization. If I'd been around, I would've suggested the following change to the property instead:
    if (_weakReferenceToSelf == null)
        //_weakReferenceToSelf = new WeakReference(this); // before
        _weakReferenceToSelf = new WeakReference(this, true); // after
That might make it live longer, but I'm not sure how the WeakReference could possibly die when a strong reference still exists, given that Dependent doesn't have a finalizer and neither does anything else in the library (I searched for ~ and found none). If that change didn't work, surely this would:
    if (_weakReferenceToSelf == null || _weakReferenceToSelf.Target == null)
        _weakReferenceToSelf = new WeakReference(this, true);
Coordinator
Aug 21, 2013 at 8:59 PM
I seem to recall trying that first fix. The problem still occurred.

The second fix did not occur to me. That seems like it would work. Then again, I can't explain why I saw dead weak references to an object that was still clearly alive.

I should be able to get the repro working again. I'll try that fix and see if it solves the problem. If so, we can have this optimization back.

It still seems like a micro optimization to me, but this is library code. We have to be good guests to the host application.
Developer
Aug 21, 2013 at 10:03 PM
Edited Aug 21, 2013 at 10:10 PM
Yeah, it seems impossible, as if... well I kinda wonder if somehow the GC eliminates the Dependent just after "return _weakReferenceToSelf" and "by luck" this doesn't happen when you make a new WeakReference. Hard to guess, I am not familiar with the exact environment in which the problem occurs. Did the GC'd Dependent belong to the View or the VM? Doesn't the View hold strong references to its Dependents?

I don't remember the original discussion but let's see, a Dependent often depends on numerous Precedents... sometimes thousands... and the Precedents need weak references to the Dependents, so in apps using UC heavily, the weak references (which must use >=4 words of memory but the true amount is unknown) would consume lots of memory unless consolidated.
Coordinator
Aug 21, 2013 at 10:15 PM
Yes, the view holds a strong reference to the Dependent. The view holds a reference to the wrapper (ObjectInstance<T>) in its DataContext. The wrapper holds references to the PropertyInstanceAtom objects. And they hold references to the Dependents. They are strong references all the way down.

That the Dependent has not been GC'd is further supported by the fact that we have gotten into the code to record a dependency when the cached weak reference was reused. If we had GC'd the Dependent, then that instance should be unreachable.

As for the race condition, there is a strong reference to the Dependent on the stack while we are making the call. That, if nothing else, should prevent the GC from reclaiming it while we are returning the weak reference.
Developer
Aug 21, 2013 at 10:30 PM
Edited Aug 21, 2013 at 10:36 PM
Very mysterious. Under certain conditions a GC can occur when it superficially looks like a reference still exists but the GC decides that it doesn't (a local variable reference is still in scope but the local variable is not used later in the method), hence the existence of GC.KeepAlive(foo). But if there's a strong heap-based reference to the Dependent, it should be impossible for the weak reference to be cleared. Gee, could it be a bona fide bug in the .NET framework?