gnash-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Gnash-commit] /srv/bzr/gnash/trunk r10704: Rewrite implementation for a


From: Benjamin Wolsey
Subject: [Gnash-commit] /srv/bzr/gnash/trunk r10704: Rewrite implementation for as object callbacks without using Timers, which
Date: Mon, 16 Mar 2009 12:14:19 +0100
User-agent: Bazaar (1.5)

------------------------------------------------------------
revno: 10704
committer: Benjamin Wolsey <address@hidden>
branch nick: trunk
timestamp: Mon 2009-03-16 12:14:19 +0100
message:
  Rewrite implementation for as object callbacks without using Timers, which 
  reduces overhead and addresses the problem of infinite recursion (callbacks
  added during a callback). It also makes the code much cleaner when
  registering callbacks and removes the need to track them.
  
  Call the callbacks predictably on every heartbeat.
  
  Don't fail instantly when an XML or LoadVars load connection fails; this
  should be delayed until the first callback. Fixes LoadVars tests. The
  onData function requires an undefined, not null, value to signify failure.
  Notify AS callbacks with false, not true, on failure.
  
  NetStream does not need a queue of statuses, but rather only the last
  one set. The fact that Gnash's buffer full event is unpredictable is still
  a small problem, but the behaviour is much more compatible.
modified:
  libbase/LoadThread.cpp
  libbase/LoadThread.h
  libcore/as_object.h
  libcore/asobj/LoadVars_as.cpp
  libcore/asobj/LoadableObject.cpp
  libcore/asobj/LoadableObject.h
  libcore/asobj/NetConnection_as.cpp
  libcore/asobj/NetConnection_as.h
  libcore/asobj/NetStream_as.cpp
  libcore/asobj/NetStream_as.h
  libcore/asobj/Sound_as.cpp
  libcore/asobj/Sound_as.h
  libcore/asobj/XMLSocket_as.cpp
  libcore/asobj/XML_as.cpp
  libcore/movie_root.cpp
  libcore/movie_root.h
  testsuite/misc-ming.all/LoadVarsTest.c
    ------------------------------------------------------------
    revno: 10703.1.1
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Sat 2009-03-14 14:51:05 +0100
    message:
      Use a new {add,remove}AdvanceTimer for updating and querying AS objects
      with that need to notify callbacks and update their own status.
    modified:
      libcore/as_object.h
      libcore/asobj/LoadableObject.cpp
      libcore/asobj/LoadableObject.h
      libcore/asobj/NetConnection_as.cpp
      libcore/asobj/NetConnection_as.h
      libcore/asobj/NetStream_as.cpp
      libcore/asobj/NetStream_as.h
      libcore/asobj/Sound_as.cpp
      libcore/asobj/Sound_as.h
      libcore/movie_root.cpp
      libcore/movie_root.h
    ------------------------------------------------------------
    revno: 10703.1.2
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Sat 2009-03-14 16:20:39 +0100
    message:
      Split timers and object callbacks into separate functions.
      
      Allow calling the same status consecutively in NetStream. This almost 
passes
      the swfdec netstream-load-loop case without endless recursion, only it
      calls onStatus too often.
    modified:
      libcore/asobj/LoadableObject.cpp
      libcore/asobj/NetStream_as.cpp
      libcore/asobj/NetStream_as.h
      libcore/asobj/XMLSocket_as.cpp
      libcore/movie_root.cpp
      libcore/movie_root.h
    ------------------------------------------------------------
    revno: 10703.1.3
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Sat 2009-03-14 18:27:38 +0100
    message:
      Correct XMLSocket typo, make sure registered objects are kept alive
      until removed from the callbacks list.
    modified:
      libcore/asobj/LoadableObject.cpp
      libcore/asobj/LoadableObject.h
      libcore/asobj/XMLSocket_as.cpp
      libcore/movie_root.cpp
    ------------------------------------------------------------
    revno: 10703.1.4
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Sat 2009-03-14 19:26:28 +0100
    message:
      Add a hack to LoadThread so that it can take a failed stream. This is
      necessary for the timing of the onData() and onStatus() calls, which 
should
      not happen immediately, but at the earliest at the beginning of the next
      frame, if the connection fails. A better solution may be to start the
      connection and download in advanceState().
    modified:
      libbase/LoadThread.cpp
      libbase/LoadThread.h
      libcore/asobj/LoadVars_as.cpp
      libcore/asobj/LoadableObject.cpp
      libcore/asobj/XML_as.cpp
      libcore/movie_root.cpp
    ------------------------------------------------------------
    revno: 10703.1.5
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Sun 2009-03-15 10:39:47 +0100
    message:
      Only call onStatus once per advance.
    modified:
      libcore/asobj/NetStream_as.cpp
    ------------------------------------------------------------
    revno: 10703.1.6
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Mon 2009-03-16 10:33:19 +0100
    message:
      Call processActionQueue whenever advance callbacks have been executed.
    modified:
      libcore/movie_root.cpp
    ------------------------------------------------------------
    revno: 10703.1.7
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Mon 2009-03-16 10:34:54 +0100
    message:
      Don't maintain a queue of statuses. Only the last one should be necessary.
      This can be tested by calling more than one different NetStream function
      in an onStatus() callback. Only the last one is dealt with on each 
      core heart-beat.
    modified:
      libcore/asobj/NetStream_as.cpp
      libcore/asobj/NetStream_as.h
    ------------------------------------------------------------
    revno: 10703.1.8
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Mon 2009-03-16 10:41:01 +0100
    message:
      Passing LoadVars tests.
    modified:
      testsuite/misc-ming.all/LoadVarsTest.c
    ------------------------------------------------------------
    revno: 10703.1.9
    committer: Benjamin Wolsey <address@hidden>
    branch nick: work
    timestamp: Mon 2009-03-16 11:40:30 +0100
    message:
      Make all LoadVars tests in misc-ming.all pass with a correction to
      onData. Correct XML onData too.
    modified:
      libbase/LoadThread.cpp
      libcore/asobj/LoadVars_as.cpp
      libcore/asobj/LoadableObject.cpp
      libcore/asobj/LoadableObject.h
      libcore/asobj/XML_as.cpp
      testsuite/misc-ming.all/LoadVarsTest.c
=== modified file 'libbase/LoadThread.cpp'
--- a/libbase/LoadThread.cpp    2009-02-25 22:33:03 +0000
+++ b/libbase/LoadThread.cpp    2009-03-16 10:40:30 +0000
@@ -37,9 +37,10 @@
        _cacheSize(0),
        _chunkSize(56),
        _streamSize(0),
-       _needAccess(false)
+       _needAccess(false),
+    _failed(!_stream.get())
 {
-       assert(_stream.get());
+    if (_failed) return;
 
        // Start the downloading.
        setupCache(); // what for ??
@@ -113,7 +114,8 @@
        // true is the new position is equal the wanted,
        // or else return false
 
-       while ( (!_completed) && (!cancelRequested()) && _loadPosition < 
static_cast<long>(pos) )
+       while ((!_completed) && (!cancelRequested()) && 
+            _loadPosition < static_cast<long>(pos))
        {
                gnashSleep(100000); // 1/10 second WATCH FOR TIMEOUTS !
        }
@@ -126,14 +128,14 @@
        else
        {
                // Completed (eof) or canceled 
-               if ( _completed )
-               {
-                       log_error("LoadThread::seek(%d) : can't seek there, 
only %d bytes available", pos, _loadPosition);
+               if (_completed) {
+                       log_error("LoadThread::seek(%d) : can't seek there, 
only "
+                    "%d bytes available", pos, _loadPosition);
                }
-               else
-               {
+               else {
                        assert( _cancelRequested );
-                       log_error("LoadThread::seek(%d) : load cancellation 
requested while seeking", pos);
+                       log_error("LoadThread::seek(%d) : load cancellation "
+                    "requested while seeking", pos);
                }
                return -1;
        }
@@ -143,8 +145,11 @@
 {
 
        // If the data is in the cache we used it
-       if (_cacheStart <= _userPosition && static_cast<long>(bytes) + 
_userPosition <= _cacheStart + _cachedData) {
-               memcpy(dst, _cache.get() + (_userPosition - _cacheStart), 
bytes);
+       if (_cacheStart <= _userPosition && 
+            static_cast<long>(bytes) + _userPosition <= 
+            _cacheStart + _cachedData) {
+
+        memcpy(dst, _cache.get() + (_userPosition - _cacheStart), bytes);
                _userPosition += bytes;
                return bytes;
 
@@ -345,22 +350,26 @@
        // the "the edge", and "warm up" the remaining data.
        int ret;
        if (_cachedData + _chunkSize > _cacheSize) {
-               ret = _stream->read(_cache.get() + _cachedData, _cacheSize - 
_cachedData);
+               ret = _stream->read(_cache.get() + _cachedData, 
+                _cacheSize - _cachedData);
 
                _cachedData += ret;
                if (ret != _cacheSize - _cachedData) {
 #ifdef GNASH_DEBUG_LOAD_THREAD
-                       log_debug("LoadThread completed during fillCache (read 
%d bytes when %d were requested)",
+                       log_debug("LoadThread completed during fillCache (read 
%d "
+                    "bytes when %d were requested)",
                                ret, _cacheSize-_cachedData);
 #endif
                        _completed = true;
-               } else {
+               }
+        else {
                        _stream->seek(_loadPosition + _chunkSize);
                        long pos = _stream->tell();
                        if (pos != _loadPosition + _chunkSize) {
 #ifdef GNASH_DEBUG_LOAD_THREAD
-                               log_debug("LoadThread completed during 
fillCache (attempted to go to position %d, but only got to %d",
-                                       _loadPosition+_chunkSize, pos);
+                               log_debug("LoadThread completed during 
fillCache "
+                        "(attempted to go to position %d, but only got to %d",
+                                           _loadPosition+_chunkSize, pos);
 #endif
                                _completed = true;
                        }

=== modified file 'libbase/LoadThread.h'
--- a/libbase/LoadThread.h      2009-02-25 22:33:03 +0000
+++ b/libbase/LoadThread.h      2009-03-14 18:26:28 +0000
@@ -103,6 +103,8 @@
        ///
        long getBytesTotal() const;
 
+    bool failed() { return _failed; }
+
        // alias for getBytesTotal()
        long size() const { return getBytesTotal(); }
 
@@ -180,6 +182,9 @@
 
        /// Reset all values to original state
        void reset();
+
+    bool _failed;
+
 };
 
 } // namespace gnash

=== modified file 'libcore/as_object.h'
--- a/libcore/as_object.h       2009-02-10 15:38:43 +0000
+++ b/libcore/as_object.h       2009-03-14 13:51:05 +0000
@@ -86,7 +86,8 @@
 
 public:
 
-       Trigger(const std::string& propname, as_function& trig, const as_value& 
customArg)
+       Trigger(const std::string& propname, as_function& trig,
+            const as_value& customArg)
                :
                _propname(propname),
                _func(&trig),
@@ -137,6 +138,12 @@
 
 public:
 
+    /// A function to be called on movie_root::advance()
+    //
+    /// This is used for requesting an object to update its state and if
+    /// required to notify any AS callbacks. 
+    virtual void advanceState() { }
+
     /// Is any non-hidden property in this object ?
     bool hasNonHiddenProperties() const {
         return _members.hasNonHiddenProperties();

=== modified file 'libcore/asobj/LoadVars_as.cpp'
--- a/libcore/asobj/LoadVars_as.cpp     2009-02-10 15:38:43 +0000
+++ b/libcore/asobj/LoadVars_as.cpp     2009-03-16 10:40:30 +0000
@@ -184,29 +184,25 @@
 {
 
        as_object* thisPtr = fn.this_ptr.get();
-       if ( ! thisPtr ) return as_value();
+       if (!thisPtr) return as_value();
 
        // See 
http://gitweb.freedesktop.org/?p=swfdec/swfdec.git;a=blob;f=libswfdec/swfdec_initialize.as
 
-       as_value src; src.set_null();
-       if ( fn.nargs ) src = fn.arg(0);
+       as_value src; 
+       if (fn.nargs) src = fn.arg(0);
 
-       if ( ! src.is_null() )
-       {
-               VM& vm = thisPtr->getVM();
+       if (src.is_undefined()) {
+               thisPtr->set_member(NSV::PROP_LOADED, false);
+               thisPtr->callMethod(NSV::PROP_ON_LOAD, false);
+    }
+    else {
+               VM& vm = fn.getVM();
                string_table& st = vm.getStringTable();
-               string_table::key decodeKey = st.find("decode"); // add to 
namedStrings ?
+               string_table::key decodeKey = st.find("decode"); 
 
-               as_value tmp(true);
-               thisPtr->set_member(NSV::PROP_LOADED, tmp);
+               thisPtr->set_member(NSV::PROP_LOADED, true);
                thisPtr->callMethod(decodeKey, src);
-               thisPtr->callMethod(NSV::PROP_ON_LOAD, tmp);
-       }
-       else
-       {
-               as_value tmp(true);
-               thisPtr->set_member(NSV::PROP_LOADED, tmp);
-               thisPtr->callMethod(NSV::PROP_ON_LOAD, tmp);
+               thisPtr->callMethod(NSV::PROP_ON_LOAD, true);
        }
 
        return as_value();

=== modified file 'libcore/asobj/LoadableObject.cpp'
--- a/libcore/asobj/LoadableObject.cpp  2009-03-13 22:46:37 +0000
+++ b/libcore/asobj/LoadableObject.cpp  2009-03-16 10:40:30 +0000
@@ -46,22 +46,15 @@
 LoadableObject::LoadableObject()
     :
     _bytesLoaded(-1),
-    _bytesTotal(-1),
-    _loadCheckerTimer(0)
+    _bytesTotal(-1)
 {
 }    
 
 
 LoadableObject::~LoadableObject()
 {
-
     deleteAllChecked(_loadThreads);
-
-    if ( _loadCheckerTimer )
-    {
-        _vm.getRoot().clear_interval_timer(_loadCheckerTimer);
-    }
-
+    _vm.getRoot().removeAdvanceCallback(this);
 }
 
 
@@ -171,15 +164,6 @@
         str = ri.streamProvider().getStream(getURL);
     }
 
-       if (!str.get()) 
-       {
-               log_error(_("Can't load from %s (security?)"), url.str());
-               return;
-               // TODO: check if this is correct
-               //as_value nullValue; nullValue.set_null();
-               //callMethod(_vm.getStringTable().find(PROPNAME("onData")), 
nullValue);
-       }
-
        log_security(_("Loading from url: '%s'"), url.str());
     target.queueLoad(str);
        
@@ -198,15 +182,6 @@
     // Checks whether access is allowed.
     std::auto_ptr<IOChannel> str(ri.streamProvider().getStream(url));
 
-       if (!str.get()) 
-       {
-               log_error(_("Can't load variables from %s (security?)"), 
url.str());
-               return;
-               // TODO: check if this is correct
-               //as_value nullValue; nullValue.set_null();
-               //callMethod(_vm.getStringTable().find(PROPNAME("onData")), 
nullValue);
-       }
-
        log_security(_("Loading from url: '%s'"), url.str());
     queueLoad(str);
 }
@@ -218,7 +193,7 @@
 
     bool startTimer = _loadThreads.empty();
 
-    std::auto_ptr<LoadThread> lt ( new LoadThread(str) );
+    std::auto_ptr<LoadThread> lt (new LoadThread(str));
 
     // we push on the front to avoid invalidating
     // iterators when queueLoad is called as effect
@@ -228,15 +203,9 @@
     // 
     _loadThreads.push_front(lt.release());
 
-    if ( startTimer )
+    if (startTimer)
     {
-        boost::intrusive_ptr<builtin_function> loadsChecker = 
-            new builtin_function(&checkLoads_wrapper);
-
-        std::auto_ptr<Timer> timer(new Timer);
-        timer->setInterval(*loadsChecker, 50, this);
-        _loadCheckerTimer = getVM().getRoot().add_interval_timer(timer, true);
-
+        getVM().getRoot().addAdvanceCallback(this);
     }
 
     _bytesLoaded = 0;
@@ -244,29 +213,24 @@
 
 }
 
-as_value
-LoadableObject::checkLoads_wrapper(const fn_call& fn)
-{
-       boost::intrusive_ptr<LoadableObject> ptr =
-        ensureType<LoadableObject>(fn.this_ptr);
-       ptr->checkLoads();
-       return as_value();
-}
-
-
 void
-LoadableObject::checkLoads()
+LoadableObject::advanceState()
 {
 
-    if (_loadThreads.empty()) return; // nothing to do
+    if (_loadThreads.empty()) return;
 
     for (LoadThreadList::iterator it=_loadThreads.begin();
             it != _loadThreads.end(); )
     {
         LoadThread* lt = *it;
 
-        if ( lt->completed() )
-        {
+        /// An empty file is the same as a failure.
+        if (lt->failed() || (lt->completed() && !lt->size())) {
+            callMethod(NSV::PROP_ON_DATA, as_value());
+            it = _loadThreads.erase(it);
+            delete lt; 
+        }
+        else if (lt->completed()) {
             size_t dataSize = _bytesTotal = _bytesLoaded = lt->getBytesTotal();
 
             boost::scoped_array<char> buf(new char[dataSize + 1]);
@@ -306,11 +270,11 @@
         }
     }
 
-    if ( _loadThreads.empty() ) 
+    if (_loadThreads.empty()) 
     {
-        _vm.getRoot().clear_interval_timer(_loadCheckerTimer);
-        _loadCheckerTimer=0;
+        _vm.getRoot().removeAdvanceCallback(this);
     }
+
 }
 
 void
@@ -456,7 +420,9 @@
 
        ValuesMap vals;
 
-       URL::parse_querystring(fn.arg(0).to_string(), vals);
+    const int version = fn.getVM().getSWFVersion();
+
+       URL::parse_querystring(fn.arg(0).to_string_versioned(version), vals);
 
        string_table& st = ptr->getVM().getStringTable();
        for  (ValuesMap::const_iterator it=vals.begin(), itEnd=vals.end();

=== modified file 'libcore/asobj/LoadableObject.h'
--- a/libcore/asobj/LoadableObject.h    2009-02-25 22:33:03 +0000
+++ b/libcore/asobj/LoadableObject.h    2009-03-16 10:40:30 +0000
@@ -81,7 +81,7 @@
                return _bytesTotal;
        }  
 
-    /// Overrides as_object::queueLoad to begin loading from a stream
+    /// Begin loading from a stream
     //
     /// @param str      The stream to load from. It is destroyed when
     ///                 we're finished with it.
@@ -91,6 +91,10 @@
     /// interchangeably with each object in ActionScript.
     static as_value loadableobject_addRequestHeader(const fn_call& fn);
 
+    /// Scan the LoadThread queue (_loadThreads) to see if any of
+    /// them completed. If any did, invoke the onData event
+    virtual void advanceState();
+
 protected:
 
     /// Convert the Loadable Object to a string.
@@ -109,17 +113,6 @@
     
     long _bytesTotal;
 
-    /// The load checker interval timer used to make loads async
-    unsigned int _loadCheckerTimer;
-
-    /// Scan the LoadThread queue (_loadThreads) to see if any of
-    /// them completed. If any did, invoke the onData event
-    void checkLoads();
-
-private:
-
-    static as_value checkLoads_wrapper(const fn_call& fn);
-
 };
 
 

=== modified file 'libcore/asobj/NetConnection_as.cpp'
--- a/libcore/asobj/NetConnection_as.cpp        2009-03-13 22:46:37 +0000
+++ b/libcore/asobj/NetConnection_as.cpp        2009-03-14 13:51:05 +0000
@@ -964,16 +964,6 @@
 
 }
 
-as_value
-NetConnection_as::advanceWrapper(const fn_call& fn)
-{
-    boost::intrusive_ptr<NetConnection_as> ptr = 
-        ensureType<NetConnection_as>(fn.this_ptr);
-    
-    ptr->advance();
-    return as_value();
-};
-
 void
 NetConnection_as::startAdvanceTimer() 
 {
@@ -984,13 +974,8 @@
         return;
     }
 
-    boost::intrusive_ptr<builtin_function> ticker_as = 
-            new builtin_function(&NetConnection_as::advanceWrapper);
-
-    std::auto_ptr<Timer> timer(new Timer);
-    unsigned long delayMS = 50; 
-    timer->setInterval(*ticker_as, delayMS, this);
-    _advanceTimer = getVM().getRoot().add_interval_timer(timer, true);
+    _advanceTimer = 1;
+    getVM().getRoot().addAdvanceCallback(this);
 
     log_debug("startAdvanceTimer: registered advance timer %d", _advanceTimer);
 }
@@ -1004,13 +989,13 @@
         return;
     }
 
-    getVM().getRoot().clear_interval_timer(_advanceTimer);
+    getVM().getRoot().removeAdvanceCallback(this);
     log_debug("stopAdvanceTimer: deregistered timer %d", _advanceTimer);
     _advanceTimer=0;
 }
 
 void
-NetConnection_as::advance()
+NetConnection_as::advanceState()
 {
     // Advance
 

=== modified file 'libcore/asobj/NetConnection_as.h'
--- a/libcore/asobj/NetConnection_as.h  2009-02-25 22:33:03 +0000
+++ b/libcore/asobj/NetConnection_as.h  2009-03-14 13:51:05 +0000
@@ -56,14 +56,13 @@
        ~NetConnection_as();
 
     /// Process connection stuff
-    virtual void advance();
-
-    static as_value advanceWrapper(const fn_call& fn);
+    virtual void advanceState();
 
     /// Make the stored URI into a valid and checked URL.
        std::string validateURL() const;
 
-    void call(as_object* asCallback, const std::string& methodName, const 
std::vector<as_value>& args, size_t firstArg);
+    void call(as_object* asCallback, const std::string& methodName,
+            const std::vector<as_value>& args, size_t firstArg);
 
     /// Process the close() method.
     void close();

=== modified file 'libcore/asobj/NetStream_as.cpp'
--- a/libcore/asobj/NetStream_as.cpp    2009-03-13 22:46:37 +0000
+++ b/libcore/asobj/NetStream_as.cpp    2009-03-16 11:14:19 +0000
@@ -102,10 +102,8 @@
     inputPos(0),
     _invalidatedVideoCharacter(0),
     _decoding_state(DEC_NONE),
-
     _videoDecoder(0),
     _videoInfoKnown(false),
-
     _audioDecoder(0),
     _audioInfoKnown(false),
 
@@ -113,19 +111,16 @@
     // as additional source
     _playbackClock(new InterruptableVirtualClock(new SystemClock)),
     _playHead(_playbackClock.get()), 
-
     _soundHandler(_vm.getRoot().runInfo().soundHandler()),
     _mediaHandler(media::MediaHandler::get()),
-
     _audioStreamer(_soundHandler),
-
-    _lastStatus(invalidStatus),
-    _advanceTimer(0)
+    _statusCode(invalidStatus)
 {
 }
 
 // extern (used by Global.cpp)
-void netstream_class_init(as_object& global)
+void
+netstream_class_init(as_object& global)
 {
 
     // This is going to be the global NetStream "class"/"function"
@@ -149,13 +144,14 @@
 void
 NetStream_as::processNotify(const std::string& funcname, as_object* info_obj)
 {
-    // TODO: check for System.onStatus too ! use a private getStatusHandler() 
method for this.
+    // TODO: check for System.onStatus too ! use a private
+    // getStatusHandler() method for this.
 
 #ifdef GNASH_DEBUG_METADATA
   log_debug(" Invoking onMetaData");
 #endif
 
-    string_table::key func = getVM().getStringTable().find(PROPNAME(funcname));
+    string_table::key func = getVM().getStringTable().find(funcname);
 
     callMethod(func, as_value(info_obj));
 }
@@ -166,20 +162,22 @@
 {
     // TODO: check for System.onStatus too ! use a private
     // getStatusHandler() method for this.
+    // Copy it to prevent threads changing it.
+    StatusCode code = invalidStatus;
 
-    StatusCode code;
-    while (1)
     {
-        code = popNextPendingStatusNotification();
-
-        // Nothing to do if no more valid notifications.
-        if (code == invalidStatus) return; 
-
-        // Must be a new object every time.
-        as_object* o = getStatusObject(code);
-
-        callMethod(NSV::PROP_ON_STATUS, o);
+        boost::mutex::scoped_lock lock(statusMutex);
+
+        std::swap(code, _statusCode);
     }
+
+    // Nothing to do if no more valid notifications.
+    if (code == invalidStatus) return; 
+
+    // Must be a new object every time.
+    as_object* o = getStatusObject(code);
+
+    callMethod(NSV::PROP_ON_STATUS, o);
 }
 
 void
@@ -187,12 +185,7 @@
 {
     // Get a lock to avoid messing with statuses while processing them
     boost::mutex::scoped_lock lock(statusMutex);
-
-    // status unchanged
-    if ( _lastStatus == status) return;
-
-    _lastStatus = status;
-    _statusQueue.push_back(status);
+    _statusCode = status;
 }
 
 void
@@ -296,29 +289,6 @@
     return o;
 }
 
-NetStream_as::StatusCode
-NetStream_as::popNextPendingStatusNotification()
-{
-    // Get an exclusive lock on the queue
-    boost::mutex::scoped_lock lock(statusMutex);
-
-    // No queued statuses to notify ...
-    if ( _statusQueue.empty() ) return invalidStatus;
-
-    StatusCode nextCode = _statusQueue.front();
-    _statusQueue.pop_front();
-    return nextCode;
-}
-
-void
-NetStream_as::clearStatusQueue()
-{
-    // Get an exclusive lock on the queue
-    boost::mutex::scoped_lock lock(statusMutex);
-
-    _statusQueue.clear();
-}
-
 void
 NetStream_as::setAudioController(character* ch)
 {
@@ -343,38 +313,16 @@
 }
 #endif // GNASH_USE_GC
 
-as_value
-NetStream_as::advanceWrapper(const fn_call& fn)
-{
-    boost::intrusive_ptr<NetStream_as> ptr =
-        ensureType<NetStream_as>(fn.this_ptr);
-
-    ptr->advance();
-    return as_value();
-}
-
 void
 NetStream_as::stopAdvanceTimer()
 {
-    if ( _advanceTimer )
-    {
-        VM& vm = getVM();
-        vm.getRoot().clear_interval_timer(_advanceTimer);
-        _advanceTimer = 0;
-    }
+    getVM().getRoot().removeAdvanceCallback(this);
 }
 
 void
 NetStream_as::startAdvanceTimer()
 {
-    boost::intrusive_ptr<builtin_function> advanceCallback = 
-        new builtin_function(&NetStream_as::advanceWrapper);
-    std::auto_ptr<Timer> timer(new Timer);
-
-    // TODO: base on media file FPS !!! 
-    unsigned long delayMS = 50;
-    timer->setInterval(*advanceCallback, delayMS, this);
-    _advanceTimer = getVM().getRoot().add_interval_timer(timer, true);
+    getVM().getRoot().addAdvanceCallback(this);
 }
 
 
@@ -394,7 +342,7 @@
 }
 
 
-void NetStream_as::pause( PauseMode mode )
+void NetStream_as::pause(PauseMode mode)
 {
     log_debug("::pause(%d) called ", mode);
     switch ( mode )
@@ -659,187 +607,187 @@
     }
 
     // Loop until a good frame is found
-    while ( 1 )
+        while ( 1 )
+        {
+            video = decodeNextVideoFrame();
+            if ( ! video.get() )
+            {
+                log_error("nextVideoFrameTimestamp returned true (%d), "
+                    "but decodeNextVideoFrame returned null, "
+                    "I don't think this should ever happen", nextTimestamp);
+                break;
+            }
+
+            if ( ! m_parser->nextVideoFrameTimestamp(nextTimestamp) )
+            {
+                // the one we decoded was the last one
+#ifdef GNASH_DEBUG_DECODING
+                log_debug("%p.getDecodedVideoFrame(%d): last video frame 
decoded "
+                    "(should set playback status to STOP?)", this, ts);
+#endif 
+                break;
+            }
+            if ( nextTimestamp > ts )
+            {
+                // the next one is in the future, we'll return this one.
+#ifdef GNASH_DEBUG_DECODING
+                log_debug("%p.getDecodedVideoFrame(%d): "
+                    "next video frame is in the future, "
+                    "we'll return this one",
+                    this, ts);
+#endif 
+                break; 
+            }
+        }
+
+        return video;
+    }
+
+    std::auto_ptr<GnashImage> 
+    NetStream_as::decodeNextVideoFrame()
     {
-        video = decodeNextVideoFrame();
+        std::auto_ptr<GnashImage> video;
+
+        if ( ! m_parser.get() )
+        {
+            log_error("decodeNextVideoFrame: no parser available");
+            return video; 
+        }
+
+        std::auto_ptr<media::EncodedVideoFrame> frame = 
m_parser->nextVideoFrame(); 
+        if ( ! frame.get() )
+        {
+#ifdef GNASH_DEBUG_DECODING
+            log_debug("%p.decodeNextVideoFrame(): "
+                "no more video frames in input",
+                this);
+#endif 
+            return video;
+        }
+
+#if 0 // TODO: check if the video is a cue point, if so, call 
processNotify(onCuePoint, object..)
+          // NOTE: should only be done for SWF>=8 ?
+        if ( 1 ) // frame->isKeyFrame() )
+        {
+            as_object* infoObj = new as_object();
+            string_table& st = getVM().getStringTable();
+            infoObj->set_member(st.find("time"), 
as_value(double(frame->timestamp())));
+            infoObj->set_member(st.find("type"), as_value("navigation"));
+            processNotify("onCuePoint", infoObj);
+        }
+#endif
+
+        assert( _videoDecoder.get() ); 
+        
+        // everything we push, we'll pop too..
+        assert( ! _videoDecoder->peek() ); 
+
+        _videoDecoder->push(*frame);
+        video = _videoDecoder->pop();
         if ( ! video.get() )
         {
-            log_error("nextVideoFrameTimestamp returned true (%d), "
-                "but decodeNextVideoFrame returned null, "
-                "I don't think this should ever happen", nextTimestamp);
-            break;
-        }
-
-        if ( ! m_parser->nextVideoFrameTimestamp(nextTimestamp) )
-        {
-            // the one we decoded was the last one
-#ifdef GNASH_DEBUG_DECODING
-            log_debug("%p.getDecodedVideoFrame(%d): last video frame decoded "
-                "(should set playback status to STOP?)", this, ts);
-#endif 
-            break;
-        }
-        if ( nextTimestamp > ts )
-        {
-            // the next one is in the future, we'll return this one.
-#ifdef GNASH_DEBUG_DECODING
-            log_debug("%p.getDecodedVideoFrame(%d): "
-                "next video frame is in the future, "
-                "we'll return this one",
-                this, ts);
-#endif 
-            break; 
-        }
-    }
-
-    return video;
-}
-
-std::auto_ptr<GnashImage> 
-NetStream_as::decodeNextVideoFrame()
-{
-    std::auto_ptr<GnashImage> video;
-
-    if ( ! m_parser.get() )
-    {
-        log_error("decodeNextVideoFrame: no parser available");
-        return video; 
-    }
-
-    std::auto_ptr<media::EncodedVideoFrame> frame = 
m_parser->nextVideoFrame(); 
-    if ( ! frame.get() )
-    {
-#ifdef GNASH_DEBUG_DECODING
-        log_debug("%p.decodeNextVideoFrame(): "
-            "no more video frames in input",
-            this);
-#endif 
+            // TODO: tell more about the failure
+            log_error(_("Error decoding encoded video frame in NetStream 
input"));
+        }
+
         return video;
     }
 
-#if 0 // TODO: check if the video is a cue point, if so, call 
processNotify(onCuePoint, object..)
-      // NOTE: should only be done for SWF>=8 ?
-    if ( 1 ) // frame->isKeyFrame() )
-    {
-        as_object* infoObj = new as_object();
-        string_table& st = getVM().getStringTable();
-        infoObj->set_member(st.find("time"), 
as_value(double(frame->timestamp())));
-        infoObj->set_member(st.find("type"), as_value("navigation"));
-        processNotify("onCuePoint", infoObj);
-    }
-#endif
-
-    assert( _videoDecoder.get() ); 
-    
-    // everything we push, we'll pop too..
-    assert( ! _videoDecoder->peek() ); 
-
-    _videoDecoder->push(*frame);
-    video = _videoDecoder->pop();
-    if ( ! video.get() )
-    {
-        // TODO: tell more about the failure
-        log_error(_("Error decoding encoded video frame in NetStream input"));
-    }
-
-    return video;
-}
-
-BufferedAudioStreamer::CursoredBuffer*
-NetStream_as::decodeNextAudioFrame()
-{
-    assert ( m_parser.get() );
-
-    std::auto_ptr<media::EncodedAudioFrame> frame = 
m_parser->nextAudioFrame(); 
-    if ( ! frame.get() )
-    {
+    BufferedAudioStreamer::CursoredBuffer*
+    NetStream_as::decodeNextAudioFrame()
+    {
+        assert ( m_parser.get() );
+
+        std::auto_ptr<media::EncodedAudioFrame> frame = 
m_parser->nextAudioFrame(); 
+        if ( ! frame.get() )
+        {
 #ifdef GNASH_DEBUG_DECODING
-        log_debug("%p.decodeNextAudioFrame: "
-            "no more video frames in input",
-            this);
+            log_debug("%p.decodeNextAudioFrame: "
+                "no more video frames in input",
+                this);
 #endif
-        return 0;
-    }
-
-    // TODO: make the buffer cursored later ?
-    BufferedAudioStreamer::CursoredBuffer* raw =
-        new BufferedAudioStreamer::CursoredBuffer();
-    raw->m_data = _audioDecoder->decode(*frame, raw->m_size);
-
-    // TODO: let the sound_handler do this .. sounds cleaner
-    if ( _audioController ) 
-    {
-        character* ch = _audioController->get();
-        if ( ch )
+            return 0;
+        }
+
+        // TODO: make the buffer cursored later ?
+        BufferedAudioStreamer::CursoredBuffer* raw =
+            new BufferedAudioStreamer::CursoredBuffer();
+        raw->m_data = _audioDecoder->decode(*frame, raw->m_size);
+
+        // TODO: let the sound_handler do this .. sounds cleaner
+        if ( _audioController ) 
         {
-            int vol = ch->getWorldVolume();
-            if ( vol != 100 )
+            character* ch = _audioController->get();
+            if ( ch )
             {
-                // NOTE: adjust_volume assumes samples 
-                // are 16 bits in size, and signed.
-                // Size is still given in bytes..
-                adjust_volume(reinterpret_cast<boost::int16_t*>(raw->m_data),
-                        raw->m_size, vol);
+                int vol = ch->getWorldVolume();
+                if ( vol != 100 )
+                {
+                    // NOTE: adjust_volume assumes samples 
+                    // are 16 bits in size, and signed.
+                    // Size is still given in bytes..
+                    
adjust_volume(reinterpret_cast<boost::int16_t*>(raw->m_data),
+                            raw->m_size, vol);
+                }
             }
         }
-    }
 
 #ifdef GNASH_DEBUG_DECODING
-    log_debug("NetStream_as::decodeNextAudioFrame: "
-        "%d bytes of encoded audio "
-        "decoded to %d bytes",
-        frame->dataSize,
-        raw->m_size);
+        log_debug("NetStream_as::decodeNextAudioFrame: "
+            "%d bytes of encoded audio "
+            "decoded to %d bytes",
+            frame->dataSize,
+            raw->m_size);
 #endif 
 
-    raw->m_ptr = raw->m_data;
-
-    return raw;
-}
-
-bool NetStream_as::decodeMediaFrame()
-{
-    return false;
-}
-
-void
-NetStream_as::seek(boost::uint32_t posSeconds)
-{
-    GNASH_REPORT_FUNCTION;
-
-    // We'll mess with the input here
-    if ( ! m_parser.get() )
-    {
-        log_debug("NetStream_as::seek(%d): no parser, no party", posSeconds);
-        return;
-    }
-
-    // Don't ask me why, but NetStream_as::seek() takes seconds...
-    boost::uint32_t pos = posSeconds*1000;
-
-    // We'll pause the clock source and mark decoders as buffering.
-    // In this way, next advance won't find the source time to 
-    // be a lot of time behind and chances to get audio buffer
-    // overruns will reduce.
-    // ::advance will resume the playbackClock if DEC_BUFFERING...
-    //
-    _playbackClock->pause();
-
-    // Seek to new position
-    boost::uint32_t newpos = pos;
-    if ( ! m_parser->seek(newpos) )
-    {
+        raw->m_ptr = raw->m_data;
+
+        return raw;
+    }
+
+    bool NetStream_as::decodeMediaFrame()
+    {
+        return false;
+    }
+
+    void
+    NetStream_as::seek(boost::uint32_t posSeconds)
+    {
+        GNASH_REPORT_FUNCTION;
+
+        // We'll mess with the input here
+        if ( ! m_parser.get() )
+        {
+            log_debug("NetStream_as::seek(%d): no parser, no party", 
posSeconds);
+            return;
+        }
+
+        // Don't ask me why, but NetStream_as::seek() takes seconds...
+        boost::uint32_t pos = posSeconds*1000;
+
+        // We'll pause the clock source and mark decoders as buffering.
+        // In this way, next advance won't find the source time to 
+        // be a lot of time behind and chances to get audio buffer
+        // overruns will reduce.
+        // ::advance will resume the playbackClock if DEC_BUFFERING...
+        //
+        _playbackClock->pause();
+
+        // Seek to new position
+        boost::uint32_t newpos = pos;
+        if ( ! m_parser->seek(newpos) )
+        {
 #ifdef GNASH_DEBUG_STATUS
-        log_debug("Setting invalidTime status");
+            log_debug("Setting invalidTime status");
 #endif
-        setStatus(invalidTime);
-        // we won't be *BUFFERING*, so resume now
-        _playbackClock->resume(); 
-        return;
-    }
-    log_debug("m_parser->seek(%d) returned %d", pos, newpos);
+            setStatus(invalidTime);
+            // we won't be *BUFFERING*, so resume now
+            _playbackClock->resume(); 
+            return;
+        }
+        log_debug("m_parser->seek(%d) returned %d", pos, newpos);
 
-    // cleanup audio queue, so won't be consumed while seeking
+        // cleanup audio queue, so won't be consumed while seeking
     _audioStreamer.cleanAudioQueue();
     
     // 'newpos' will always be on a keyframe (supposedly)
@@ -1294,16 +1242,14 @@
 
 
 void
-NetStream_as::advance()
+NetStream_as::advanceState()
 {
     // Check if there are any new status messages, and if we should
     // pass them to a event handler
     processStatusNotifications();
 
-    // Nothing to do if we don't have a parser. Unregister the timer, as
-    // all status notifications should have been processed.
-    if ( ! m_parser.get() ) {
-        stopAdvanceTimer();
+    // Nothing to do if we don't have a parser.
+    if (!m_parser.get()) {
         return;
     }
 
@@ -1430,7 +1376,6 @@
 void
 NetStream_as::unpausePlayback()
 {
-    GNASH_REPORT_FUNCTION;
 
     PlayHead::PlaybackStatus oldStatus = 
         _playHead.setState(PlayHead::PLAY_PLAYING);

=== modified file 'libcore/asobj/NetStream_as.h'
--- a/libcore/asobj/NetStream_as.h      2009-02-25 22:33:03 +0000
+++ b/libcore/asobj/NetStream_as.h      2009-03-16 09:34:54 +0000
@@ -353,14 +353,14 @@
     ///
     boost::int32_t time();
 
-    /// Called at the SWF framerate. Used to process queued status messages
+    /// Called at the SWF heart-beat. Used to process queued status messages
     /// and (re)start after a buffering pause. In NetStreamFfmpeg it is also
-    /// used to find the next video frame to be shown, though this might 
change.
-    void advance();
+    /// used to find the next video frame to be shown, though this might
+    /// change.
+    void advanceState();
     
     /// Returns the current framerate in frames per second.
     double getCurrentFPS()  { return 0; }
-    
 
     /// Sets the NetConnection needed to access external files
     //
@@ -374,14 +374,16 @@
     /// Return true if the NetStream has an associated NetConnection
     bool isConnected() const { return (_netCon); }
 
-    /// Specifies the number of milliseconds to buffer before starting to 
display the stream.
+    /// Specifies the number of milliseconds to buffer before starting
+    /// to display the stream.
     //
     /// @param time
     /// The time in milliseconds that should be buffered.
     ///
     void setBufferTime(boost::uint32_t time);
 
-    /// Returns what the buffer time has been set to. (100 miliseconds is 
default)
+    /// Returns what the buffer time has been set to. (100 milliseconds
+    /// is default)
     //
     /// @return The size of the buffer in milliseconds.
     ///
@@ -396,8 +398,8 @@
     ///
     long bytesTotal();
 
-    /// Returns the number of millisecond of the media file that is buffered 
and 
-    /// yet to be played
+    /// Returns the number of millisecond of the media file that is
+    /// buffered and yet to be played
     //
     /// @return Returns the number of millisecond of the media file that is 
     /// buffered and yet to be played
@@ -411,7 +413,8 @@
 
     /// Returns the video frame closest to current cursor. See time().
     //
-    /// @return a image containing the video frame, a NULL auto_ptr if none 
were ready
+    /// @return a image containing the video frame, a NULL auto_ptr if
+    /// none were ready
     ///
     std::auto_ptr<GnashImage> get_video();
     
@@ -421,8 +424,6 @@
         _invalidatedVideoCharacter = ch;
     }
 
-
-
     /// Callback used by sound_handler to get audio data
     //
     /// This is a sound_handler::aux_streamer_ptr type.
@@ -442,6 +443,18 @@
         DEC_BUFFERING
     };
 
+    typedef std::pair<std::string, std::string> NetStreamStatus;
+
+    /// Get 'status' (first) and 'level' (second) strings for given status code
+    //
+    /// Any invalid code, out of bound or explicitly invalid (invalidCode) 
+    /// returns two empty strings.
+    ///
+    void getStatusCodeInfo(StatusCode code, NetStreamStatus& info);
+
+    /// Return a newly allocated information object for the given status
+    as_object* getStatusObject(StatusCode code);
+
     /// Initialize video decoder and (if successful) PlayHead consumer 
     //
     /// @param info Video codec information
@@ -454,12 +467,6 @@
     ///
     void initAudioDecoder(const media::AudioInfo& parser);
 
-    DecodingState _decoding_state;
-
-    // Mutex protecting _playback_state and _decoding_state
-    // (not sure a single one is appropriate)
-    boost::mutex _state_mutex;
-
     // Setups the playback
     bool startPlayback();
 
@@ -484,7 +491,7 @@
 
     /// Update the image/videoframe to be returned by next get_video() call.
     //
-    /// Uses by ::advance().
+    /// Used by advanceState().
     ///
     /// Note that get_video will be called by Video::display(), which
     /// is usually called right after Video::advance(), so the result
@@ -496,7 +503,7 @@
     /// If true, video is consumed/refreshed even if playhead is paused.
     /// By default this is false, but will be used on ::seek (user-reguested)
     ///
-    void refreshVideoFrame(bool alsoIfPaused=false);
+    void refreshVideoFrame(bool alsoIfPaused = false);
 
     /// Refill audio buffers, so to contain new frames since last run
     /// and up to current timestamp
@@ -537,6 +544,17 @@
 
     DecodingState decodingStatus(DecodingState newstate = DEC_NONE);
 
+    /// Parse a chunk of input
+    /// Currently blocks, ideally should parse as much
+    /// as possible w/out blocking
+    void parseNextChunk();
+
+    DecodingState _decoding_state;
+
+    // Mutex protecting _playback_state and _decoding_state
+    // (not sure a single one is appropriate)
+    boost::mutex _state_mutex;
+    
     /// Video decoder
     std::auto_ptr<media::VideoDecoder> _videoDecoder;
 
@@ -550,7 +568,7 @@
     bool _audioInfoKnown;
 
     /// Virtual clock used as playback clock source
-    std::auto_ptr<InterruptableVirtualClock> _playbackClock;
+    boost::scoped_ptr<InterruptableVirtualClock> _playbackClock;
 
     /// Playback control device 
     PlayHead _playHead;
@@ -561,11 +579,6 @@
     // Current media handler
     media::MediaHandler* _mediaHandler;
 
-    /// Parse a chunk of input
-    /// Currently blocks, ideally should parse as much
-    /// as possible w/out blocking
-    void parseNextChunk();
-
     /// Input stream
     //
     /// This should just be a temporary variable, transferred
@@ -576,50 +589,12 @@
     /// The buffered audio streamer
     BufferedAudioStreamer _audioStreamer;
 
-    /// Pop next queued status notification from the queue
-    //
-    /// Lock the statusMutex during operations
-    ///
-    /// @return The status code to notify, or invalidStatus when
-    ///     the queue is empty
-    ///
-    StatusCode popNextPendingStatusNotification();
-
-    /// Clear status notification queue
-    //
-    /// Lock the statusMutex during operations
-    ///
-    void clearStatusQueue();
-
-    // Queue of status notifications.
-    typedef std::deque<StatusCode> StatusQueue;
-
     /// List of status messages to be processed
-    StatusQueue _statusQueue;
+    StatusCode _statusCode;
 
     /// Mutex protecting _statusQueue
     boost::mutex statusMutex;
 
-    /// Last status code (to avoid consecutively notifying the same event)
-    StatusCode _lastStatus;
-
-    typedef std::pair<std::string, std::string> NetStreamStatus;
-
-    /// Get 'status' (first) and 'level' (second) strings for given status code
-    //
-    /// Any invalid code, out of bound or explicitly invalid (invalidCode) 
-    /// returns two empty strings.
-    ///
-    void getStatusCodeInfo(StatusCode code, NetStreamStatus& info);
-
-    /// Return a newly allocated information object for the given status
-    as_object* getStatusObject(StatusCode code);
-
-    /// hack for using a Timer to drive ::advance calls
-    static as_value advanceWrapper(const fn_call& fn);
-
-    /// Identifier of the advance timer
-    unsigned int _advanceTimer;
 };
 
 

=== modified file 'libcore/asobj/Sound_as.cpp'
--- a/libcore/asobj/Sound_as.cpp        2009-02-25 22:33:03 +0000
+++ b/libcore/asobj/Sound_as.cpp        2009-03-14 13:51:05 +0000
@@ -1018,25 +1018,8 @@
 void
 Sound_as::startProbeTimer()
 {
-    boost::intrusive_ptr<builtin_function> cb = 
-        new builtin_function(&Sound_as::probeAudioWrapper);
-    std::auto_ptr<Timer> timer(new Timer);
-
-    // 2 times each second (83 would be 12 times each second)
-    unsigned long delayMS = 500; 
-    timer->setInterval(*cb, delayMS, this);
-    _probeTimer = getVM().getRoot().add_interval_timer(timer, true);
-}
-
-/*private static*/
-as_value
-Sound_as::probeAudioWrapper(const fn_call& fn)
-{
-    //GNASH_REPORT_FUNCTION;
-
-    boost::intrusive_ptr<Sound_as> ptr = ensureType<Sound_as>(fn.this_ptr);
-    ptr->probeAudio();
-    return as_value();
+    _probeTimer = 1;
+    getVM().getRoot().addAdvanceCallback(this);
 }
 
 /*private*/
@@ -1050,12 +1033,18 @@
     if ( _probeTimer )
     {
         VM& vm = getVM();
-        vm.getRoot().clear_interval_timer(_probeTimer);
-        log_debug(" clear_interval_timer(%d) called", _probeTimer);
+        vm.getRoot().removeAdvanceCallback(this);
+        log_debug(" sound callback removed");
         _probeTimer = 0;
     }
 }
 
+void
+Sound_as::advanceState()
+{
+    probeAudio();
+}
+
 /*private*/
 void
 Sound_as::probeAudio()

=== modified file 'libcore/asobj/Sound_as.h'
--- a/libcore/asobj/Sound_as.h  2009-02-25 22:33:03 +0000
+++ b/libcore/asobj/Sound_as.h  2009-03-14 13:51:05 +0000
@@ -171,11 +171,11 @@
     /// Unregister the probe timer
     void stopProbeTimer();
 
+    virtual void advanceState();
+
     /// Probe audio
     void probeAudio();
 
-    static as_value probeAudioWrapper(const fn_call&);
-
     int _probeTimer;
 
     bool _soundCompleted;

=== modified file 'libcore/asobj/XMLSocket_as.cpp'
--- a/libcore/asobj/XMLSocket_as.cpp    2009-02-25 22:33:03 +0000
+++ b/libcore/asobj/XMLSocket_as.cpp    2009-03-14 17:27:38 +0000
@@ -58,7 +58,6 @@
 static as_value xmlsocket_close(const fn_call& fn);
 
 // These are the event handlers called for this object
-static as_value xmlsocket_inputChecker(const fn_call& fn);
 static as_value xmlsocket_onData(const fn_call& fn);
 
 static as_object* getXMLSocketInterface();
@@ -82,10 +81,17 @@
     void send(std::string str);
     void close();
 
+
+    virtual void advanceState()
+    {
+        if (!_connected) return;
+        checkForIncomingData();
+    }
+
+private:
+
        void checkForIncomingData();
-
-private:
-
+    
     void fillMessageList(MessageList& msgs);
 
        /// Return the as_function with given name, converting case if needed
@@ -107,6 +113,7 @@
 
 XMLSocket_as::~XMLSocket_as()
 {
+    getVM().getRoot().removeAdvanceCallback(this);
 }
 
 bool
@@ -292,20 +299,7 @@
     log_debug(_("XMLSocket.connect(): trying to call onConnect"));
     ptr->callMethod(NSV::PROP_ON_CONNECT, true);
            
-
-    // This is bad and should be rewritten.
-    log_debug(_("Setting up timer for calling XMLSocket.onData()"));
-
-    std::auto_ptr<Timer> timer(new Timer);
-    boost::intrusive_ptr<builtin_function> ondata_handler =
-        new builtin_function(&xmlsocket_inputChecker, NULL);
-    // just make sure it's expired at every frame iteration (20 FPS used here)
-    unsigned interval = 50;
-    timer->setInterval(*ondata_handler, interval,
-            boost::dynamic_pointer_cast<as_object>(ptr));
-
-    VM& vm = ptr->getVM();
-    vm.getRoot().add_interval_timer(timer, true);
+    ptr->getVM().getRoot().addAdvanceCallback(ptr.get());
 
     log_debug(_("Timer set"));
 
@@ -321,7 +315,9 @@
 {
     GNASH_REPORT_FUNCTION;
     
-    boost::intrusive_ptr<XMLSocket_as> ptr = 
ensureType<XMLSocket_as>(fn.this_ptr);
+    boost::intrusive_ptr<XMLSocket_as> ptr =
+        ensureType<XMLSocket_as>(fn.this_ptr);
+
     const std::string& str = fn.arg(0).to_string();
     ptr->send(str);
     return as_value();
@@ -336,7 +332,8 @@
 {
     GNASH_REPORT_FUNCTION;
     
-    boost::intrusive_ptr<XMLSocket_as> ptr = 
ensureType<XMLSocket_as>(fn.this_ptr);
+    boost::intrusive_ptr<XMLSocket_as> ptr =
+        ensureType<XMLSocket_as>(fn.this_ptr);
 
     // If we're not connected, there's nothing to do
     if (!ptr->connected()) return as_value();
@@ -363,22 +360,6 @@
     return as_value(xmlsock_obj);
 }
 
-
-as_value
-xmlsocket_inputChecker(const fn_call& fn)
-{
-    boost::intrusive_ptr<XMLSocket_as> ptr = 
ensureType<XMLSocket_as>(fn.this_ptr);
-    if ( ! ptr->connected() )
-    {
-        log_error(_("%s: not connected"), __FUNCTION__);
-        return as_value();
-    }
-
-    ptr->checkForIncomingData();
-
-    return as_value();
-}
-
 as_value
 xmlsocket_onData(const fn_call& fn)
 {

=== modified file 'libcore/asobj/XML_as.cpp'
--- a/libcore/asobj/XML_as.cpp  2009-01-22 20:10:39 +0000
+++ b/libcore/asobj/XML_as.cpp  2009-03-16 10:40:30 +0000
@@ -896,21 +896,20 @@
     as_object* thisPtr = fn.this_ptr.get();
     assert(thisPtr);
 
-    // See 
http://gitweb.freedesktop.org/?p=swfdec/swfdec.git;a=blob;f=libswfdec/swfdec_initialize.as
-
-    as_value src; src.set_null();
-    if ( fn.nargs ) src = fn.arg(0);
-
-    if ( ! src.is_null() )
-    {
+    // See http://gitweb.freedesktop.org/?p=swfdec/swfdec.git;
+    // a=blob;f=libswfdec/swfdec_initialize.as
+
+    as_value src;
+    if (fn.nargs) src = fn.arg(0);
+
+    if (!src.is_undefined()) {
         thisPtr->set_member(NSV::PROP_LOADED, true);
         thisPtr->callMethod(NSV::PROP_PARSE_XML, src);
         thisPtr->callMethod(NSV::PROP_ON_LOAD, true);
     }
-    else
-    {
-        thisPtr->set_member(NSV::PROP_LOADED, true);
-        thisPtr->callMethod(NSV::PROP_ON_LOAD, true);
+    else {
+        thisPtr->set_member(NSV::PROP_LOADED, false);
+        thisPtr->callMethod(NSV::PROP_ON_LOAD, false);
     }
 
     return as_value();

=== modified file 'libcore/movie_root.cpp'
--- a/libcore/movie_root.cpp    2009-03-13 22:46:37 +0000
+++ b/libcore/movie_root.cpp    2009-03-16 09:33:19 +0000
@@ -1001,19 +1001,12 @@
 
 
 unsigned int
-movie_root::add_interval_timer(std::auto_ptr<Timer> timer, bool internal)
+movie_root::add_interval_timer(std::auto_ptr<Timer> timer)
 {
        assert(timer.get());
        assert(testInvariant());
                        
        int id = ++_lastTimerId;
-       if ( internal ) id = -id;
-
-       if ( _intervalTimers.size() >= 255 )
-       {
-               // TODO: Why this limitation ? 
-               log_error("FIXME: %d timers currently active, won't add another 
one", _intervalTimers.size());
-       }
 
        assert(_intervalTimers.find(id) == _intervalTimers.end());
        _intervalTimers[id] = timer.release(); 
@@ -1048,7 +1041,7 @@
 
     try {
 
-           if ( (now - _lastMovieAdvancement) >= _movieAdvancementDelay )
+           if ((now - _lastMovieAdvancement) >= _movieAdvancementDelay)
            {
             advanced = true;
                    advanceMovie();
@@ -1060,23 +1053,21 @@
                    // and printing a warnign when we're later then tolerated...
                    //
                    _lastMovieAdvancement = now; // or _vm.getTime(); ?
+
            }
-
-           // TODO: execute timers ?
+        
+        executeAdvanceCallbacks();
+           
            executeTimers();
+       
        }
-       catch (ActionLimitException& al)
-    {
-           //log_error(_("ActionLimits hit during advance: %s. Disabling 
scripts"), al.what());
-           //disableScripts();
-
+       catch (ActionLimitException& al) {
         // The PP does not disable scripts when the stack limit is reached,
         // but rather struggles on. 
            log_error(_("Action limit hit during advance: %s"), al.what());
            clearActionQueue();
     }
-    catch (ActionParserException& e)
-    {
+    catch (ActionParserException& e) {
         log_error(_("Buffer overread during advance: %s"), e.what());
         clearActionQueue();
     }
@@ -1087,32 +1078,31 @@
 void
 movie_root::advanceMovie()
 {
-       // GNASH_REPORT_FUNCTION;
 
        // Do mouse drag, if needed
        doMouseDrag();
 
-       // Advance all non-unloaded characters in the LiveChars list
+    // Advance all non-unloaded characters in the LiveChars list
        // in reverse order (last added, first advanced)
        // NOTE: can throw ActionLimitException
        advanceLiveChars(); 
 
-       // Process loadMovie requests
-       // 
-       // NOTE: should be done before executing timers,
-       //       see swfdec's test/trace/loadmovie-case-{5,6}.swf 
-       // NOTE: processing loadMovie requests after advanceLiveChars
-       //       is known to fix more tests in misc-mtasc.all/levels.swf
-       //       to be checked if it keeps the swfdec testsuite safe
-       processLoadMovieRequests();
-
-       // Process queued actions
-       // NOTE: can throw ActionLimitException
-       processActionQueue();
-
-       cleanupAndCollect();
-
-       assert(testInvariant());
+    // Process loadMovie requests
+    // 
+    // NOTE: should be done before executing timers,
+    //          see swfdec's test/trace/loadmovie-case-{5,6}.swf 
+    // NOTE: processing loadMovie requests after advanceLiveChars
+    //       is known to fix more tests in misc-mtasc.all/levels.swf
+    //       to be checked if it keeps the swfdec testsuite safe
+    processLoadMovieRequests();
+
+    // Process queued actions
+    // NOTE: can throw ActionLimitException
+    processActionQueue();
+
+    cleanupAndCollect();
+
+    assert(testInvariant());
 }
 
 
@@ -1258,7 +1248,7 @@
 
     assert(testInvariant());
 
-    if( ! copy.empty() )
+    if ( ! copy.empty() )
        {
                // process actions queued in the above step
                processActionQueue();
@@ -1281,21 +1271,13 @@
 }
 
 
-/* static private */
-void movie_root::remove_listener(CharacterList& ll, character* listener)
+void
+movie_root::remove_listener(CharacterList& ll, character* listener)
 {
        assert(listener);
 
-#if 0
-       for(CharacterList::iterator iter = ll.begin(); iter != ll.end(); )
-       {
-               if(*iter == listener) iter = ll.erase(iter);
-               else ++iter;
-       }
-#else
-    // This should be faster.  
-       ll.remove_if(std::bind2nd(std::equal_to<boost::intrusive_ptr<character> 
>(), listener));
-#endif 
+       ll.remove_if(std::bind2nd(
+                std::equal_to<boost::intrusive_ptr<character> >(), listener));
 }
 
 void
@@ -1697,11 +1679,22 @@
 }
 
 void
+movie_root::addAdvanceCallback(as_object* obj)
+{
+    _objectCallbacks.insert(obj);
+}
+
+void
+movie_root::removeAdvanceCallback(as_object* obj)
+{
+    _objectCallbacks.erase(obj);
+}
+
+void
 movie_root::processActionQueue()
 {
        if ( _disableScripts )
        {
-               //log_debug(_("Scripts are disabled, global instance list has 
%d elements"), _liveChars.size());
                /// cleanup anything pushed later..
                clearActionQueue();
                return;
@@ -1748,50 +1741,47 @@
 
        std::auto_ptr<ExecutableCode> code ( new GlobalCode(buf, target) );
 
-#if 0
-       // Immediately execute code targetted at a lower level while processing
-       // an higher level.
-       if ( processingActions() && lvl < _processingActionLevel )
-       {
-               log_debug("Action pushed in level %d executed immediately (as 
we are currently executing level %d)", lvl, _processingActionLevel);
-               code->execute();
-               return;
-       }
-#endif
-
        _actionQueue[lvl].push_back(code.release());
 }
 
 void
-movie_root::pushAction(boost::intrusive_ptr<as_function> func, 
boost::intrusive_ptr<character> target, int lvl)
+movie_root::pushAction(boost::intrusive_ptr<as_function> func,
+        boost::intrusive_ptr<character> target, int lvl)
 {
        assert(lvl >= 0 && lvl < apSIZE);
 #ifdef GNASH_DEBUG
-       log_debug("Pushed function (event hanlder?) with target %s", 
target->getTargetPath());
+       log_debug("Pushed function (event hanlder?) with target %s",
+            target->getTargetPath());
 #endif
 
        std::auto_ptr<ExecutableCode> code ( new FunctionCode(func, target) );
 
-#if 0
-       // Immediately execute code targetted at a lower level while processing
-       // an higher level.
-       if ( processingActions() && lvl < _processingActionLevel )
-       {
-               log_debug("Action pushed in level %d executed immediately (as 
we are currently executing level %d)", lvl, _processingActionLevel);
-               code->execute();
-               return;
-       }
-#endif
-
        _actionQueue[lvl].push_back(code.release());
 }
 
-/* private */
+void
+movie_root::executeAdvanceCallbacks()
+{
+
+    if (_objectCallbacks.empty()) return;
+
+    // Copy it, as the call can change the original, which is not only 
+    // bad for invalidating iterators, but also allows infinite recursion.
+    std::vector<as_object*> currentCallbacks;
+    std::copy(_objectCallbacks.begin(), _objectCallbacks.end(),
+            std::back_inserter(currentCallbacks));
+
+    std::for_each(currentCallbacks.begin(), currentCallbacks.end(), 
+            std::mem_fun(&as_object::advanceState));
+
+    processActionQueue();
+}
+
 void
 movie_root::executeTimers()
 {
 #ifdef GNASH_DEBUG_TIMERS_EXPIRATION
-        log_debug("Checking %d timers for expiration", _intervalTimers.size());
+        log_debug("Checking %d timers for expiry", _intervalTimers.size());
 #endif
 
        unsigned long now = _vm.getTime();
@@ -1799,9 +1789,9 @@
        typedef std::multimap<unsigned int, Timer*> ExpiredTimers;
        ExpiredTimers expiredTimers;
 
-       for (TimerMap::iterator it=_intervalTimers.begin(), 
itEnd=_intervalTimers.end();
-                       it != itEnd; )
-       {
+       for (TimerMap::iterator it=_intervalTimers.begin(),
+            itEnd=_intervalTimers.end(); it != itEnd; ) {
+
                // Get an iterator to next element, as we'll use
                // erase to drop cleared timers, and that would
                // invalidate the current iterator.
@@ -1836,17 +1826,11 @@
        }
 
        for (ExpiredTimers::iterator it=expiredTimers.begin(),
-                       itEnd=expiredTimers.end();
-               it != itEnd; ++it)
-       {
+            itEnd=expiredTimers.end(); it != itEnd; ++it) {
                it->second->executeAndReset();
        }
 
-       if ( ! expiredTimers.empty() )
-       {
-               // process actions queued when executing interval callbacks
-               processActionQueue();
-       }
+    if (!expiredTimers.empty()) processActionQueue();
 
 }
 
@@ -1882,15 +1866,15 @@
         i->second->markReachableResources();
     }
 
+    std::for_each(_objectCallbacks.begin(), _objectCallbacks.end(),
+            std::mem_fun(&as_object::setReachable));
+
     // Mark resources reachable by queued action code
     for (int lvl=0; lvl<apSIZE; ++lvl)
     {
         const ActionQueue& q = _actionQueue[lvl];
-        for (ActionQueue::const_iterator i=q.begin(), e=q.end();
-                i != e; ++i)
-        {
-            (*i)->markReachableResources();
-        }
+        std::for_each(q.begin(), q.end(),
+                std::mem_fun(&ExecutableCode::markReachableResources));
     }
 
     // Mark global Key object
@@ -1907,31 +1891,28 @@
     // NOTE: we don't need to mark _liveChars as any elements in that list
     //       should be NOT unloaded and thus marked as reachable by their
     //       parent.
-    //std::for_each(_liveChars.begin(), _liveChars.end(), 
boost::bind(&character::setReachable, _1));
 #if GNASH_PARANOIA_LEVEL > 1
     for (LiveChars::const_iterator i=_liveChars.begin(), e=_liveChars.end();
-            i!=e; ++i)
-    {
+            i!=e; ++i) {
         assert((*i)->isReachable());
     }
 #endif
     
-    // NOTE: cleanupUnloadedListeners should have cleaned up all unloaded key 
listeners 
-    //       the remaining ones should be marked by their parents
-    //std::for_each(m_key_listeners.begin(), m_key_listeners.end(), 
boost::bind(&character::setReachable, _1));
+    // NOTE: cleanupUnloadedListeners should have cleaned up all unloaded
+    // key listeners. The remaining ones should be marked by their parents
 #if GNASH_PARANOIA_LEVEL > 1
-    for (LiveChars::const_iterator i=m_key_listeners.begin(), 
e=m_key_listeners.end(); i!=e; ++i)
-    {
+    for (LiveChars::const_iterator i=m_key_listeners.begin(),
+            e=m_key_listeners.end(); i!=e; ++i) {
         assert((*i)->isReachable());
     }
 #endif
 
-    // NOTE: cleanupUnloadedListeners should have cleaned up all unloaded 
mouse listeners 
-    //       the remaining ones should be marked by their parents
-    //std::for_each(m_mouse_listeners.begin(), m_mouse_listeners.end(), 
boost::bind(&character::setReachable, _1));
+    // NOTE: cleanupUnloadedListeners should have cleaned up all
+    // unloaded mouse listeners. The remaining ones should be marked by
+    // their parents
 #if GNASH_PARANOIA_LEVEL > 1
-    for (LiveChars::const_iterator i=m_mouse_listeners.begin(), 
e=m_mouse_listeners.end(); i!=e; ++i)
-    {
+    for (LiveChars::const_iterator i = m_mouse_listeners.begin(),
+            e = m_mouse_listeners.end(); i!=e; ++i) {
         assert((*i)->isReachable());
     }
 #endif

=== modified file 'libcore/movie_root.h'
--- a/libcore/movie_root.h      2009-03-12 10:14:31 +0000
+++ b/libcore/movie_root.h      2009-03-14 15:20:39 +0000
@@ -325,8 +325,11 @@
     ///         for subsequent call to clear_interval_timer.
     ///         It will NEVER be zero.
     ///
-    unsigned int add_interval_timer(std::auto_ptr<Timer> timer,
-            bool internal = false);
+    unsigned int add_interval_timer(std::auto_ptr<Timer> timer);
+
+    void addAdvanceCallback(as_object* obj);
+
+    void removeAdvanceCallback(as_object* obj);
 
     /// Remove timer identified by given integer
     //
@@ -927,6 +930,9 @@
     LiveChars _liveChars;
 
     /// Execute expired timers
+    void executeAdvanceCallbacks();
+    
+    /// Execute expired timers
     void executeTimers();
 
     /// Notify the global Key ActionScript object about a key status change
@@ -984,6 +990,10 @@
 
     MouseButtonState  m_mouse_button_state;
 
+    /// Objects requesting a callback on every movie_root::advance()
+    typedef std::set<as_object*> ObjectCallbacks;
+    ObjectCallbacks _objectCallbacks;
+
     typedef std::map<int, Timer*> TimerMap;
 
     TimerMap _intervalTimers;

=== modified file 'testsuite/misc-ming.all/LoadVarsTest.c'
--- a/testsuite/misc-ming.all/LoadVarsTest.c    2009-02-25 22:33:03 +0000
+++ b/testsuite/misc-ming.all/LoadVarsTest.c    2009-03-16 10:40:30 +0000
@@ -131,7 +131,7 @@
     SWFMovie_nextFrame(mo);
    
     // Check result, restore state.
-    xcheck_equals(mo, "dataString",
+    check_equals(mo, "dataString",
                  "'0: onData called with undefined argument undefined'");
     check_equals(mo, "l.loaded", "false");
     add_actions(mo, "l.onData = odatB;");
@@ -149,7 +149,7 @@
 
     SWFMovie_nextFrame(mo);
           
-    xcheck_equals(mo, "loadString",
+    check_equals(mo, "loadString",
                  "'1: onLoad called with boolean argument false'");
     check_equals(mo, "l.loaded", "false");
     add_actions(mo, "l.onLoad = olB;");
@@ -170,7 +170,7 @@
     SWFMovie_nextFrame(mo);
           
     check_equals(mo, "loadString", "''");
-    xcheck_equals(mo, "dataString",
+    check_equals(mo, "dataString",
                  "'2: onData called with undefined argument undefined'");
     check_equals(mo, "l.loaded", "false");
     add_actions(mo, "l.onLoad = olB;"
@@ -194,7 +194,7 @@
 
     SWFMovie_nextFrame(mo);
     // check_equals is too braindead to do this without escaping.
-    xcheck_equals(mo, "escape(dataString)",
+    check_equals(mo, "escape(dataString)",
            "'3%3A%20onData%20called%20with%20string%20argument%20v2%5Fvar1%3D"
         "val1%26v2%5Fvar2%3Dval2%26%0A'");
     check_equals(mo, "l.loaded", "false");
@@ -215,15 +215,15 @@
     add_actions(mo, "stop();");
 
     SWFMovie_nextFrame(mo);
-    xcheck_equals(mo, "loadString",
+    check_equals(mo, "loadString",
                  "'4: onLoad called with boolean argument true'");
     add_actions(mo, "l.onLoad = olB;");
 
     /// decode is called from onData (i.e. it's called when we overwrite
     /// onLoad, not onData).
-    xcheck_equals(mo, "decodeCalled", "1");
+    check_equals(mo, "decodeCalled", "1");
     // check_equals is too braindead to do this without escaping.
-    xcheck_equals(mo, "escape(decodeString)",
+    check_equals(mo, "escape(decodeString)",
               "'decode%20called%20with%20string%20argument%20v2%5Fvar1%3D"
         "val1%26v2%5Fvar2%3Dval2%26%0A'");
 
@@ -245,14 +245,14 @@
 
     SWFMovie_nextFrame(mo);
     // check_equals is too braindead to do this without escaping.
-    xcheck_equals(mo, "escape(dataString)",
+    check_equals(mo, "escape(dataString)",
            "'5%3A%20onData%20called%20with%20undefined%20"
            "argument%20undefined'");
     check_equals(mo, "l.loaded", "false");
     add_actions(mo, "l.onData = odatB;");
 
     // No more calls to decode.
-    xcheck_equals(mo, "decodeCalled", "1");
+    check_equals(mo, "decodeCalled", "1");
 
 
     /// End of tests.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]