Observable, Observer and recursive update() problem

R

Rogan Dawes

Hi,

I am trying to design a web security application.

I am trying to separate the model and the controller(s) and the UI, as
is good practice.

So, it seems that the way to do this is to have the Model extend
Observable, and have the various controllers Observe it.

I plan to have multiple plugins (controllers), which all Observe the model.

As part of their observeration, they might make further changes to the
model, which will also trigger updates/notifications.

How do I prevent recursive notification from happening, and make sure
that all Observers get their notifications in the right order?

Stupid example:

A temperature controller app.

Model:
Keeps track of the temperature, issues updates if it changes.
Also keeps track of the state of the heater and the aircon, and issues
updates if those change.

Controller:
Reads the temperature from a sensor, and updates the model.

Observer1:

A display that reports the temperature, and the status of the aircon and
heater

Observer2:

A feature that automatically turns on the aircon or heater if the
temperature goes out of limit.



So the process would go something like:

* Controller reads the temperature, updates the model.
* Model calls update(temperature) in each Observer sequentially. Say it
calls Observer2 first.
* Observer2 notices that the temperature is too high, and turns on the
aircon, and updates the model to reflect this.
* The model then calls update(aircon) in each Observer sequentially.

At this point, we are re-entering Observer2.update() recursively.

In this stupid example, this may not be such a big deal, but now add
another Observer that is tracking the status of the aircon, and turning
off the heater whenever the aircon comes on. (I know, bad practice, it
should have been turned off long ago. Its a stupid example, OK?!)

So the Observer2 update(aircon) finishes, Observer1 update(aircon)
finishes, and the Observer3 update(aircon) is called, which turns off
the heater, and updates the model.

The model then calls update(heater) in each observer sequentially.

Note that, at this point, the Observer1 has still not received the
initial temperature notification, although it has received the aircon
notification.



This all seems wrong to me. The question is, how to fix it?

1. Make the model queue update() events, and fire them sequentially,
making sure that each one is finished before firing the next.

Pro: No more recursion.
Con: The first invocation of a model changing method has to loop
repeatedly (inside the model), and will only return after any resulting
events have been dispatched.
Con: Possible deadlock/synchronising problems?
Con: What happens if another thread submits an update while the queue is
being processed? That thread would return immediately, and the original
thread then has to deal with those updates too?

2. Give the model an event thread, and allow each update to return
immediately, while the event thread calls update for each event
sequentially.

Pro: model updates return immediately, and the caller can go back to
what it was doing.
Con: Does having an event thread violate the MVC design?

Other approaches?

Rogan
 
C

Chris Uppal

Rogan said:
I plan to have multiple plugins (controllers), which all Observe the
model.

As part of their observeration, they might make further changes to the
model, which will also trigger updates/notifications.

How do I prevent recursive notification from happening, and make sure
that all Observers get their notifications in the right order?

My immediate thought is that if you have to control these aspects of the
Observer pattern then you probably don't actually want the loose coupling that
Observer is intended to achieve.

More specifically. It's usually possible to control recursion by ensuring that
changed notification is only generated if there is /actually/ a change -- ie.
if the observed aspect has changed its value, not just that it /might/ have
changed. This will normally limit recursion to an acceptable level.

Secondly, if the various Observers have to see the notifications in a specific
order to avoid stepping on each other's feet, then that's is part of /their/
responsibility, it's not the responsibility of the Model. In this case I'd be
inclined to leave the Model simple, let it generate notifications however and
whenever it wanted, and add a Dispatcher object to the system. The Dispatcher
itself would Observe the Model and forward notifications to its clients.
Observers that didn't care about these issues would probably connect directly
to the Model (unless you find consistency is more important than simplicity);
the "fussy" Observers would instead connect to the Dispatcher and would
negotiate with it to get the forwarded notifications in whatever way was
appropriate (via a queue, on separate threads, in a specific order, or
whatever).

-- chris
 
T

Thomas Weidenfeller

Rogan said:
So, it seems that the way to do this is to have the Model extend
Observable, and have the various controllers Observe it.

Observer/Observable are rather inflexible. The main reason is that you
have to subclass Observable. This might get in your way, when you have
another hierarchy for business-logic objects.
How do I prevent recursive notification from happening, and make sure
that all Observers get their notifications in the right order?

a) Try to avoid it

b) Set/clear a flag (investigate the Observable....changed() methods if
they work for you. If not, add an own)

c) You temporarily unsubscribe the observer

d) You have special methods which don't fire the notification

/Thomas
 
R

Rogan Dawes

Chris said:
Rogan Dawes wrote:




My immediate thought is that if you have to control these aspects of the
Observer pattern then you probably don't actually want the loose coupling that
Observer is intended to achieve.

More specifically. It's usually possible to control recursion by ensuring that
changed notification is only generated if there is /actually/ a change -- ie.
if the observed aspect has changed its value, not just that it /might/ have
changed. This will normally limit recursion to an acceptable level.

My problem is that one change might trigger off a cascading change in
other values. I hear what you say about not using Observer/Observable,
though.
Secondly, if the various Observers have to see the notifications in a specific
order to avoid stepping on each other's feet, then that's is part of /their/
responsibility, it's not the responsibility of the Model. In this case I'd be
inclined to leave the Model simple, let it generate notifications however and
whenever it wanted, and add a Dispatcher object to the system. The Dispatcher
itself would Observe the Model and forward notifications to its clients.
Observers that didn't care about these issues would probably connect directly
to the Model (unless you find consistency is more important than simplicity);
the "fussy" Observers would instead connect to the Dispatcher and would
negotiate with it to get the forwarded notifications in whatever way was
appropriate (via a queue, on separate threads, in a specific order, or
whatever).

-- chris

Thanks for your comments. The approach that I plan to take is as follows:

class SiteModel extends Observable {

private boolean _notifying = false;

public void setConversationProperty(String id, String name, String value) {
synchronized(_writeLock) {
// make changes
// create the event
// queue the event
myNotify();
}
}

private void myNotify() {
if (_notifying) return;
_notifying = true;
while (queue.size() > 0) {
event = queue.remove(0);
setChanged();
notifyObservers(event);
}
_notifying = false;
}

}

In this way, only a single thread can make changes that would cause
notifications at a time. So, that thread has to deal with all the events
that cascade as a result of that initial change.

Other threads would not be able to pass the _writeLock to add their own
events to the queue, until the first thread has finished issuing all the
events that resulted from its change.

Well, that is the theory, anyway!

Thanks for the response.

Rogan
 
I

iamfractal

Rogan Dawes said:
Thanks for your comments. The approach that I plan to take is as follows:

class SiteModel extends Observable {

private boolean _notifying = false;

Don't use flags, as they smear the state of your object. Instead, use
the State pattern: have something like SiteModelBusyState and
SiteModelIdleState.

public void setConversationProperty(String id, String name, String value) {
synchronized(_writeLock) {
// make changes
// create the event
// queue the event
myNotify();
}
}

private void myNotify() {
if (_notifying) return;
_notifying = true;
while (queue.size() > 0) {
event = queue.remove(0);
setChanged();
notifyObservers(event);
}
_notifying = false;
}

}

In this way, only a single thread can make changes that would cause
notifications at a time. So, that thread has to deal with all the events
that cascade as a result of that initial change.

Other threads would not be able to pass the _writeLock to add their own
events to the queue, until the first thread has finished issuing all the
events that resulted from its change.

I didn't think threading was the problem. You can, afterall, have
cascading updates within even a single thread. Or do all your
observers run in their own thread?

I thought Chris had the right idea: keep the model simple. Queues are
also a fine idea: have the model accessible only via a queue, so that
an update caused by a notification cannot directly update the model,
but only put the update in a queue. This guarantees order, though you
have to bear in mind that updates put in the queue may attempt to
operate on stale (or deleted) data when they are serviced.


..ed

www.EdmundKirwan.com - Home of The Fractal Class Composition
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top