protect against recursive reentry

This commit is contained in:
David Rose 2002-09-10 16:29:26 +00:00
parent 58cfb7312b
commit 221bf704cc
2 changed files with 117 additions and 19 deletions

View File

@ -39,6 +39,7 @@ CMetaInterval(const string &name) :
_precision = interval_precision; _precision = interval_precision;
_current_nesting_level = 0; _current_nesting_level = 0;
_next_event_index = 0; _next_event_index = 0;
_processing_events = false;
} }
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
@ -111,6 +112,8 @@ clear_intervals() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
int CMetaInterval:: int CMetaInterval::
push_level(double rel_time, RelativeStart rel_to) { push_level(double rel_time, RelativeStart rel_to) {
nassertr(_event_queue.empty() && !_processing_events, -1);
_defs.push_back(IntervalDef()); _defs.push_back(IntervalDef());
IntervalDef &def = _defs.back(); IntervalDef &def = _defs.back();
def._type = DT_push_level; def._type = DT_push_level;
@ -135,6 +138,7 @@ push_level(double rel_time, RelativeStart rel_to) {
int CMetaInterval:: int CMetaInterval::
add_c_interval(CInterval *c_interval, add_c_interval(CInterval *c_interval,
double rel_time, RelativeStart rel_to) { double rel_time, RelativeStart rel_to) {
nassertr(_event_queue.empty() && !_processing_events, -1);
nassertr(c_interval != (CInterval *)NULL, -1); nassertr(c_interval != (CInterval *)NULL, -1);
c_interval->_parents.push_back(this); c_interval->_parents.push_back(this);
@ -175,6 +179,8 @@ int CMetaInterval::
add_ext_index(int ext_index, const string &name, double duration, add_ext_index(int ext_index, const string &name, double duration,
bool open_ended, bool open_ended,
double rel_time, RelativeStart rel_to) { double rel_time, RelativeStart rel_to) {
nassertr(_event_queue.empty() && !_processing_events, -1);
_defs.push_back(IntervalDef()); _defs.push_back(IntervalDef());
IntervalDef &def = _defs.back(); IntervalDef &def = _defs.back();
def._type = DT_ext_index; def._type = DT_ext_index;
@ -197,6 +203,7 @@ add_ext_index(int ext_index, const string &name, double duration,
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
int CMetaInterval:: int CMetaInterval::
pop_level() { pop_level() {
nassertr(_event_queue.empty() && !_processing_events, -1);
nassertr(_current_nesting_level > 0, -1); nassertr(_current_nesting_level > 0, -1);
_defs.push_back(IntervalDef()); _defs.push_back(IntervalDef());
@ -226,6 +233,7 @@ pop_level() {
bool CMetaInterval:: bool CMetaInterval::
set_interval_start_time(const string &name, double rel_time, set_interval_start_time(const string &name, double rel_time,
CMetaInterval::RelativeStart rel_to) { CMetaInterval::RelativeStart rel_to) {
nassertr(_event_queue.empty() && !_processing_events, false);
Defs::iterator di; Defs::iterator di;
for (di = _defs.begin(); di != _defs.end(); ++di) { for (di = _defs.begin(); di != _defs.end(); ++di) {
IntervalDef &def = (*di); IntervalDef &def = (*di);
@ -342,6 +350,11 @@ get_interval_end_time(const string &name) const {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_initialize(double t) { priv_initialize(double t) {
if (_processing_events) {
enqueue_self_event(ET_initialize, t);
return;
}
check_stopped("priv_initialize"); check_stopped("priv_initialize");
// It may be tempting to flush the event_queue here, but don't do // It may be tempting to flush the event_queue here, but don't do
// it. Those are events that must still be serviced from some // it. Those are events that must still be serviced from some
@ -355,16 +368,18 @@ priv_initialize(double t) {
int now = double_to_int_time(t); int now = double_to_int_time(t);
// Now look for events from the beginning up to the current time. // Now look for events from the beginning up to the current time.
_processing_events = true;
ActiveEvents new_active; ActiveEvents new_active;
while (_next_event_index < _events.size() && while (_next_event_index < _events.size() &&
_events[_next_event_index]->_time <= now) { _events[_next_event_index]->_time <= now) {
PlaybackEvent *event = _events[_next_event_index]; PlaybackEvent *event = _events[_next_event_index];
_next_event_index++;
// Do the indicated event. // Do the indicated event.
do_event_forward(event, new_active, true); do_event_forward(event, new_active, true);
_next_event_index++;
} }
finish_events_forward(now, new_active); finish_events_forward(now, new_active);
_processing_events = false;
_curr_t = t; _curr_t = t;
_state = S_started; _state = S_started;
@ -380,12 +395,18 @@ priv_initialize(double t) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_instant() { priv_instant() {
if (_processing_events) {
enqueue_self_event(ET_instant);
return;
}
check_stopped("priv_instant"); check_stopped("priv_instant");
recompute(); recompute();
_active.clear(); _active.clear();
// Apply all of the events. This just means we invoke "instant" for // Apply all of the events. This just means we invoke "instant" for
// any end or instant event, ignoring the begin events. // any end or instant event, ignoring the begin events.
_processing_events = true;
PlaybackEvents::iterator ei; PlaybackEvents::iterator ei;
for (ei = _events.begin(); ei != _events.end(); ++ei) { for (ei = _events.begin(); ei != _events.end(); ++ei) {
PlaybackEvent *event = (*ei); PlaybackEvent *event = (*ei);
@ -393,6 +414,7 @@ priv_instant() {
enqueue_event(event->_n, ET_instant, true, 0); enqueue_event(event->_n, ET_instant, true, 0);
} }
} }
_processing_events = false;
_next_event_index = _events.size(); _next_event_index = _events.size();
_curr_t = get_duration(); _curr_t = get_duration();
@ -408,12 +430,18 @@ priv_instant() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_step(double t) { priv_step(double t) {
if (_processing_events) {
enqueue_self_event(ET_step, t);
return;
}
check_started("priv_step"); check_started("priv_step");
int now = double_to_int_time(t); int now = double_to_int_time(t);
// Now look for events between the last time we ran and the current // Now look for events between the last time we ran and the current
// time. // time.
_processing_events = true;
if (_next_event_index < _events.size() && if (_next_event_index < _events.size() &&
_events[_next_event_index]->_time <= now) { _events[_next_event_index]->_time <= now) {
// The normal case: time is increasing. // The normal case: time is increasing.
@ -421,10 +449,10 @@ priv_step(double t) {
while (_next_event_index < _events.size() && while (_next_event_index < _events.size() &&
_events[_next_event_index]->_time <= now) { _events[_next_event_index]->_time <= now) {
PlaybackEvent *event = _events[_next_event_index]; PlaybackEvent *event = _events[_next_event_index];
_next_event_index++;
// Do the indicated event. // Do the indicated event.
do_event_forward(event, new_active, false); do_event_forward(event, new_active, false);
_next_event_index++;
} }
finish_events_forward(now, new_active); finish_events_forward(now, new_active);
@ -436,11 +464,13 @@ priv_step(double t) {
_events[_next_event_index - 1]->_time > now) { _events[_next_event_index - 1]->_time > now) {
_next_event_index--; _next_event_index--;
PlaybackEvent *event = _events[_next_event_index]; PlaybackEvent *event = _events[_next_event_index];
do_event_reverse(event, new_active, false); do_event_reverse(event, new_active, false);
} }
finish_events_reverse(now, new_active); finish_events_reverse(now, new_active);
} }
_processing_events = false;
_curr_t = t; _curr_t = t;
_state = S_started; _state = S_started;
@ -455,21 +485,29 @@ priv_step(double t) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_finalize() { priv_finalize() {
if (_processing_events) {
enqueue_self_event(ET_finalize);
return;
}
double duration = get_duration(); double duration = get_duration();
if (_state == S_initial) { if (_state == S_initial) {
priv_initialize(duration); priv_initialize(duration);
} }
// Do all remaining events. // Do all remaining events.
_processing_events = true;
ActiveEvents new_active; ActiveEvents new_active;
while (_next_event_index < _events.size()) { while (_next_event_index < _events.size()) {
PlaybackEvent *event = _events[_next_event_index]; PlaybackEvent *event = _events[_next_event_index];
_next_event_index++;
// Do the indicated event. // Do the indicated event.
do_event_forward(event, new_active, true); do_event_forward(event, new_active, true);
_next_event_index++;
} }
finish_events_forward(double_to_int_time(duration), new_active); finish_events_forward(double_to_int_time(duration), new_active);
_processing_events = false;
_curr_t = duration; _curr_t = duration;
_state = S_final; _state = S_final;
} }
@ -484,6 +522,11 @@ priv_finalize() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_reverse_initialize(double t) { priv_reverse_initialize(double t) {
if (_processing_events) {
enqueue_self_event(ET_reverse_initialize, t);
return;
}
check_stopped("priv_reverse_initialize"); check_stopped("priv_reverse_initialize");
// It may be tempting to flush the event_queue here, but don't do // It may be tempting to flush the event_queue here, but don't do
// it. Those are events that must still be serviced from some // it. Those are events that must still be serviced from some
@ -497,6 +540,7 @@ priv_reverse_initialize(double t) {
int now = double_to_int_time(t); int now = double_to_int_time(t);
// Now look for events from the end down to the current time. // Now look for events from the end down to the current time.
_processing_events = true;
ActiveEvents new_active; ActiveEvents new_active;
while (_next_event_index > 0 && while (_next_event_index > 0 &&
_events[_next_event_index - 1]->_time > now) { _events[_next_event_index - 1]->_time > now) {
@ -507,6 +551,7 @@ priv_reverse_initialize(double t) {
do_event_reverse(event, new_active, true); do_event_reverse(event, new_active, true);
} }
finish_events_reverse(now, new_active); finish_events_reverse(now, new_active);
_processing_events = false;
_curr_t = t; _curr_t = t;
_state = S_started; _state = S_started;
@ -523,12 +568,18 @@ priv_reverse_initialize(double t) {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_reverse_instant() { priv_reverse_instant() {
if (_processing_events) {
enqueue_self_event(ET_reverse_instant);
return;
}
check_stopped("priv_reverse_instant"); check_stopped("priv_reverse_instant");
recompute(); recompute();
_active.clear(); _active.clear();
// Apply all of the events. This just means we invoke "instant" for // Apply all of the events. This just means we invoke "instant" for
// any end or instant event, ignoring the begin events. // any end or instant event, ignoring the begin events.
_processing_events = true;
PlaybackEvents::reverse_iterator ei; PlaybackEvents::reverse_iterator ei;
for (ei = _events.rbegin(); ei != _events.rend(); ++ei) { for (ei = _events.rbegin(); ei != _events.rend(); ++ei) {
PlaybackEvent *event = (*ei); PlaybackEvent *event = (*ei);
@ -536,6 +587,7 @@ priv_reverse_instant() {
enqueue_event(event->_n, ET_reverse_instant, true, 0); enqueue_event(event->_n, ET_reverse_instant, true, 0);
} }
} }
_processing_events = false;
_next_event_index = 0; _next_event_index = 0;
_curr_t = 0.0; _curr_t = 0.0;
@ -551,20 +603,28 @@ priv_reverse_instant() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_reverse_finalize() { priv_reverse_finalize() {
if (_processing_events) {
enqueue_self_event(ET_reverse_finalize);
return;
}
if (_state == S_initial) { if (_state == S_initial) {
priv_initialize(0.0); priv_initialize(0.0);
} }
// Do all remaining events at the beginning. // Do all remaining events at the beginning.
_processing_events = true;
ActiveEvents new_active; ActiveEvents new_active;
while (_next_event_index > 0) { while (_next_event_index > 0) {
_next_event_index--; _next_event_index--;
PlaybackEvent *event = _events[_next_event_index]; PlaybackEvent *event = _events[_next_event_index];
do_event_reverse(event, new_active, true); do_event_reverse(event, new_active, true);
} }
finish_events_reverse(0, new_active); finish_events_reverse(0, new_active);
_processing_events = false;
_curr_t = 0.0; _curr_t = 0.0;
_state = S_initial; _state = S_initial;
} }
@ -585,11 +645,19 @@ priv_reverse_finalize() {
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
void CMetaInterval:: void CMetaInterval::
priv_interrupt() { priv_interrupt() {
if (_processing_events) {
enqueue_self_event(ET_interrupt);
return;
}
_processing_events = true;
ActiveEvents::iterator ai; ActiveEvents::iterator ai;
for (ai = _active.begin(); ai != _active.end(); ++ai) { for (ai = _active.begin(); ai != _active.end(); ++ai) {
PlaybackEvent *event = (*ai); PlaybackEvent *event = (*ai);
enqueue_event(event->_n, ET_interrupt, false); enqueue_event(event->_n, ET_interrupt, false);
} }
_processing_events = false;
if (_state == S_started) { if (_state == S_started) {
_state = S_paused; _state = S_paused;
} }
@ -917,6 +985,25 @@ enqueue_event(int n, CInterval::EventType event_type, bool is_initial, int time)
_event_queue.push_back(EventQueueEntry(n, event_type, time)); _event_queue.push_back(EventQueueEntry(n, event_type, time));
} }
////////////////////////////////////////////////////////////////////
// Function: CMetaInterval::enqueue_self_event
// Access: Private
// Description: Enqueues a reference to *this* interval. This is
// called only when the interval is recursively
// re-entered; the request will be serviced when the
// current request is done processing.
//
// time is only relevant for ET_initialize,
// ET_reverse_initialize, and ET_step.
////////////////////////////////////////////////////////////////////
void CMetaInterval::
enqueue_self_event(CInterval::EventType event_type, double t) {
interval_cat.info()
<< "Recursive reentry detected into " << *this << "\n";
int time = double_to_int_time(t);
_event_queue.push_back(EventQueueEntry(-1, event_type, time));
}
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Function: CMetaInterval::service_event_queue // Function: CMetaInterval::service_event_queue
// Access: Private // Access: Private
@ -931,7 +1018,13 @@ enqueue_event(int n, CInterval::EventType event_type, bool is_initial, int time)
bool CMetaInterval:: bool CMetaInterval::
service_event_queue() { service_event_queue() {
while (!_event_queue.empty()) { while (!_event_queue.empty()) {
nassertr(!_processing_events, true);
const EventQueueEntry &entry = _event_queue.front(); const EventQueueEntry &entry = _event_queue.front();
if (entry._n == -1) {
// Index -1 is a special code for *this* interval.
priv_do_event(int_to_double_time(entry._time), entry._event_type);
} else {
nassertr(entry._n >= 0 && entry._n < (int)_defs.size(), false); nassertr(entry._n >= 0 && entry._n < (int)_defs.size(), false);
const IntervalDef &def = _defs[entry._n]; const IntervalDef &def = _defs[entry._n];
switch (def._type) { switch (def._type) {
@ -948,10 +1041,13 @@ service_event_queue() {
nassertr(false, false); nassertr(false, false);
return false; return false;
} }
}
_event_queue.pop_front(); _event_queue.pop_front();
} }
// No more events on the queue. // No more events on the queue.
nassertr(!_processing_events, false);
return false; return false;
} }

View File

@ -150,6 +150,7 @@ private:
void enqueue_event(int n, CInterval::EventType event_type, bool is_initial, void enqueue_event(int n, CInterval::EventType event_type, bool is_initial,
int time = 0); int time = 0);
void enqueue_self_event(CInterval::EventType event_type, double t = 0.0);
bool service_event_queue(); bool service_event_queue();
int recompute_level(int n, int level_begin, int &level_end); int recompute_level(int n, int level_begin, int &level_end);
@ -165,6 +166,7 @@ private:
int _end_time; int _end_time;
size_t _next_event_index; size_t _next_event_index;
bool _processing_events;
// This is the queue of events that have occurred due to a recent // This is the queue of events that have occurred due to a recent
// priv_initialize(), priv_step(), etc., but have not yet been serviced, due // priv_initialize(), priv_step(), etc., but have not yet been serviced, due