Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » GEF » Infinite loop in DeferredUpdateManager strikes back
Infinite loop in DeferredUpdateManager strikes back [message #218761] Thu, 29 June 2006 10:59 Go to next message
Eclipse UserFriend
Originally posted by: cricard.businessobjects.com

Hi all,

It seems that the subject was already raised by Jason Woods in 2003.
To summarize, this happens when there is a cyclic dependency in figure
bounds.
In fact, according to Randy:

"For example, layer X's bounds are a function of its children's bounds,
while the children are being placed according to layer X's bounds, which
causes layer X to update its bounds, which causes the children to move, etc.
Other types of cycles might involve connection anchors, etc."

Well, I experienced that yesterday, which certainly means there's something
rotten in my kingdom - uh, sorry, software.
I know I'll have to sort that out somehow, but in the meantime, I found a
workaround which only involves swapping *two* lines in
DeferredUpdateManager:

public synchronized void addInvalidFigure(IFigure f) {
1: if (invalidFigures.contains(f))
2: return;
3: queueWork(); // Possibly reentrancy in (1), with an unchanged
'invalidFigures' => infinite loop since (3) will be re-executed
4: invalidFigures.add(f);
}

If we swap lines (3) and (4) everything works fine.
Well, this fixes the consequences, not the cause (shame on me), but I
thought it would be better than nothing.

As for the technical part - for those of you who are interested - all you
have to do is extend LightweightSystem so it returns your own version of
DeferredUpdateManager.
Unfortunately, many members are private with no accessors ('invalidFigures'
in DeferredUpdateManager, and 'manager' in LightweightSystem for example),
so you'll have to duplicate them in your extended classes. Booo-oooo :/

Randy, am I completely wide of the mark when I suggest to swap those two
lines, or is there a technical reason why addInvalidFigure() is coded this
way?
If not, the same issue might arise in queueWork(), which is reentrant in
some cases, as stated above:

protected void queueWork() {
1: if (!updateQueued) {
2: sendUpdateRequest();
3: updateQueued = true;
}
}

Thus, lines (2) and (3) might be swapped as well with apparently no
particular side-effect.

Cheers,

Christophe.
Re: Infinite loop in DeferredUpdateManager strikes back [message #218771 is a reply to message #218761] Thu, 29 June 2006 13:32 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: none.us.ibm.com

I'm afraid I don't understand the reentrancy issue. How does calling
queueWork() invoke addInvalidFigure()? Or, same question for
sendUpdateRequest(). Reentrant implies that the method is called while it is
already on the stack, so a stack trace would show the method twice.

Have you overridden queueWork()?

"cricri" <cricard@businessobjects.com> wrote in message
news:e80bqq$947$1@utils.eclipse.org...
> Hi all,
>
> It seems that the subject was already raised by Jason Woods in 2003.
> To summarize, this happens when there is a cyclic dependency in figure
> bounds.
> In fact, according to Randy:
>
> "For example, layer X's bounds are a function of its children's bounds,
> while the children are being placed according to layer X's bounds, which
> causes layer X to update its bounds, which causes the children to move,
> etc.
> Other types of cycles might involve connection anchors, etc."
>
> Well, I experienced that yesterday, which certainly means there's
> something rotten in my kingdom - uh, sorry, software.
> I know I'll have to sort that out somehow, but in the meantime, I found a
> workaround which only involves swapping *two* lines in
> DeferredUpdateManager:
>
> public synchronized void addInvalidFigure(IFigure f) {
> 1: if (invalidFigures.contains(f))
> 2: return;
> 3: queueWork(); // Possibly reentrancy in (1), with an unchanged
> 'invalidFigures' => infinite loop since (3) will be re-executed
> 4: invalidFigures.add(f);
> }
>
> If we swap lines (3) and (4) everything works fine.
> Well, this fixes the consequences, not the cause (shame on me), but I
> thought it would be better than nothing.
>
> As for the technical part - for those of you who are interested - all you
> have to do is extend LightweightSystem so it returns your own version of
> DeferredUpdateManager.
> Unfortunately, many members are private with no accessors
> ('invalidFigures' in DeferredUpdateManager, and 'manager' in
> LightweightSystem for example), so you'll have to duplicate them in your
> extended classes. Booo-oooo :/
>
> Randy, am I completely wide of the mark when I suggest to swap those two
> lines, or is there a technical reason why addInvalidFigure() is coded this
> way?
> If not, the same issue might arise in queueWork(), which is reentrant in
> some cases, as stated above:
>
> protected void queueWork() {
> 1: if (!updateQueued) {
> 2: sendUpdateRequest();
> 3: updateQueued = true;
> }
> }
>
> Thus, lines (2) and (3) might be swapped as well with apparently no
> particular side-effect.
>
> Cheers,
>
> Christophe.
>
>
>
Re: Infinite loop in DeferredUpdateManager strikes back [message #218819 is a reply to message #218771] Mon, 03 July 2006 12:27 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: cricard.businessobjects.com

Well, after giving it a second look, I must admit it looks like
reentrancy... but isn't actually.
What happens is the following: at some point, addInvalidFigure() is called
forever with LightweightSystem$RootFigure as parameter.
After tracing a bit, I discovered addInvalidFigure() got called indirectly
by performValidation(), which is responsible for calling
invalidFigures.clear():

public void performValidation() {
if (invalidFigures.isEmpty())
return;

try {
IFigure fig;

fireValidating();
for (int i = 0; i < invalidFigures.size(); i++) {
fig = (IFigure) invalidFigures.get(i);
invalidFigures.set(i, null);
fig.validate(); // Modifies (increments)
invalidFigures.size() (which is re-evaluated at each loop) because
addInvalidFigure() is called. Thus the sup bound is never reached...
}
}
finally {
invalidFigures.clear(); //... and this is never called
}
}

Eventually, we end up with an 'invalidFigures' field in DeferredManager
which keeps growing, and contains only nulls... but for an entry which is
LightweightSystem$RootFigure.
Thus, neither addInvalidFigure() nor performValidation() are reentrant
stricto sensu, but the unwanted calls to addInvalidFigure() produce the same
effects.

Hope it helps...

"Randy Hudson" <none@us.ibm.com> wrote in message
news:e80kou$36k$1@utils.eclipse.org...
> I'm afraid I don't understand the reentrancy issue. How does calling
> queueWork() invoke addInvalidFigure()? Or, same question for
> sendUpdateRequest(). Reentrant implies that the method is called while it
> is already on the stack, so a stack trace would show the method twice.
>
> Have you overridden queueWork()?
>
> "cricri" <cricard@businessobjects.com> wrote in message
> news:e80bqq$947$1@utils.eclipse.org...
>> Hi all,
>>
>> It seems that the subject was already raised by Jason Woods in 2003.
>> To summarize, this happens when there is a cyclic dependency in figure
>> bounds.
>> In fact, according to Randy:
>>
>> "For example, layer X's bounds are a function of its children's bounds,
>> while the children are being placed according to layer X's bounds, which
>> causes layer X to update its bounds, which causes the children to move,
>> etc.
>> Other types of cycles might involve connection anchors, etc."
>>
>> Well, I experienced that yesterday, which certainly means there's
>> something rotten in my kingdom - uh, sorry, software.
>> I know I'll have to sort that out somehow, but in the meantime, I found a
>> workaround which only involves swapping *two* lines in
>> DeferredUpdateManager:
>>
>> public synchronized void addInvalidFigure(IFigure f) {
>> 1: if (invalidFigures.contains(f))
>> 2: return;
>> 3: queueWork(); // Possibly reentrancy in (1), with an unchanged
>> 'invalidFigures' => infinite loop since (3) will be re-executed
>> 4: invalidFigures.add(f);
>> }
>>
>> If we swap lines (3) and (4) everything works fine.
>> Well, this fixes the consequences, not the cause (shame on me), but I
>> thought it would be better than nothing.
>>
>> As for the technical part - for those of you who are interested - all you
>> have to do is extend LightweightSystem so it returns your own version of
>> DeferredUpdateManager.
>> Unfortunately, many members are private with no accessors
>> ('invalidFigures' in DeferredUpdateManager, and 'manager' in
>> LightweightSystem for example), so you'll have to duplicate them in your
>> extended classes. Booo-oooo :/
>>
>> Randy, am I completely wide of the mark when I suggest to swap those two
>> lines, or is there a technical reason why addInvalidFigure() is coded
>> this way?
>> If not, the same issue might arise in queueWork(), which is reentrant in
>> some cases, as stated above:
>>
>> protected void queueWork() {
>> 1: if (!updateQueued) {
>> 2: sendUpdateRequest();
>> 3: updateQueued = true;
>> }
>> }
>>
>> Thus, lines (2) and (3) might be swapped as well with apparently no
>> particular side-effect.
>>
>> Cheers,
>>
>> Christophe.
>>
>>
>>
>
>
Re: Infinite loop in DeferredUpdateManager strikes back [message #218904 is a reply to message #218819] Tue, 04 July 2006 14:25 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: none.us.ibm.com

So, you have a layout that never terminates. This is usually the result of a
bug in some layout code, where some part of layout/validation is looking at
the current state of the figure. Validation/layout need to be predictable
and reproducible, so it should not be a function of the current state of the
locations. Look for calls to getSize instead of getPreferredSize, etc.

This can also happen when mixing freeform and non-freeform layers, or when
layers are bouncing between two states such as with and without scrollbars.

"cricri" <cricard@businessobjects.com> wrote in message
news:e8b2eo$kc7$1@utils.eclipse.org...
> Well, after giving it a second look, I must admit it looks like
> reentrancy... but isn't actually.
> What happens is the following: at some point, addInvalidFigure() is called
> forever with LightweightSystem$RootFigure as parameter.
> After tracing a bit, I discovered addInvalidFigure() got called indirectly
> by performValidation(), which is responsible for calling
> invalidFigures.clear():
>
> public void performValidation() {
> if (invalidFigures.isEmpty())
> return;
>
> try {
> IFigure fig;
>
> fireValidating();
> for (int i = 0; i < invalidFigures.size(); i++) {
> fig = (IFigure) invalidFigures.get(i);
> invalidFigures.set(i, null);
> fig.validate(); // Modifies (increments)
> invalidFigures.size() (which is re-evaluated at each loop) because
> addInvalidFigure() is called. Thus the sup bound is never reached...
> }
> }
> finally {
> invalidFigures.clear(); //... and this is never called
> }
> }
>
> Eventually, we end up with an 'invalidFigures' field in DeferredManager
> which keeps growing, and contains only nulls... but for an entry which is
> LightweightSystem$RootFigure.
> Thus, neither addInvalidFigure() nor performValidation() are reentrant
> stricto sensu, but the unwanted calls to addInvalidFigure() produce the
> same effects.
>
> Hope it helps...
>
> "Randy Hudson" <none@us.ibm.com> wrote in message
> news:e80kou$36k$1@utils.eclipse.org...
>> I'm afraid I don't understand the reentrancy issue. How does calling
>> queueWork() invoke addInvalidFigure()? Or, same question for
>> sendUpdateRequest(). Reentrant implies that the method is called while it
>> is already on the stack, so a stack trace would show the method twice.
>>
>> Have you overridden queueWork()?
>>
>> "cricri" <cricard@businessobjects.com> wrote in message
>> news:e80bqq$947$1@utils.eclipse.org...
>>> Hi all,
>>>
>>> It seems that the subject was already raised by Jason Woods in 2003.
>>> To summarize, this happens when there is a cyclic dependency in figure
>>> bounds.
>>> In fact, according to Randy:
>>>
>>> "For example, layer X's bounds are a function of its children's bounds,
>>> while the children are being placed according to layer X's bounds, which
>>> causes layer X to update its bounds, which causes the children to move,
>>> etc.
>>> Other types of cycles might involve connection anchors, etc."
>>>
>>> Well, I experienced that yesterday, which certainly means there's
>>> something rotten in my kingdom - uh, sorry, software.
>>> I know I'll have to sort that out somehow, but in the meantime, I found
>>> a workaround which only involves swapping *two* lines in
>>> DeferredUpdateManager:
>>>
>>> public synchronized void addInvalidFigure(IFigure f) {
>>> 1: if (invalidFigures.contains(f))
>>> 2: return;
>>> 3: queueWork(); // Possibly reentrancy in (1), with an unchanged
>>> 'invalidFigures' => infinite loop since (3) will be re-executed
>>> 4: invalidFigures.add(f);
>>> }
>>>
>>> If we swap lines (3) and (4) everything works fine.
>>> Well, this fixes the consequences, not the cause (shame on me), but I
>>> thought it would be better than nothing.
>>>
>>> As for the technical part - for those of you who are interested - all
>>> you have to do is extend LightweightSystem so it returns your own
>>> version of DeferredUpdateManager.
>>> Unfortunately, many members are private with no accessors
>>> ('invalidFigures' in DeferredUpdateManager, and 'manager' in
>>> LightweightSystem for example), so you'll have to duplicate them in your
>>> extended classes. Booo-oooo :/
>>>
>>> Randy, am I completely wide of the mark when I suggest to swap those two
>>> lines, or is there a technical reason why addInvalidFigure() is coded
>>> this way?
>>> If not, the same issue might arise in queueWork(), which is reentrant in
>>> some cases, as stated above:
>>>
>>> protected void queueWork() {
>>> 1: if (!updateQueued) {
>>> 2: sendUpdateRequest();
>>> 3: updateQueued = true;
>>> }
>>> }
>>>
>>> Thus, lines (2) and (3) might be swapped as well with apparently no
>>> particular side-effect.
>>>
>>> Cheers,
>>>
>>> Christophe.
>>>
>>>
>>>
>>
>>
>
>
Re: Infinite loop in DeferredUpdateManager strikes back [message #218935 is a reply to message #218904] Wed, 05 July 2006 09:48 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: cricard.businessobjects.com

Well, yeah, I had already understood I deserved to be terminated with
extreme prejudice because I had somehow introduced an update cycle :D
I just wanted to point out that by swapping two lines of code in
DeferredUpdateManager, clumsy developers of my ilk might never experience
unwanted infinite loops... unless of course we all agree it builds character
;)

Anyway, thanks for taking the time to answer, and thus give me a better
understanding of what happened under the hood!

"Randy Hudson" <none@us.ibm.com> wrote in message
news:e8dtpu$sdb$1@utils.eclipse.org...
> So, you have a layout that never terminates. This is usually the result of
> a bug in some layout code, where some part of layout/validation is looking
> at the current state of the figure. Validation/layout need to be
> predictable and reproducible, so it should not be a function of the
> current state of the locations. Look for calls to getSize instead of
> getPreferredSize, etc.
>
> This can also happen when mixing freeform and non-freeform layers, or when
> layers are bouncing between two states such as with and without
> scrollbars.
>
> "cricri" <cricard@businessobjects.com> wrote in message
> news:e8b2eo$kc7$1@utils.eclipse.org...
>> Well, after giving it a second look, I must admit it looks like
>> reentrancy... but isn't actually.
>> What happens is the following: at some point, addInvalidFigure() is
>> called forever with LightweightSystem$RootFigure as parameter.
>> After tracing a bit, I discovered addInvalidFigure() got called
>> indirectly by performValidation(), which is responsible for calling
>> invalidFigures.clear():
>>
>> public void performValidation() {
>> if (invalidFigures.isEmpty())
>> return;
>>
>> try {
>> IFigure fig;
>>
>> fireValidating();
>> for (int i = 0; i < invalidFigures.size(); i++) {
>> fig = (IFigure) invalidFigures.get(i);
>> invalidFigures.set(i, null);
>> fig.validate(); // Modifies (increments)
>> invalidFigures.size() (which is re-evaluated at each loop) because
>> addInvalidFigure() is called. Thus the sup bound is never reached...
>> }
>> }
>> finally {
>> invalidFigures.clear(); //... and this is never called
>> }
>> }
>>
>> Eventually, we end up with an 'invalidFigures' field in DeferredManager
>> which keeps growing, and contains only nulls... but for an entry which is
>> LightweightSystem$RootFigure.
>> Thus, neither addInvalidFigure() nor performValidation() are reentrant
>> stricto sensu, but the unwanted calls to addInvalidFigure() produce the
>> same effects.
>>
>> Hope it helps...
>>
>> "Randy Hudson" <none@us.ibm.com> wrote in message
>> news:e80kou$36k$1@utils.eclipse.org...
>>> I'm afraid I don't understand the reentrancy issue. How does calling
>>> queueWork() invoke addInvalidFigure()? Or, same question for
>>> sendUpdateRequest(). Reentrant implies that the method is called while
>>> it is already on the stack, so a stack trace would show the method
>>> twice.
>>>
>>> Have you overridden queueWork()?
>>>
>>> "cricri" <cricard@businessobjects.com> wrote in message
>>> news:e80bqq$947$1@utils.eclipse.org...
>>>> Hi all,
>>>>
>>>> It seems that the subject was already raised by Jason Woods in 2003.
>>>> To summarize, this happens when there is a cyclic dependency in figure
>>>> bounds.
>>>> In fact, according to Randy:
>>>>
>>>> "For example, layer X's bounds are a function of its children's bounds,
>>>> while the children are being placed according to layer X's bounds,
>>>> which causes layer X to update its bounds, which causes the children to
>>>> move, etc.
>>>> Other types of cycles might involve connection anchors, etc."
>>>>
>>>> Well, I experienced that yesterday, which certainly means there's
>>>> something rotten in my kingdom - uh, sorry, software.
>>>> I know I'll have to sort that out somehow, but in the meantime, I found
>>>> a workaround which only involves swapping *two* lines in
>>>> DeferredUpdateManager:
>>>>
>>>> public synchronized void addInvalidFigure(IFigure f) {
>>>> 1: if (invalidFigures.contains(f))
>>>> 2: return;
>>>> 3: queueWork(); // Possibly reentrancy in (1), with an unchanged
>>>> 'invalidFigures' => infinite loop since (3) will be re-executed
>>>> 4: invalidFigures.add(f);
>>>> }
>>>>
>>>> If we swap lines (3) and (4) everything works fine.
>>>> Well, this fixes the consequences, not the cause (shame on me), but I
>>>> thought it would be better than nothing.
>>>>
>>>> As for the technical part - for those of you who are interested - all
>>>> you have to do is extend LightweightSystem so it returns your own
>>>> version of DeferredUpdateManager.
>>>> Unfortunately, many members are private with no accessors
>>>> ('invalidFigures' in DeferredUpdateManager, and 'manager' in
>>>> LightweightSystem for example), so you'll have to duplicate them in
>>>> your extended classes. Booo-oooo :/
>>>>
>>>> Randy, am I completely wide of the mark when I suggest to swap those
>>>> two lines, or is there a technical reason why addInvalidFigure() is
>>>> coded this way?
>>>> If not, the same issue might arise in queueWork(), which is reentrant
>>>> in some cases, as stated above:
>>>>
>>>> protected void queueWork() {
>>>> 1: if (!updateQueued) {
>>>> 2: sendUpdateRequest();
>>>> 3: updateQueued = true;
>>>> }
>>>> }
>>>>
>>>> Thus, lines (2) and (3) might be swapped as well with apparently no
>>>> particular side-effect.
>>>>
>>>> Cheers,
>>>>
>>>> Christophe.
>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>
Re: Infinite loop in DeferredUpdateManager strikes back [message #218977 is a reply to message #218935] Wed, 05 July 2006 14:50 Go to previous messageGo to next message
Eclipse UserFriend
Originally posted by: none.us.ibm.com

Terminated? I was still trying to help you find the source of the bug. As
far as swapping the lines of code, I didn't even understand how those lines
interacted with each other. queueWork does nothing and returns.
sendUpdateRequest() also does basically nothing and returns. They both
operate asynchronously. I don't see how they are affected by the fields that
are touched before or after the method call, unless you've overridden one of
those methods to by synchronous.

There are valid scenarioes where addInvalidFigure does get called a few
times during validation for the same figure (generally the root figure). For
example, if Layer 1 has a figure that follows another figure on Layer 2 (on
top of 1). If the "tracked" figure moves, layer 1 needs to revalidate to
position whatever might be following it.

"cricri" <cricard@businessobjects.com> wrote in message
news:e8g1u9$gb4$1@utils.eclipse.org...
> Well, yeah, I had already understood I deserved to be terminated with
> extreme prejudice because I had somehow introduced an update cycle :D
> I just wanted to point out that by swapping two lines of code in
> DeferredUpdateManager, clumsy developers of my ilk might never experience
> unwanted infinite loops... unless of course we all agree it builds
> character ;)
>
> Anyway, thanks for taking the time to answer, and thus give me a better
> understanding of what happened under the hood!
>
> "Randy Hudson" <none@us.ibm.com> wrote in message
> news:e8dtpu$sdb$1@utils.eclipse.org...
>> So, you have a layout that never terminates. This is usually the result
>> of a bug in some layout code, where some part of layout/validation is
>> looking at the current state of the figure. Validation/layout need to be
>> predictable and reproducible, so it should not be a function of the
>> current state of the locations. Look for calls to getSize instead of
>> getPreferredSize, etc.
>>
>> This can also happen when mixing freeform and non-freeform layers, or
>> when layers are bouncing between two states such as with and without
>> scrollbars.
>>
>> "cricri" <cricard@businessobjects.com> wrote in message
>> news:e8b2eo$kc7$1@utils.eclipse.org...
>>> Well, after giving it a second look, I must admit it looks like
>>> reentrancy... but isn't actually.
>>> What happens is the following: at some point, addInvalidFigure() is
>>> called forever with LightweightSystem$RootFigure as parameter.
>>> After tracing a bit, I discovered addInvalidFigure() got called
>>> indirectly by performValidation(), which is responsible for calling
>>> invalidFigures.clear():
>>>
>>> public void performValidation() {
>>> if (invalidFigures.isEmpty())
>>> return;
>>>
>>> try {
>>> IFigure fig;
>>>
>>> fireValidating();
>>> for (int i = 0; i < invalidFigures.size(); i++) {
>>> fig = (IFigure) invalidFigures.get(i);
>>> invalidFigures.set(i, null);
>>> fig.validate(); // Modifies (increments)
>>> invalidFigures.size() (which is re-evaluated at each loop) because
>>> addInvalidFigure() is called. Thus the sup bound is never reached...
>>> }
>>> }
>>> finally {
>>> invalidFigures.clear(); //... and this is never called
>>> }
>>> }
>>>
>>> Eventually, we end up with an 'invalidFigures' field in DeferredManager
>>> which keeps growing, and contains only nulls... but for an entry which
>>> is LightweightSystem$RootFigure.
>>> Thus, neither addInvalidFigure() nor performValidation() are reentrant
>>> stricto sensu, but the unwanted calls to addInvalidFigure() produce the
>>> same effects.
>>>
>>> Hope it helps...
>>>
>>> "Randy Hudson" <none@us.ibm.com> wrote in message
>>> news:e80kou$36k$1@utils.eclipse.org...
>>>> I'm afraid I don't understand the reentrancy issue. How does calling
>>>> queueWork() invoke addInvalidFigure()? Or, same question for
>>>> sendUpdateRequest(). Reentrant implies that the method is called while
>>>> it is already on the stack, so a stack trace would show the method
>>>> twice.
>>>>
>>>> Have you overridden queueWork()?
>>>>
>>>> "cricri" <cricard@businessobjects.com> wrote in message
>>>> news:e80bqq$947$1@utils.eclipse.org...
>>>>> Hi all,
>>>>>
>>>>> It seems that the subject was already raised by Jason Woods in 2003.
>>>>> To summarize, this happens when there is a cyclic dependency in figure
>>>>> bounds.
>>>>> In fact, according to Randy:
>>>>>
>>>>> "For example, layer X's bounds are a function of its children's
>>>>> bounds, while the children are being placed according to layer X's
>>>>> bounds, which causes layer X to update its bounds, which causes the
>>>>> children to move, etc.
>>>>> Other types of cycles might involve connection anchors, etc."
>>>>>
>>>>> Well, I experienced that yesterday, which certainly means there's
>>>>> something rotten in my kingdom - uh, sorry, software.
>>>>> I know I'll have to sort that out somehow, but in the meantime, I
>>>>> found a workaround which only involves swapping *two* lines in
>>>>> DeferredUpdateManager:
>>>>>
>>>>> public synchronized void addInvalidFigure(IFigure f) {
>>>>> 1: if (invalidFigures.contains(f))
>>>>> 2: return;
>>>>> 3: queueWork(); // Possibly reentrancy in (1), with an
>>>>> unchanged 'invalidFigures' => infinite loop since (3) will be
>>>>> re-executed
>>>>> 4: invalidFigures.add(f);
>>>>> }
>>>>>
>>>>> If we swap lines (3) and (4) everything works fine.
>>>>> Well, this fixes the consequences, not the cause (shame on me), but I
>>>>> thought it would be better than nothing.
>>>>>
>>>>> As for the technical part - for those of you who are interested - all
>>>>> you have to do is extend LightweightSystem so it returns your own
>>>>> version of DeferredUpdateManager.
>>>>> Unfortunately, many members are private with no accessors
>>>>> ('invalidFigures' in DeferredUpdateManager, and 'manager' in
>>>>> LightweightSystem for example), so you'll have to duplicate them in
>>>>> your extended classes. Booo-oooo :/
>>>>>
>>>>> Randy, am I completely wide of the mark when I suggest to swap those
>>>>> two lines, or is there a technical reason why addInvalidFigure() is
>>>>> coded this way?
>>>>> If not, the same issue might arise in queueWork(), which is reentrant
>>>>> in some cases, as stated above:
>>>>>
>>>>> protected void queueWork() {
>>>>> 1: if (!updateQueued) {
>>>>> 2: sendUpdateRequest();
>>>>> 3: updateQueued = true;
>>>>> }
>>>>> }
>>>>>
>>>>> Thus, lines (2) and (3) might be swapped as well with apparently no
>>>>> particular side-effect.
>>>>>
>>>>> Cheers,
>>>>>
>>>>> Christophe.
>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>
Re: Infinite loop in DeferredUpdateManager strikes back [message #218983 is a reply to message #218977] Wed, 05 July 2006 16:26 Go to previous message
Eclipse UserFriend
Originally posted by: cricard.businessobjects.com

Whooops, sorry, no offense meant.
I was just kidding when I said I deserved to be terminated for my
clumsiness... generally speaking, and definitely not by you in particular.
Ahhhh, when English is not one's native language...

However I assure you that swapping both lines does the trick, although I'm
as dumbfounded as you by such a positive result.
And no, I hadn't overridden anything *before* to make things work
synchronously :/

But I can manage to describe precisely what I did *after* to open a document
that couldn't be opened otherwise (due to the infinite loop):

1- Overrode the following method in my viewer:

protected LightweightSystem createLightweightSystem() {
return new MyLWS();
}

2- Extended LightweightSystem so as to return my own version of
DeferredUpdateManager:

class MyLWS extends LightweightSystem {
private UpdateManager manager; // Darn, there's already a private
one in DeferredUpdateManager

public MyLWS() {
super();
}

public void setUpdateManager(UpdateManager um) {
manager = um;
manager.setRoot(getRootFigure());
super.setUpdateManager(manager);
}

public UpdateManager getUpdateManager() {
if (manager == null)
setUpdateManager(new MyDeferredUpdateManager());
return manager;
}
}

3- Extended DeferredUpdateManager so as to override addInvalidFigure(), and
test the fix I had figured out:

class MyDeferredUpdateManager extends DeferredUpdateManager {
private ArrayList<IFigure> invalidFigures = new
ArrayList<IFigure>(); // Darn, there's already a private one in
DeferredUpdateManager

public MyDeferredUpdateManager() {
super();
}

@Override
public void addInvalidFigure(IFigure figure) {
if (invalidFigures.contains(figure)) // (Swap) First test
figure...
return;
invalidFigures.add(figure); // (Swap) ...then
add it to the list
super.addInvalidFigure(figure);
}

@Override
public void performValidation() { // Also override this
one...
super.performValidation();
invalidFigures.clear(); // ...because we
need to clear our own private copy
}
}

And, once again, please accept my humblest apologies for the previous
misunderstanding.

"Randy Hudson" <none@us.ibm.com> wrote in message
news:e8gjjt$9pd$1@utils.eclipse.org...
> Terminated? I was still trying to help you find the source of the bug. As
> far as swapping the lines of code, I didn't even understand how those
> lines interacted with each other. queueWork does nothing and returns.
> sendUpdateRequest() also does basically nothing and returns. They both
> operate asynchronously. I don't see how they are affected by the fields
> that are touched before or after the method call, unless you've overridden
> one of those methods to by synchronous.
>
> There are valid scenarioes where addInvalidFigure does get called a few
> times during validation for the same figure (generally the root figure).
> For example, if Layer 1 has a figure that follows another figure on Layer
> 2 (on top of 1). If the "tracked" figure moves, layer 1 needs to
> revalidate to position whatever might be following it.
>
> "cricri" <cricard@businessobjects.com> wrote in message
> news:e8g1u9$gb4$1@utils.eclipse.org...
>> Well, yeah, I had already understood I deserved to be terminated with
>> extreme prejudice because I had somehow introduced an update cycle :D
>> I just wanted to point out that by swapping two lines of code in
>> DeferredUpdateManager, clumsy developers of my ilk might never experience
>> unwanted infinite loops... unless of course we all agree it builds
>> character ;)
>>
>> Anyway, thanks for taking the time to answer, and thus give me a better
>> understanding of what happened under the hood!
>>
>> "Randy Hudson" <none@us.ibm.com> wrote in message
>> news:e8dtpu$sdb$1@utils.eclipse.org...
>>> So, you have a layout that never terminates. This is usually the result
>>> of a bug in some layout code, where some part of layout/validation is
>>> looking at the current state of the figure. Validation/layout need to be
>>> predictable and reproducible, so it should not be a function of the
>>> current state of the locations. Look for calls to getSize instead of
>>> getPreferredSize, etc.
>>>
>>> This can also happen when mixing freeform and non-freeform layers, or
>>> when layers are bouncing between two states such as with and without
>>> scrollbars.
>>>
>>> "cricri" <cricard@businessobjects.com> wrote in message
>>> news:e8b2eo$kc7$1@utils.eclipse.org...
>>>> Well, after giving it a second look, I must admit it looks like
>>>> reentrancy... but isn't actually.
>>>> What happens is the following: at some point, addInvalidFigure() is
>>>> called forever with LightweightSystem$RootFigure as parameter.
>>>> After tracing a bit, I discovered addInvalidFigure() got called
>>>> indirectly by performValidation(), which is responsible for calling
>>>> invalidFigures.clear():
>>>>
>>>> public void performValidation() {
>>>> if (invalidFigures.isEmpty())
>>>> return;
>>>>
>>>> try {
>>>> IFigure fig;
>>>>
>>>> fireValidating();
>>>> for (int i = 0; i < invalidFigures.size(); i++) {
>>>> fig = (IFigure) invalidFigures.get(i);
>>>> invalidFigures.set(i, null);
>>>> fig.validate(); // Modifies (increments)
>>>> invalidFigures.size() (which is re-evaluated at each loop) because
>>>> addInvalidFigure() is called. Thus the sup bound is never reached...
>>>> }
>>>> }
>>>> finally {
>>>> invalidFigures.clear(); //... and this is never called
>>>> }
>>>> }
>>>>
>>>> Eventually, we end up with an 'invalidFigures' field in DeferredManager
>>>> which keeps growing, and contains only nulls... but for an entry which
>>>> is LightweightSystem$RootFigure.
>>>> Thus, neither addInvalidFigure() nor performValidation() are reentrant
>>>> stricto sensu, but the unwanted calls to addInvalidFigure() produce the
>>>> same effects.
>>>>
>>>> Hope it helps...
>>>>
>>>> "Randy Hudson" <none@us.ibm.com> wrote in message
>>>> news:e80kou$36k$1@utils.eclipse.org...
>>>>> I'm afraid I don't understand the reentrancy issue. How does calling
>>>>> queueWork() invoke addInvalidFigure()? Or, same question for
>>>>> sendUpdateRequest(). Reentrant implies that the method is called while
>>>>> it is already on the stack, so a stack trace would show the method
>>>>> twice.
>>>>>
>>>>> Have you overridden queueWork()?
>>>>>
>>>>> "cricri" <cricard@businessobjects.com> wrote in message
>>>>> news:e80bqq$947$1@utils.eclipse.org...
>>>>>> Hi all,
>>>>>>
>>>>>> It seems that the subject was already raised by Jason Woods in 2003.
>>>>>> To summarize, this happens when there is a cyclic dependency in
>>>>>> figure bounds.
>>>>>> In fact, according to Randy:
>>>>>>
>>>>>> "For example, layer X's bounds are a function of its children's
>>>>>> bounds, while the children are being placed according to layer X's
>>>>>> bounds, which causes layer X to update its bounds, which causes the
>>>>>> children to move, etc.
>>>>>> Other types of cycles might involve connection anchors, etc."
>>>>>>
>>>>>> Well, I experienced that yesterday, which certainly means there's
>>>>>> something rotten in my kingdom - uh, sorry, software.
>>>>>> I know I'll have to sort that out somehow, but in the meantime, I
>>>>>> found a workaround which only involves swapping *two* lines in
>>>>>> DeferredUpdateManager:
>>>>>>
>>>>>> public synchronized void addInvalidFigure(IFigure f) {
>>>>>> 1: if (invalidFigures.contains(f))
>>>>>> 2: return;
>>>>>> 3: queueWork(); // Possibly reentrancy in (1), with an
>>>>>> unchanged 'invalidFigures' => infinite loop since (3) will be
>>>>>> re-executed
>>>>>> 4: invalidFigures.add(f);
>>>>>> }
>>>>>>
>>>>>> If we swap lines (3) and (4) everything works fine.
>>>>>> Well, this fixes the consequences, not the cause (shame on me), but I
>>>>>> thought it would be better than nothing.
>>>>>>
>>>>>> As for the technical part - for those of you who are interested - all
>>>>>> you have to do is extend LightweightSystem so it returns your own
>>>>>> version of DeferredUpdateManager.
>>>>>> Unfortunately, many members are private with no accessors
>>>>>> ('invalidFigures' in DeferredUpdateManager, and 'manager' in
>>>>>> LightweightSystem for example), so you'll have to duplicate them in
>>>>>> your extended classes. Booo-oooo :/
>>>>>>
>>>>>> Randy, am I completely wide of the mark when I suggest to swap those
>>>>>> two lines, or is there a technical reason why addInvalidFigure() is
>>>>>> coded this way?
>>>>>> If not, the same issue might arise in queueWork(), which is reentrant
>>>>>> in some cases, as stated above:
>>>>>>
>>>>>> protected void queueWork() {
>>>>>> 1: if (!updateQueued) {
>>>>>> 2: sendUpdateRequest();
>>>>>> 3: updateQueued = true;
>>>>>> }
>>>>>> }
>>>>>>
>>>>>> Thus, lines (2) and (3) might be swapped as well with apparently no
>>>>>> particular side-effect.
>>>>>>
>>>>>> Cheers,
>>>>>>
>>>>>> Christophe.
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>
>>>
>>
>>
>
>
Previous Topic:GridLayout
Next Topic:GEF in RCP
Goto Forum:
  


Current Time: Fri Apr 19 11:56:31 GMT 2024

Powered by FUDForum. Page generated in 0.02309 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top