[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Gnash-commit] /srv/bzr/gnash/trunk r11620: Make all movies and bitmap l
From: |
Sandro Santilli |
Subject: |
[Gnash-commit] /srv/bzr/gnash/trunk r11620: Make all movies and bitmap loads non-blocking by using a single thread for socket initialization, magic number parsing and construction of appropriate definition. |
Date: |
Thu, 12 Nov 2009 22:00:31 +0100 |
User-agent: |
Bazaar (1.16.1) |
------------------------------------------------------------
revno: 11620 [merge]
committer: Sandro Santilli <address@hidden>
branch nick: trunk
timestamp: Thu 2009-11-12 22:00:31 +0100
message:
Make all movies and bitmap loads non-blocking by using a single thread for
socket initialization, magic number parsing and construction of appropriate
definition.
added:
libcore/MovieLoader.cpp
libcore/MovieLoader.h
modified:
NEWS
libcore/Makefile.am
libcore/MovieLibrary.h
libcore/movie_root.cpp
libcore/movie_root.h
libcore/parser/SWFMovieDefinition.cpp
libcore/parser/SWFMovieDefinition.h
testsuite/misc-ming.all/loadMovieTestRunner.cpp
testsuite/movies.all/gravity_embedded-TestRunner.cpp
testsuite/swfdec/PASSING
testsuite/swfdec/REALTIME
utilities/processor.cpp
=== modified file 'NEWS'
--- a/NEWS 2009-11-08 11:57:40 +0000
+++ b/NEWS 2009-11-12 08:02:01 +0000
@@ -1,5 +1,16 @@
+20??-??-??
+
+Gnash 0.8.6
+Improvements since 0.8.5 release are:
+
+ * Asynchronous load of all resources (XML, variables, movies, bitmaps)
+
+
2009-03-04 Rob Savoye <address@hidden>
+Gnash 0.8.5 released !
+Improvements since 0.8.4 release are:
+
Due to better XML parsing compatibility and a minor fix to NetConnection,
many more video sites work than in the last release. Support for new
codecs keeps YouTube support up to date.
=== modified file 'libcore/Makefile.am'
--- a/libcore/Makefile.am 2009-10-01 13:19:43 +0000
+++ b/libcore/Makefile.am 2009-11-12 12:13:15 +0000
@@ -130,6 +130,7 @@
RGBA.cpp \
asClass.cpp \
asNamespace.cpp \
+ MovieLoader.cpp \
$(FREETYPE_SOURCES) \
$(NULL)
@@ -204,6 +205,7 @@
swf_function.h \
Timers.h \
Video.h \
+ MovieLoader.h \
$(NULL)
if ENABLE_AVM2
=== modified file 'libcore/MovieLibrary.h'
--- a/libcore/MovieLibrary.h 2009-06-15 13:50:02 +0000
+++ b/libcore/MovieLibrary.h 2009-11-11 23:18:13 +0000
@@ -23,6 +23,7 @@
#include <map>
#include <algorithm>
+#include <boost/thread/thread.hpp>
namespace gnash {
@@ -63,6 +64,7 @@
bool get(const std::string& key,
boost::intrusive_ptr<movie_definition>* ret)
{
+ boost::mutex::scoped_lock lock(_mapMutex);
LibraryContainer::iterator it = _map.find(key);
if (it == _map.end()) return false;
@@ -75,6 +77,7 @@
/// Mark all library elements as reachable (for GC)
void markReachableResources() const
{
+ boost::mutex::scoped_lock lock(_mapMutex);
for (LibraryContainer::const_iterator i=_map.begin(), e=_map.end();
i!=e; ++i)
{
@@ -95,11 +98,16 @@
temp.def = mov;
temp.hitCount = 0;
+ boost::mutex::scoped_lock lock(_mapMutex);
_map[key] = temp;
}
- void clear() { _map.clear(); }
+ void clear()
+ {
+ boost::mutex::scoped_lock lock(_mapMutex);
+ _map.clear();
+ }
private:
@@ -120,11 +128,14 @@
}
while (_map.size() > max) {
+ boost::mutex::scoped_lock lock(_mapMutex);
_map.erase(std::min_element(_map.begin(), _map.end(),
&findWorstHitCount));
}
}
+
+ mutable boost::mutex _mapMutex;
};
=== added file 'libcore/MovieLoader.cpp'
--- a/libcore/MovieLoader.cpp 1970-01-01 00:00:00 +0000
+++ b/libcore/MovieLoader.cpp 2009-11-12 12:13:15 +0000
@@ -0,0 +1,508 @@
+#include "log.h"
+#include "MovieFactory.h"
+#include "movie_root.h"
+#include "MovieFactory.h"
+#include "DisplayObject.h"
+#include "as_value.h"
+#include "as_object.h"
+#include "movie_definition.h"
+#include "Movie.h"
+#include "MovieClip.h"
+#include "URL.h"
+#include "namedStrings.h"
+#include "ExecutableCode.h"
+
+#include <memory> // for auto_ptr
+#include <boost/bind.hpp>
+
+//#define GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING 1
+//#define GNASH_DEBUG_LOCKING 1
+
+namespace gnash {
+
+MovieLoader::MovieLoader(movie_root& mr)
+ :
+ _movieRoot(mr),
+ _thread(0),
+ _barrier(2) // main and loader thread
+{
+}
+
+// private
+// runs in loader thread
+void
+MovieLoader::processRequests()
+{
+ // let _thread assignment happen before going on
+ _barrier.wait();
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Starting movie loader thread");
+#endif
+
+
+ while (1) {
+
+ // check for shutdown/cancel request
+ if ( killed() )
+ {
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Loader thread killed");
+#endif
+ return;
+ }
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processRequests: lock on requests: trying");
+#endif
+
+ boost::mutex::scoped_lock lock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processRequests: lock on requests: obtained");
+#endif
+
+ // Find first non-completed request (the others we'll wait)
+ Requests::iterator endIt = _requests.end();
+ Requests::iterator it = find_if(_requests.begin(), endIt,
+ boost::bind(&Request::pending, _1));
+
+ if (it == endIt)
+ {
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Movie loader thread getting to sleep (nothing more to
do)");
+#endif
+ // all completed, we can get to sleep
+ _wakeup.wait(lock);
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Movie loader thread waked up");
+#endif
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processRequests: lock on requests: release");
+#endif
+
+ continue;
+ }
+
+ Request* lr = *it;
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processRequests: lock on requests: release");
+#endif
+
+ lock.unlock(); // now main thread can continue to push requests
+
+ processRequest(*lr);
+
+ }
+
+}
+
+// private
+// runs in loader thread
+void
+MovieLoader::processRequest(Request& r)
+{
+ const URL& url = r.getURL();
+ bool usePost = r.usePost();
+ const std::string* postdata = usePost ? &(r.getPostData()) : 0;
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Movie loader thread processing request for target %s",
+ r.getTarget());
+#endif
+
+ boost::intrusive_ptr<movie_definition> md (
+ MovieFactory::makeMovie(url, _movieRoot.runResources(),
+ NULL, true, postdata)
+ );
+ r.setCompleted(md);
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Movie loader thread completed request for target %s",
+ r.getTarget());
+#endif
+}
+
+// public
+// runs in main thread
+void
+MovieLoader::clear()
+{
+ if ( _thread.get() )
+ {
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("clear: lock on requests: trying");
+#endif
+
+ boost::mutex::scoped_lock requestsLock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("clear: lock on requests: obtained");
+#endif
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("clear: lock on kill: trying");
+#endif
+
+ boost::mutex::scoped_lock lock(_killMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("clear: lock on kill: obtained");
+#endif
+
+ _killed=true;
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("clear: lock on kill: release for kill");
+#endif
+
+ lock.unlock();
+
+log_debug("waking up loader thread");
+
+ _wakeup.notify_all(); // in case it was sleeping
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("clear: lock on requests: release ater notify_all");
+#endif
+ requestsLock.unlock(); // allow the thread to die
+
+log_debug("notified, joining");
+ _thread->join();
+log_debug("joined");
+ _thread.reset();
+ }
+
+ // no thread now, can clean w/out locking
+ clearRequests();
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("clear: lock on requests: release if not after notify_all");
+#endif
+}
+
+// private, no locking
+// runs in main thread
+void
+MovieLoader::clearRequests()
+{
+ for (Requests::iterator it=_requests.begin(),
+ end = _requests.end(); it != end; ++it)
+ {
+ delete *it;
+ }
+ _requests.clear();
+}
+
+// private
+// runs in main thread
+bool
+MovieLoader::processCompletedRequest(const Request& r)
+{
+ //GNASH_REPORT_FUNCTION;
+
+ boost::intrusive_ptr<movie_definition> md;
+ if ( ! r.getCompleted(md) ) return false; // not completed yet
+
+ const std::string& target = r.getTarget();
+ DisplayObject* targetDO = _movieRoot.findCharacterByTarget(target);
+ as_object* handler = r.getHandler();
+
+ if ( ! md )
+ {
+
+ if ( targetDO && handler )
+ {
+ // Signal load error
+ // Tested not to happen if target isn't found at time of loading
+ //
+
+ as_value arg1(getObject(targetDO));
+
+ // FIXME: docs suggest the string can be either "URLNotFound" or
+ // "LoadNeverCompleted". This is neither of them:
+ as_value arg2("Failed to load movie or jpeg");
+
+ // FIXME: The last argument is HTTP status, or 0 if no connection
+ // was attempted (sandbox) or no status information is available
+ // (supposedly the Adobe mozilla plugin).
+ as_value arg3(0.0);
+
+ handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadError",
+ arg1, arg2, arg3);
+ }
+ return true; // nothing to do, but completed
+ }
+
+ const URL& url = r.getURL();
+
+ Movie* extern_movie = md->createMovie(*_movieRoot.getVM().getGlobal());
+ if (!extern_movie)
+ {
+ log_error(_("Can't create Movie instance "
+ "for definition loaded from %s"), url);
+ return true; // completed in any case...
+ }
+
+ // Parse query string
+ MovieClip::MovieVariables vars;
+ url.parse_querystring(url.querystring(), vars);
+ extern_movie->setVariables(vars);
+
+ if (targetDO)
+ {
+ targetDO->getLoadedMovie(extern_movie);
+ }
+ else
+ {
+ unsigned int levelno;
+ if (_movieRoot.isLevelTarget(target, levelno))
+ {
+ log_debug(_("processCompletedRequest: _level loading "
+ "(level %u)"), levelno);
+ extern_movie->set_depth(levelno +
DisplayObject::staticDepthOffset);
+ _movieRoot.setLevel(levelno, extern_movie);
+ }
+ else
+ {
+ log_debug("Target %s of a loadMovie request doesn't exist at "
+ "load complete time", target);
+ return true;
+ }
+ }
+
+ if (handler && targetDO)
+ {
+ // Dispatch onLoadStart
+ // FIXME: should be signalled before starting to load
+ // (0/-1 bytes loaded/total) but still with *new*
+ // display object as target (ie: the target won't
+ // contain members set either before or after loadClip.
+ handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadStart",
+ getObject(targetDO));
+
+ // Dispatch onLoadProgress
+ // FIXME: should be signalled on every readNonBlocking()
+ // with a buffer size of 65535 bytes.
+ //
+ size_t bytesLoaded = md->get_bytes_loaded();
+ size_t bytesTotal = md->get_bytes_total();
+ handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadProgress",
+ getObject(targetDO), bytesLoaded, bytesTotal);
+
+ // Dispatch onLoadComplete
+ // FIXME: find semantic of last arg
+ handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadComplete",
+ getObject(targetDO), as_value(0.0));
+
+
+ // Dispatch onLoadInit
+
+ // This event must be dispatched when actions
+ // in first frame of loaded clip have been executed.
+ //
+ // Since getLoadedMovie or setLevel above will invoke
+ // stagePlacementCallback and thus queue all actions in first
+ // frame, we'll queue the
+ // onLoadInit call next, so it happens after the former.
+ //
+ std::auto_ptr<ExecutableCode> code(
+ new DelayedFunctionCall(handler, NSV::PROP_BROADCAST_MESSAGE,
+ "onLoadInit", getObject(targetDO)));
+
+ getRoot(*handler).pushAction(code, movie_root::apDOACTION);
+ }
+
+ return true;
+
+}
+
+// private
+// runs in main thread
+void
+MovieLoader::processCompletedRequests()
+{
+ //GNASH_REPORT_FUNCTION;
+
+ for (;;)
+ {
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("processCompletedRequests: lock on requests: trying");
+#endif
+
+ boost::mutex::scoped_lock requestsLock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("processCompletedRequests: lock on requests: obtained");
+#endif
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Checking %d requests for completeness",
+ _requests.size());
+#endif
+
+
+ Request* firstCompleted = 0;
+ Requests::iterator endIt = _requests.end();
+ Requests::iterator it = find_if(_requests.begin(), endIt,
+ boost::bind(&Request::completed, _1));
+ if ( it != endIt ) firstCompleted=*it;
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processCompletedRequests: lock on requests: releasing");
+#endif
+
+ requestsLock.unlock();
+
+ if ( firstCompleted )
+ {
+
+#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
+ log_debug("Load request for target %s completed",
+ firstCompleted->getTarget());
+#endif
+
+ bool checkit = processCompletedRequest(*firstCompleted);
+ assert(checkit);
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processCompletedRequests: lock on requests for removal: "
+ "trying");
+#endif
+
+ requestsLock.lock();
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processCompletedRequests: lock on requests for removal: "
+ "obtained");
+#endif
+
+ _requests.remove(firstCompleted);
+ delete firstCompleted;
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("processCompletedRequests: lock on requests for removal: "
+ "release");
+#endif
+ }
+ else
+ {
+ break;
+ }
+
+ }
+}
+
+// private
+// runs in loader thread
+bool
+MovieLoader::killed()
+{
+ boost::mutex::scoped_lock lock(_killMutex);
+ return _killed;
+}
+
+// public
+// runs in main thread
+void
+MovieLoader::loadMovie(const std::string& urlstr,
+ const std::string& target,
+ const std::string& data,
+ MovieClip::VariablesMethod method,
+ as_object* handler)
+{
+
+ /// URL security is checked in StreamProvider::getStream() down the
+ /// chain.
+ URL url(urlstr, _movieRoot.runResources().baseURL());
+
+ /// If the method is MovieClip::METHOD_NONE, we send no data.
+ if (method == MovieClip::METHOD_GET)
+ {
+ std::string varsToSend(urlstr);
+ /// GET: append data to query string.
+ std::string qs = url.querystring();
+ if ( qs.empty() ) varsToSend.insert(0, 1, '?');
+ else varsToSend.insert(0, 1, '&');
+ url.set_querystring(qs + varsToSend);
+ }
+
+ log_debug("MovieLoader::loadMovie(%s, %s)", url.str(), target);
+
+ const std::string* postdata = (method == MovieClip::METHOD_POST) ? &data
+ : 0;
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("loadMovie: lock on requests: trying");
+#endif
+
+ boost::mutex::scoped_lock lock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+log_debug("loadMovie: lock on requests: obtained");
+#endif
+
+ _requests.push_front(
+ new Request(url, target, postdata, handler)
+ );
+
+ // Start or wake up the loader thread
+ if ( ! _thread.get() )
+ {
+ _killed=false;
+ _thread.reset(new boost::thread(boost::bind(
+ &MovieLoader::processRequests, this)));
+ _barrier.wait(); // let execution start before proceeding
+ }
+ else
+ {
+ log_debug("loadMovie: waking up existing thread");
+ _wakeup.notify_all();
+ }
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("loadMovie: lock on requests: release");
+#endif
+}
+
+// public
+// runs in main thread
+MovieLoader::~MovieLoader()
+{
+ clear(); // will kill the thread
+}
+
+// public
+// runs in main thread
+void
+MovieLoader::setReachable() const
+{
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("setReachable: lock on requests: trying");
+#endif
+
+ boost::mutex::scoped_lock lock(_requestsMutex);
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("setReachable: lock on requests: obtained");
+#endif
+
+ for (Requests::const_iterator it=_requests.begin(),
+ end = _requests.end(); it != end; ++it)
+ {
+ (*it)->setReachable();
+ }
+
+#ifdef GNASH_DEBUG_LOCKING
+ log_debug("setReachable: lock on requests: release");
+#endif
+}
+
+
+} // namespace gnash
=== added file 'libcore/MovieLoader.h'
--- a/libcore/MovieLoader.h 1970-01-01 00:00:00 +0000
+++ b/libcore/MovieLoader.h 2009-11-12 12:13:15 +0000
@@ -0,0 +1,219 @@
+//
+// Copyright (C) 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+#ifndef GNASH_MOVIE_LOADER_H
+#define GNASH_MOVIE_LOADER_H
+
+#include "URL.h" // for Request composition
+#include "as_object.h" // for setReachable inline
+#include "movie_definition.h" // for use in intrusive_ptr (inline)
+#include "MovieClip.h" // for MovieClip::VariablesMethod type
+
+#include <list>
+#include <string>
+#include <boost/noncopyable.hpp>
+#include <boost/thread/thread.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/thread/barrier.hpp>
+
+// Forward declarations
+namespace gnash {
+ class movie_root;
+ class movie_definition;
+}
+
+namespace gnash {
+
+/// Movie loader
+//
+/// All public functions are intended to be called by the main thread
+/// Hide the asynchonous mechanism of movies loading.
+/// Currently implemented using threads, could be refactored to use
+/// non-blocking reads.
+///
+class DSOEXPORT MovieLoader : boost::noncopyable {
+
+public:
+
+ MovieLoader(movie_root& mr);
+
+ ~MovieLoader();
+
+ /// Queue a request for loading a movie
+ //
+ /// This function constructs the URL and, if required, the postdata
+ /// from the arguments. The variables to send should *not* be appended
+ /// to @param urlstr before calling this function.
+ //
+ /// @param urlstr The url exactly as requested. This may already
+ /// contain a query string.
+ /// @param target Target for request.
+ /// @param data The variables data to send, URL encoded in
+ /// key/value pairs
+ /// @param method The VariablesMethod to use for sending the data. If
+ /// MovieClip::METHOD_NONE, no data will be sent.
+ /// @param handler An object which will be signalled of load
+ /// events (onLoadStart, onLoadComplete, onLoadInit,
+ /// onLoadError). Can be null if caller doesn't care.
+ ///
+ void loadMovie(const std::string& url, const std::string& target,
+ const std::string& data, MovieClip::VariablesMethod method,
+ as_object* handler=0);
+
+ /// Drop all requests and kill the thread
+ void clear();
+
+ /// Process all completed movie load requests.
+ void processCompletedRequests();
+
+ void setReachable() const;
+
+private:
+
+ /// A movie load request
+ class Request : boost::noncopyable {
+ public:
+ /// @param postdata
+ /// If not null POST method will be used for HTTP.
+ ///
+ Request(const URL& u, const std::string& t,
+ const std::string* postdata, as_object* handler)
+ :
+ _target(t),
+ _url(u),
+ _usePost(false),
+ _mdef(0),
+ _mutex(),
+ _handler(handler),
+ _completed(false)
+ {
+ if ( postdata )
+ {
+ _postData = *postdata;
+ _usePost = true;
+ }
+ }
+
+ const std::string& getTarget() const { return _target; }
+ const URL& getURL() const { return _url; }
+ const std::string& getPostData() const { return _postData; }
+ bool usePost() const { return _usePost; }
+ as_object* getHandler() const { return _handler; }
+ void setReachable() const {
+ if (_handler) _handler->setReachable();
+ }
+
+ /// Get the loaded movie definition, if any
+ //
+ /// @param md the loaded movie_definition is copied here
+ /// if it was impossible to create one.
+ ///
+ /// @return true if the request was completed, false otherwise.
+ ///
+ /// RULE: if return is FALSE param 'md' will be set to 0.
+ /// RULE: if return is TRUE param 'md' may be set to 0 or non 0.
+ /// RULE: if parameter 'md' is set to non 0, TRUE must be returned.
+ ///
+ /// locks _mutex
+ ///
+ bool getCompleted(boost::intrusive_ptr<movie_definition>& md) const
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ md = _mdef;
+ return _completed;
+ }
+
+ /// Only check if request is completed
+ bool pending() const
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ return !_completed;
+ }
+
+ /// Only check if request is completed
+ bool completed() const
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ return _completed;
+ }
+
+ /// Complete the request
+ //
+ /// @param md the loaded movie_definition, or 0 if
+ /// it was impossible to create one.
+ ///
+ /// locks _mutex
+ ///
+ void setCompleted(boost::intrusive_ptr<movie_definition> md)
+ {
+ boost::mutex::scoped_lock lock(_mutex);
+ _mdef = md;
+ _completed = true;
+ }
+
+ private:
+ std::string _target;
+ URL _url;
+ bool _usePost;
+ std::string _postData;
+ boost::intrusive_ptr<movie_definition> _mdef;
+ mutable boost::mutex _mutex;
+ as_object* _handler;
+ bool _completed;
+ };
+
+ /// Load requests
+ typedef std::list<Request*> Requests;
+ Requests _requests;
+
+ mutable boost::mutex _requestsMutex;
+
+ void processRequests();
+ void processRequest(Request& r);
+ void clearRequests();
+
+ /// Check a Request and process if completed.
+ //
+ /// @return true if the request was completely processed.
+ ///
+ bool processCompletedRequest(const Request& r);
+
+ /// Was thread kill requested ?
+ bool killed();
+
+ bool _killed;
+
+ boost::mutex _killMutex;
+
+ boost::condition _wakeup;
+
+ /// needed for some facilities like find_character_by_target
+ movie_root& _movieRoot;
+
+ std::auto_ptr<boost::thread> _thread;
+
+ // Barrier to ensure that _thread
+ // is initialized before the loader thread
+ // continues execution
+ boost::barrier _barrier;
+
+};
+
+} // namespace gnash
+
+#endif // GNASH_MOVIE_LOADER_H
=== modified file 'libcore/movie_root.cpp'
--- a/libcore/movie_root.cpp 2009-11-12 10:47:18 +0000
+++ b/libcore/movie_root.cpp 2009-11-12 21:00:31 +0000
@@ -139,7 +139,8 @@
_timeoutLimit(15),
_movieAdvancementDelay(83), // ~12 fps by default
_lastMovieAdvancement(0),
- _unnamedInstance(0)
+ _unnamedInstance(0),
+ _movieLoader(*this)
{
// This takes care of informing the renderer (if present) too.
setQuality(QUALITY_HIGH);
@@ -187,22 +188,11 @@
_intervalTimers.clear();
}
-void
-movie_root::clearLoadMovieRequests()
-{
- for (LoadMovieRequests::iterator it=_loadMovieRequests.begin(),
- end = _loadMovieRequests.end(); it != end; ++it)
- {
- delete *it;
- }
- _loadMovieRequests.clear();
-}
-
movie_root::~movie_root()
{
clearActionQueue();
clearIntervalTimers();
- clearLoadMovieRequests();
+ _movieLoader.clear();
assert(testInvariant());
}
@@ -518,7 +508,7 @@
clearIntervalTimers();
// remove all loadMovie requests
- clearLoadMovieRequests();
+ _movieLoader.clear();
// remove key/mouse listeners
_keyListeners.clear();
@@ -888,7 +878,8 @@
// 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();
+ //
+ _movieLoader.processCompletedRequests();
// Process queued actions
// NOTE: can throw ActionLimitException
@@ -1676,11 +1667,7 @@
}
// Mark LoadMovieRequest handlers as reachable
- for (LoadMovieRequests::const_iterator it=_loadMovieRequests.begin(),
- end = _loadMovieRequests.end(); it != end; ++it)
- {
- (*it)->setReachable();
- }
+ _movieLoader.setReachable();
// Mark resources reachable by queued action code
for (int lvl=0; lvl<apSIZE; ++lvl)
@@ -2065,210 +2052,6 @@
}
-void
-movie_root::loadMovie(const std::string& urlstr, const std::string& target,
- const std::string& data, MovieClip::VariablesMethod method,
- as_object* handler)
-{
-
- /// URL security is checked in StreamProvider::getStream() down the
- /// chain.
- URL url(urlstr, _runResources.baseURL());
-
- /// If the method is MovieClip::METHOD_NONE, we send no data.
- if (method == MovieClip::METHOD_GET)
- {
- std::string varsToSend(urlstr);
- /// GET: append data to query string.
- std::string qs = url.querystring();
- if ( qs.empty() ) varsToSend.insert(0, 1, '?');
- else varsToSend.insert(0, 1, '&');
- url.set_querystring(qs + varsToSend);
- }
-
- log_debug("movie_root::loadMovie(%s, %s)", url.str(), target);
-
- const std::string* postdata = (method == MovieClip::METHOD_POST) ? &data
- : 0;
-
- _loadMovieRequests.push_front(
- new LoadMovieRequest(url, target, postdata, handler)
- );
-
- // TODO: start thread
-}
-
-void
-movie_root::processLoadMovieRequest(LoadMovieRequest& r)
-{
- const URL& url = r.getURL();
- bool usePost = r.usePost();
- const std::string* postdata = usePost ? &(r.getPostData()) : 0;
-
- // TODO: make this load asyncronous !!
- boost::intrusive_ptr<movie_definition> md (
- MovieFactory::makeMovie(url, _runResources, NULL, true, postdata));
- r.setCompleted(md);
-
- bool completed = processCompletedLoadMovieRequest(r);
- assert(completed);
-}
-
-/* private */
-bool
-movie_root::processCompletedLoadMovieRequest(const LoadMovieRequest& r)
-{
- GNASH_REPORT_FUNCTION;
-
- boost::intrusive_ptr<movie_definition> md;
- if ( ! r.getCompleted(md) ) return false; // not completed yet
-
- const std::string& target = r.getTarget();
- DisplayObject* targetDO = findCharacterByTarget(target);
- as_object* handler = r.getHandler();
-
- if ( ! md )
- {
-
- if ( targetDO && handler )
- {
- // Signal load error
- // Tested not to happen if target isn't found at time of loading
- //
-
- as_value arg1(getObject(targetDO));
-
- // FIXME: docs suggest the string can be either "URLNotFound" or
- // "LoadNeverCompleted". This is neither of them:
- as_value arg2("Failed to load movie or jpeg");
-
- // FIXME: The last argument is HTTP status, or 0 if no connection
- // was attempted (sandbox) or no status information is available
- // (supposedly the Adobe mozilla plugin).
- as_value arg3(0.0);
-
- handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadError",
- arg1, arg2, arg3);
- }
- return true; // nothing to do, but completed
- }
-
- const URL& url = r.getURL();
-
- Movie* extern_movie = md->createMovie(*_vm.getGlobal());
- if (!extern_movie)
- {
- log_error(_("Can't create Movie instance "
- "for definition loaded from %s"), url);
- return true; // completed in any case...
- }
-
- // Parse query string
- MovieClip::MovieVariables vars;
- url.parse_querystring(url.querystring(), vars);
- extern_movie->setVariables(vars);
-
- if (targetDO)
- {
- targetDO->getLoadedMovie(extern_movie);
- }
- else
- {
- unsigned int levelno;
- if (isLevelTarget(target, levelno))
- {
- log_debug(_("processCompletedLoadMovieRequest: _level loading "
- "(level %u)"), levelno);
- extern_movie->set_depth(levelno +
DisplayObject::staticDepthOffset);
- setLevel(levelno, extern_movie);
- }
- else
- {
- log_debug("Target %s of a loadMovie request doesn't exist at "
- "load complete time", target);
- return true;
- }
- }
-
- if (handler && targetDO)
- {
- // Dispatch onLoadStart
- // FIXME: should be signalled before starting to load
- // (0/-1 bytes loaded/total) but still with *new*
- // display object as target (ie: the target won't
- // contain members set either before or after loadClip.
- handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadStart",
- getObject(targetDO));
-
- // Dispatch onLoadProgress
- // FIXME: should be signalled on every readNonBlocking()
- // with a buffer size of 65535 bytes.
- //
- size_t bytesLoaded = md->get_bytes_loaded();
- size_t bytesTotal = md->get_bytes_total();
- handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadProgress",
- getObject(targetDO), bytesLoaded, bytesTotal);
-
- // Dispatch onLoadComplete
- // FIXME: find semantic of last arg
- handler->callMethod(NSV::PROP_BROADCAST_MESSAGE, "onLoadComplete",
- getObject(targetDO), as_value(0.0));
-
-
- // Displatch onLoadInit
-
- // This event must be dispatched when actions
- // in first frame of loaded clip have been executed.
- //
- // Since getLoadedMovie or setLevel above will invoke
- // stagePlacementCallback and thus queue all actions in first
- // frame, we'll queue the
- // onLoadInit call next, so it happens after the former.
- //
- std::auto_ptr<ExecutableCode> code(
- new DelayedFunctionCall(handler, NSV::PROP_BROADCAST_MESSAGE,
- "onLoadInit", getObject(targetDO)));
-
- getRoot(*handler).pushAction(code, movie_root::apDOACTION);
- }
-
- return true;
-
-}
-
-/* private */
-void
-movie_root::processCompletedLoadMovieRequests()
-{
- GNASH_REPORT_FUNCTION;
-
- for (LoadMovieRequests::iterator it=_loadMovieRequests.begin();
- it != _loadMovieRequests.end(); )
- {
- const LoadMovieRequest* lr=*it;
- if ( processCompletedLoadMovieRequest(*lr) )
- {
- it = _loadMovieRequests.erase(it);
- delete lr;
- }
- }
-}
-
-void
-movie_root::processLoadMovieRequests()
-{
-#ifdef GNASH_DEBUG_LOADMOVIE_REQUESTS_PROCESSING
- log_debug("Processing %d loadMovie requests", _loadMovieRequests.size());
-#endif
- for (LoadMovieRequests::iterator it=_loadMovieRequests.begin();
- it != _loadMovieRequests.end(); )
- {
- LoadMovieRequest* lr=*it;
- processLoadMovieRequest(*lr);
- it = _loadMovieRequests.erase(it);
- delete lr;
- }
-}
bool
movie_root::isLevelTarget(const std::string& name, unsigned int& levelno)
=== modified file 'libcore/movie_root.h'
--- a/libcore/movie_root.h 2009-11-12 10:48:38 +0000
+++ b/libcore/movie_root.h 2009-11-12 21:00:31 +0000
@@ -81,6 +81,7 @@
#include "gnash.h" // Quality
#include "MovieClip.h"
#include "SimpleBuffer.h" // for LoadCallback
+#include "MovieLoader.h"
#ifdef USE_SWFTREE
# include "tree.hh"
@@ -187,6 +188,14 @@
///
Movie* getLevel(unsigned int num) const;
+ /// Put the given movie at the given level
+ //
+ /// @param movie
+ /// The Movie to store at the given level.
+ /// Its depth will be set to <num>+DisplayObject::staticDepthOffset and
+ /// its name to _level<num>
+ void setLevel(unsigned int num, Movie* movie);
+
/// Replace an existing level with a new movie
//
/// Depth will be assigned to external_movie by this function.
@@ -708,7 +717,10 @@
///
void loadMovie(const std::string& url, const std::string& target,
const std::string& data, MovieClip::VariablesMethod method,
- as_object* handler=0);
+ as_object* handler=0)
+ {
+ _movieLoader.loadMovie(url, target, data, method, handler);
+ }
/// Send a request to the hosting application (e.g. browser).
//
@@ -928,103 +940,6 @@
/// Registered FsCommand handler, if any
AbstractFsCallback* _fsCommandHandler;
- /// A load movie request
- class LoadMovieRequest : boost::noncopyable {
- public:
- /// @param postdata
- /// If not null POST method will be used for HTTP.
- ///
- LoadMovieRequest(const URL& u, const std::string& t,
- const std::string* postdata, as_object* handler)
- :
- _target(t),
- _url(u),
- _usePost(false),
- _mdef(0),
- _completed(false),
- _mutex(),
- _handler(handler)
- {
- if ( postdata )
- {
- _postData = *postdata;
- _usePost = true;
- }
- }
-
- const std::string& getTarget() const { return _target; }
- const URL& getURL() const { return _url; }
- const std::string& getPostData() const { return _postData; }
- bool usePost() const { return _usePost; }
- as_object* getHandler() const { return _handler; }
- void setReachable() const {
- if (_handler) _handler->setReachable();
- }
-
- /// Get the loaded movie definition, if any
- //
- /// @param md the loaded movie_definition is copied here
- /// if it was impossible to create one.
- ///
- /// @return true if the request was completed, false otherwise.
- ///
- /// RULE: if return is FALSE param 'md' will be set to 0.
- /// RULE: if return is TRUE param 'md' may be set to 0 or non 0.
- /// RULE: if parameter 'md' is set to non 0, TRUE must be returned.
- ///
- /// locks _mutex
- ///
- bool getCompleted(boost::intrusive_ptr<movie_definition>& md) const
- {
- boost::mutex::scoped_lock lock(_mutex);
- md = _mdef;
- return _completed;
- }
-
- /// Complete the request
- //
- /// @param md the loaded movie_definition, or 0 if
- /// it was impossible to create one.
- ///
- /// locks _mutex
- ///
- void setCompleted(boost::intrusive_ptr<movie_definition> md)
- {
- boost::mutex::scoped_lock lock(_mutex);
- _mdef = md;
- _completed = true;
- }
-
- private:
- std::string _target;
- URL _url;
- bool _usePost;
- std::string _postData;
- boost::intrusive_ptr<movie_definition> _mdef;
- bool _completed;
- mutable boost::mutex _mutex;
- as_object* _handler;
- };
-
- /// Load movie requests
- typedef std::list<LoadMovieRequest*> LoadMovieRequests;
- LoadMovieRequests _loadMovieRequests;
-
- /// Process all load movie requests
- void processLoadMovieRequests();
-
- /// Process a single load movie request
- void processLoadMovieRequest(LoadMovieRequest& r);
-
- /// Check a LoadMovieRequest and process if completed.
- //
- /// @return true if the request was completely processed.
- ///
- bool processCompletedLoadMovieRequest(const LoadMovieRequest& r);
-
- /// Process all completed loadMovie requests.
- void processCompletedLoadMovieRequests();
-
/// Listeners container
typedef std::list<DisplayObject*> Listeners;
@@ -1037,9 +952,6 @@
/// Delete all elements on the timers list
void clearIntervalTimers();
- /// Delete all LoadMovieRequests
- void clearLoadMovieRequests();
-
/// Execute expired timers
void executeAdvanceCallbacks();
@@ -1103,14 +1015,6 @@
/// Advance all non-unloaded live chars
void advanceLiveChars();
- /// Put the given movie at the given level
- //
- /// @param movie
- /// The Movie to store at the given level.
- /// Its depth will be set to <num>+DisplayObject::staticDepthOffset and
- /// its name to _level<num>
- void setLevel(unsigned int num, Movie* movie);
-
/// Boundaries of the Stage are always world boundaries
/// and are only invalidated by changes in the background
/// color.
@@ -1270,6 +1174,8 @@
size_t _unnamedInstance;
+ MovieLoader _movieLoader;
+
};
DSOEXPORT short stringToStageAlign(const std::string& s);
=== modified file 'libcore/parser/SWFMovieDefinition.cpp'
--- a/libcore/parser/SWFMovieDefinition.cpp 2009-11-04 16:55:59 +0000
+++ b/libcore/parser/SWFMovieDefinition.cpp 2009-11-12 12:13:15 +0000
@@ -71,7 +71,7 @@
template<typename T> void markMappedResources(const T& t);
}
-MovieLoader::MovieLoader(SWFMovieDefinition& md)
+SWFMovieLoader::SWFMovieLoader(SWFMovieDefinition& md)
:
_movie_def(md),
_thread(NULL),
@@ -79,7 +79,7 @@
{
}
-MovieLoader::~MovieLoader()
+SWFMovieLoader::~SWFMovieLoader()
{
// we should assert _movie_def._loadingCanceled
// but we're not friend yet (anyone introduce us ?)
@@ -91,7 +91,7 @@
}
bool
-MovieLoader::started() const
+SWFMovieLoader::started() const
{
boost::mutex::scoped_lock lock(_mutex);
@@ -99,7 +99,7 @@
}
bool
-MovieLoader::isSelfThread() const
+SWFMovieLoader::isSelfThread() const
{
boost::mutex::scoped_lock lock(_mutex);
@@ -117,19 +117,19 @@
// static..
void
-MovieLoader::execute(MovieLoader& ml, SWFMovieDefinition* md)
+SWFMovieLoader::execute(SWFMovieLoader& ml, SWFMovieDefinition* md)
{
ml._barrier.wait(); // let _thread assignment happen before going on
md->read_all_swf();
}
bool
-MovieLoader::start()
+SWFMovieLoader::start()
{
#ifndef LOAD_MOVIES_IN_A_SEPARATE_THREAD
std::abort();
#endif
- // don't start MovieLoader thread() which rely
+ // don't start SWFMovieLoader thread() which rely
// on boost::thread() returning before they are executed. Therefore,
// we must employ locking.
// Those tests do seem a bit redundant, though...
=== modified file 'libcore/parser/SWFMovieDefinition.h'
--- a/libcore/parser/SWFMovieDefinition.h 2009-11-04 15:03:15 +0000
+++ b/libcore/parser/SWFMovieDefinition.h 2009-11-12 12:13:15 +0000
@@ -63,13 +63,13 @@
/// SWFMovieDefinition helper class handling start and execution of
/// an SWF loading thread
///
-class MovieLoader
+class SWFMovieLoader
{
public:
- MovieLoader(SWFMovieDefinition& md);
+ SWFMovieLoader(SWFMovieDefinition& md);
- ~MovieLoader();
+ ~SWFMovieLoader();
/// Start loading thread.
//
@@ -79,10 +79,10 @@
///
bool start();
- /// Return true if the MovieLoader thread was started
+ /// Return true if the loader thread was started
bool started() const;
- /// Return true if called from the MovieLoader thread.
+ /// Return true if called from the loader thread.
bool isSelfThread() const;
private:
@@ -98,7 +98,7 @@
boost::barrier _barrier;
/// Entry point for the actual thread
- static void execute(MovieLoader& ml, SWFMovieDefinition* md);
+ static void execute(SWFMovieLoader& ml, SWFMovieDefinition* md);
};
@@ -512,7 +512,7 @@
size_t _swf_end_pos;
/// asyncronous SWF loader and parser
- MovieLoader _loader;
+ SWFMovieLoader _loader;
/// \brief
/// Increment loaded frames count, signaling frame reached condition if
=== modified file 'testsuite/misc-ming.all/loadMovieTestRunner.cpp'
--- a/testsuite/misc-ming.all/loadMovieTestRunner.cpp 2009-10-28 13:33:35
+0000
+++ b/testsuite/misc-ming.all/loadMovieTestRunner.cpp 2009-11-11 23:17:29
+0000
@@ -116,8 +116,8 @@
tester->movePointerTo(80, 80);
check(tester->isMouseOverMouseEntity());
tester->pressMouseButton();
+ usleep(5000); // give it some time... TODO: drop this test and use a
self-containment instead
tester->advance(); // loads (should) happen on next advance
- usleep(500); // give it some time... TODO: drop this test and use a
self-containment instead
coverartch =
const_cast<DisplayObject*>(tester->findDisplayItemByName(*root, "coverart"));
check(coverart != coverartch->to_movie());
coverart = coverartch->to_movie();
@@ -135,8 +135,8 @@
tester->movePointerTo(280, 80);
check(tester->isMouseOverMouseEntity());
tester->click();
+ usleep(5000); // give it some time... TODO: drop this test and use a
self-containment instead
tester->advance(); // loads (should) happen on next advance
- usleep(500); // give it some time... TODO: drop this test and use a
self-containment instead
coverartch =
const_cast<DisplayObject*>(tester->findDisplayItemByName(*root, "coverart"));
coverart = coverartch->to_movie();
check_equals(coverart->get_root()->url(), greenURL.str());
@@ -154,8 +154,8 @@
tester->movePointerTo(480, 80);
check(tester->isMouseOverMouseEntity());
tester->click();
+ usleep(5000); // give it some time... TODO: drop this test and use a
self-containment instead
tester->advance(); // loads (should) happen on next advance
- usleep(500); // give it some time... TODO: drop this test and use a
self-containment instead
coverartch =
const_cast<DisplayObject*>(tester->findDisplayItemByName(*root, "coverart"));
coverart = coverartch->to_movie();
check_equals(coverart->get_root()->url(), offspringURL.str());
=== modified file 'testsuite/movies.all/gravity_embedded-TestRunner.cpp'
--- a/testsuite/movies.all/gravity_embedded-TestRunner.cpp 2009-11-04
13:40:12 +0000
+++ b/testsuite/movies.all/gravity_embedded-TestRunner.cpp 2009-11-11
23:17:29 +0000
@@ -31,6 +31,7 @@
#include "check.h"
#include <string>
#include <cassert>
+#include <unistd.h> // for usleep
using namespace gnash;
using namespace std;
@@ -56,8 +57,9 @@
check_equals(root->get_frame_count(), 1);
- // give loader time to load the actual gravity.swf movie ?
- //sleep(1);
+ // give loader time to load the actual gravity.swf movie
+ usleep(5000);
+ tester.advance(); // have load processed
// used to get members
as_value tmp;
=== modified file 'testsuite/swfdec/PASSING'
--- a/testsuite/swfdec/PASSING 2009-11-10 00:59:35 +0000
+++ b/testsuite/swfdec/PASSING 2009-11-11 23:18:13 +0000
@@ -308,6 +308,8 @@
crash-0.5.5-stylesheet-update-7.swf:c112cfe1b313fb437fe45cb0c3a7b665
crash-0.5.5-stylesheet-update-8.swf:73a61afbd582fabd2dbba360cc3763e4
crash-0.5.90-empty-action.swf:c564ca09c4bc71fb89b042bc07d247eb
+crash-0.6.0-moviecliploader-7.swf:c829c5b17aed2c9d8a1720f826299e67
+crash-0.6.0-moviecliploader-8.swf:38c04c5f96ffedeba09ee5a2aea17a3f
crash-0.6.2-replaceText-5.swf:f9a3132918e7d3daaca3bb8122b8b847
crash-0.6.2-replaceText-6.swf:26a8a906a7ab6dc2411d0c72d84cbdfa
crash-0.6.2-replaceText-7.swf:da9ccc92ab99681f702edc62d143edf7
=== modified file 'testsuite/swfdec/REALTIME'
--- a/testsuite/swfdec/REALTIME 2008-05-28 15:23:26 +0000
+++ b/testsuite/swfdec/REALTIME 2009-11-11 23:35:17 +0000
@@ -1,13 +1,28 @@
+instance-name-loaded-5.swf
+instance-name-loaded-6.swf
+instance-name-loaded-7.swf
+loadmovie-case-5.swf
+loadmovie-case-6.swf
+loadmovie-case-7.swf
loadvariables-5.swf
loadvariables-6.swf
loadvariables-7.swf
-netstream-onmetadata.swf
-netstream-onstatus.swf
-netstream-dimensions.swf
+movieclip-get-swf-version-load-5.swf
+movieclip-get-swf-version-load-6.swf
+movieclip-get-swf-version-load-7.swf
+movieclip-get-swf-version-load-8.swf
+moviecliploader-known-image-size-7.swf
+moviecliploader-known-image-size-8.swf
+movieclip-lockroot-loadmovie-6.swf
+movieclip-lockroot-loadmovie-7.swf
+movieclip-lockroot-loadmovie-8.swf
movieclip-version-5.swf
movieclip-version-6.swf
movieclip-version-7.swf
movieclip-version-8.swf
+netstream-dimensions.swf
+netstream-onmetadata.swf
+netstream-onstatus.swf
sec-0.6.2-local-access-5.swf
sec-0.6.2-local-access-6.swf
sec-0.6.2-local-access-7.swf
=== modified file 'utilities/processor.cpp'
--- a/utilities/processor.cpp 2009-10-21 07:24:55 +0000
+++ b/utilities/processor.cpp 2009-11-11 23:18:05 +0000
@@ -601,6 +601,9 @@
cl.elapsed(), localDelay);
gnashSleep(localDelay);
}
+
+ // clear movie_root (too early?)
+ m.clear();
// Clear resources.
gnash::clear();
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Gnash-commit] /srv/bzr/gnash/trunk r11620: Make all movies and bitmap loads non-blocking by using a single thread for socket initialization, magic number parsing and construction of appropriate definition.,
Sandro Santilli <=