6

I have only one class with many instances. Every instance is observer of couple of other instances. As well every instance can be observable by couple of another instances.

How to avoid infinite loop of calling update() in observers?

ouflak
  • 2,458
  • 10
  • 44
  • 49
joseph
  • 687
  • 3
  • 12
  • 21

5 Answers5

8

If your system is single threaded, then you simply need a guard inside your notify method:

private boolean _notifying;

public void notify() {
  if(_notifying) {
    return;
  }
  _notifying = true;
  try {
    // ... do notifying here...
  } finally {
    _notifying = false;
  }
}
james
  • 468
  • 2
  • 3
7

What you are looking for is a graph-traversal algorithm that detects cycles. One simple approach (that only works in a single threaded scenario) is to keep a global/static counter to let each top-level update() invocation get a unique identifier. Each observer then tracks whether already it has processed the update with the given identifier (ID) and in that case ignores it. This means that your update method will have to be expanded with a parameter with the ID number of the specific update.

Dave Jarvis
  • 30,436
  • 41
  • 178
  • 315
Anders Abel
  • 67,989
  • 17
  • 150
  • 217
5

Well, if you are defining the "event" object, you could maybe add to it the objects that have processed the event already. In that case, if you close the loop you can exit. In seudo-code

eventFired(Event e)
  if (e.hasBeenEvaluatedBy(this)){
    return;
  }
  e.addEvaluator(this);

  // Do magic

  refire(e);
}

In this case we get something like: * A fires something. * B processes it and adds itself to the list * B refires * C processes event and adds itself to the list * C refires * A processes the event and adds itself to the list * A refires * B catches the event, but is already on the list. No refire, infinite loop broken

Ids could be used instead of pointers to avoid garbage collection issues

Mario Ortegón
  • 18,670
  • 17
  • 71
  • 81
  • I think storing list of visited nodes inside event is a bad idea. There are constant number of nodes which are created once. And storages (i.e. Lists or Sets) are created once in them. But there are lots of new events every moment. And lots of new lists should be created every moment too. – Roman Mar 17 '10 at 18:34
2

Let your observers add received events (or their ids) to some temporary storage and when every new event is received let them verify whether the same event is saved in the storage. If it is then they shouldn't handle it.

But if we'll try to solve a problem rather than find a suitable workaround then: your problem is that object A can listen to object B, and object B can listen to object A at the same time (maybe with some intermediary objects). It's a bad design IMHO.

Observers should be used to loose coupling, making object A know about object B, but not vice versa.

If your objects A and B both know about each other then I don't see why you need to use observers.

Roman
  • 64,384
  • 92
  • 238
  • 332
  • I cant tell anything not knowing the domain. I would probably prefer not oop approach to implement graph (i.e. I would create class Graph and have simple 2d array as an adjacency matrix in it). With this approach it's easier to implement standard graph algorithms (at least for me). – Roman Mar 17 '10 at 18:26
  • Maybe you tell us what are you going to do? – Roman Mar 17 '10 at 18:32
  • How are you going to develop it if you can't describe it? :)) – Roman Mar 17 '10 at 18:35
0

One way is to only fire events like updated if something was actually updated. The benefit of this approach is that you can often reduce the number of events triggered (reducing work) and simplify the code. e.g.

final Set<String> set = ...

public void onAdded(String string) {
   // is only added once.
   if (set.add(string)) {
       // only notifies once no matter how many times onAdded is 
       // called for this string, recursively or not.
       notifyAdded(string);
   }
}
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 1
    That can fail if two objects try to conform themselves to changes in the other, but do so in contradictory ways. For example, when CheckBox1 changes, it notifies CheckBox2, which changes its state to match CheckBox1. When CheckBox2 changes, it notifies CheckBox1, which changes its state to be opposite CheckBox2. Either notification by itself would be fine, but together they force an infinite loop (possibly infinite recursion as well). – supercat Mar 07 '11 at 23:06
  • 1
    The state changes are not important here. Only the events, this only allows an event to propegate once (which in itself may be undesirable) however it cannot repeat more than once. – Peter Lawrey Mar 08 '11 at 08:39