- Use a NotifyLock instead of (History|Simulation)::set_notify_enabled() method, which is more elegant and also exception-safe

- Delete set_notify_enabled() method from ConcreteHistory; it was both wrong and useless, and caused impredictable behaviour!
- Don't make some methods of History and Simulation virtual if we don't want the user to override them
- Loading from file and jumping to an instant of the simulation should be much quickier now

git-svn-id: svn://svn.gna.org/svn/sgpemv2/trunk@1170 3ecf2c5c-341e-0410-92b4-d18e462d057c
This commit is contained in:
tchernobog 2006-09-15 09:34:12 +00:00
parent 737324f250
commit 83b655496f
11 changed files with 94 additions and 73 deletions

View File

@ -70,7 +70,7 @@ void XMLSerializer::restore_snapshot(const Glib::ustring& filename, History& his
// DEBUG - remove me when finished
// disable notifications over history until the end of this method
bool was_enabled = hist.set_notify_enabled(false);
History::LockNotify h_lock(hist);
#ifdef LIBXML_SAX1_ENABLED
xmlDocPtr doc;
@ -107,8 +107,6 @@ void XMLSerializer::restore_snapshot(const Glib::ustring& filename, History& his
#error Compilation of LIBXML with SAX1 support must be enabled
#endif /* LIBXML_SAX1_ENABLED */
// Re-enable notifications over history observers
hist.set_notify_enabled(was_enabled);
}

View File

@ -166,7 +166,7 @@ AddRequestDialog::run_edit(Request& request)
{
assert(_list_model->children());
bool was_enabled = history.set_notify_enabled(false);
History::LockNotify h_lock(history);
// I know it's a bit hack-ish, but do you know an elegant alternative way?
for(Iseq<vector<SubRequest*>::iterator> it = iseq(subrequests); it; ++it)
@ -178,8 +178,6 @@ AddRequestDialog::run_edit(Request& request)
for(Iseq<TreeIter> it = iseq(sreq_container); it; ++it)
history.add_subrequest(request, (*it)[_list_key_column], (*it)[_list_duration_column]);
history.set_notify_enabled(was_enabled);
}
hide();

View File

@ -523,14 +523,6 @@ ConcreteHistory::reset(bool notify)
notify_change();
}
void
ConcreteHistory::notify_change()
{
History::RegisteredObservers::iterator it;
for (it = _observers.begin(); it != _observers.end(); it++)
(*it)->update(*this);
}
bool
ConcreteHistory::is_sealed() const

View File

@ -124,8 +124,6 @@ namespace sgpem
typedef std::vector<ConcreteEnvironment*> Snapshots;
Snapshots _snapshots;
virtual void notify_change();
private:
// Disable assignment, implement it only if needed
ConcreteHistory& operator=(const ConcreteHistory& op2);

View File

@ -73,8 +73,8 @@ ConcreteSimulation::jump_to(History::position p) throw(UserInterruptException, N
// Disable momentarily updates for registered observers on
// sgpem::Simulation and sgpem::History.
bool his_enabled = _history.set_notify_enabled(false);
bool sim_enabled = set_notify_enabled(false);
History::LockNotify h_lock(_history);
Simulation::LockNotify s_lock(*this);
pause();
@ -82,30 +82,12 @@ ConcreteSimulation::jump_to(History::position p) throw(UserInterruptException, N
History::position increment = 0;
while (yet_to_finish && p > _history.get_front() + increment)
{
try
{
yet_to_finish = step();
increment++;
}
catch(const CPUPolicyException&)
{
// Lookout that notifications have to be re-enabled.
_history.set_notify_enabled(his_enabled);
set_notify_enabled(sim_enabled);
throw;
}
yet_to_finish = step();
increment++;
}
get_history().set_front(std::min(p, _history.get_size()));
if (!yet_to_finish)
stop();
// Reenables updates to registered observers.
// Calls _history.notify_change() on reactivation.
_history.set_notify_enabled(his_enabled);
// Do the same for notifications on the state
// of Simulation
set_notify_enabled(sim_enabled);
}

View File

@ -81,6 +81,5 @@ namespace sgpem
}
#endif

View File

@ -83,8 +83,15 @@ History::set_notify_enabled(bool enabled)
return old_value;
}
bool
History::is_notify_enabled() const
// --------- History::LockNotify ---------------
History::LockNotify::LockNotify(History& history)
: _h(history), _was_enabled(_h.set_notify_enabled(false))
{
return _notify;
}
History::LockNotify::~LockNotify()
{
_h.set_notify_enabled(_was_enabled);
}

View File

@ -56,6 +56,10 @@ namespace sgpem
class SG_DLLEXPORT History
{
public:
// Forward declaration
class LockNotify;
friend class History::LockNotify;
typedef unsigned int size_t;
typedef unsigned int time_t;
typedef unsigned int position;
@ -134,6 +138,14 @@ namespace sgpem
virtual void attach(HistoryObserver& observer);
virtual void detach(const HistoryObserver& observer);
virtual void reset() = 0;
protected:
typedef std::vector<HistoryObserver*> RegisteredObservers;
RegisteredObservers _observers;
void notify_change();
/** \brief Enable/disable notifications to registered observers
*
* This is quite useful to disable momentarily notification while you
@ -142,26 +154,36 @@ namespace sgpem
*
* \return The old value
*/
virtual bool set_notify_enabled(bool enabled = true);
bool is_notify_enabled() const;
virtual void reset() = 0;
protected:
typedef std::vector<HistoryObserver*> RegisteredObservers;
RegisteredObservers _observers;
virtual void notify_change();
bool set_notify_enabled(bool enabled = true);
position _front;
private:
bool _notify;
}
; //~ class History
}; //~ class History
/** \brief Disables notifications to History during the life of this object
*
* This class is useful if you've to do a lot of sequential operations on
* History that would reset it / notify its observers. For example, when loading
* from a file. In this case, create an object of this type on the stack.
* The destructor will take care of re-enabling notifications.
*/
class SG_DLLEXPORT History::LockNotify
{
public:
LockNotify(History& history);
~LockNotify();
private:
History& _h;
bool _was_enabled;
LockNotify(const LockNotify&);
LockNotify& operator=(const LockNotify&);
}; //~ class History::LockNotify
}//~ namespace sgpem

View File

@ -67,6 +67,9 @@ namespace sgpem
class SG_DLLEXPORT Simulation : public Singleton<ConcreteSimulation>
{
public:
class LockNotify;
friend class Simulation::LockNotify;
enum state
{
state_running = 0xdeafd0d0,
@ -170,6 +173,10 @@ namespace sgpem
virtual void attach(SimulationObserver& observer);
virtual void detach(const SimulationObserver& observer);
protected:
typedef std::vector<SimulationObserver*> RegisteredObservers;
RegisteredObservers _observers;
/** \brief Enable/disable notifications to registered observers
*
* This is quite useful to disable momentarily notification while you
@ -178,20 +185,36 @@ namespace sgpem
*
* \return The old value
*/
virtual bool set_notify_enabled(bool enabled = true);
bool is_notify_enabled() const;
protected:
typedef std::vector<SimulationObserver*> RegisteredObservers;
RegisteredObservers _observers;
bool set_notify_enabled(bool enabled = true);
Simulation(); // Constructor
virtual void notify_change();
void notify_change();
private:
bool _notify;
};
}; //~ class Simulation
/** \brief Disables notifications to Simulation during the life of this object
*
* This class is useful if you've to do a lot of sequential operations on
* Simulation that would reset it / notify its observers. For example, when loading
* from a file. In this case, create an object of this type on the stack.
* The destructor will take care of re-enabling notifications.
*/
class SG_DLLEXPORT Simulation::LockNotify
{
public:
LockNotify(Simulation& simulation);
~LockNotify();
private:
Simulation& _s;
bool _was_enabled;
LockNotify(const LockNotify&);
LockNotify& operator=(const LockNotify&);
}; //~ class Simulation::LockNotify
}

View File

@ -89,8 +89,14 @@ Simulation::set_notify_enabled(bool enabled)
return old_value;
}
bool
Simulation::is_notify_enabled() const
// --------- Simulation::LockNotify ---------------
Simulation::LockNotify::LockNotify(Simulation& simulation)
: _s(simulation), _was_enabled(_s.set_notify_enabled(false))
{
return _notify;
}
Simulation::LockNotify::~LockNotify()
{
_s.set_notify_enabled(_was_enabled);
}

View File

@ -84,9 +84,6 @@ JumpToDialog::start()
// start listening to simulation updates
sim.attach(*this);
// remember state of notifications for History
bool reenable = h.is_notify_enabled();
try
{
if(_target_instant < h.get_size() - 1)
@ -94,9 +91,9 @@ JumpToDialog::start()
else
sim.jump_to(h.get_size() - 1);
// disable notifications since we could call
// disable notifications on History since we could call
// run() a lot of times
h.set_notify_enabled(false);
History::LockNotify h_lock(h);
while(h.get_front() <= _target_instant)
{
sim.run();
@ -149,7 +146,6 @@ JumpToDialog::start()
// Ending successfully: detach me, reenable notifications,
// and emit response ``okay''
sim.detach(*this);
h.set_notify_enabled(reenable);
response(Gtk::RESPONSE_OK);
}