Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow threaded compilation in an async Loader
Enables threaded compilation for a Loader "source".

Change-Id: I2d60a3ace07aab58f3b8f069e45a2864178c959f
Reviewed-by: Chris Adams <christopher.adams@nokia.com>
  • Loading branch information
Martin Jones authored and Qt by Nokia committed Mar 15, 2012
1 parent b634c19 commit 31467e8
Show file tree
Hide file tree
Showing 21 changed files with 3,383 additions and 34 deletions.
109 changes: 91 additions & 18 deletions src/qml/qml/qqmlcomponent.cpp
Expand Up @@ -280,6 +280,16 @@ static inline QString buildTypeNameForDebug(const QMetaObject *metaObject)
\value Error An error has occurred. Call errors() to retrieve a list of \{QQmlError}{errors}.
*/

/*!
\enum QQmlComponent::CompilationMode
Specifies whether the QQmlComponent should load the component immediately, or asynchonously.
\value PreferSynchronous Prefer loading/compiling the component immediately, blocking the thread.
This is not always possible, e.g. remote URLs will always load asynchronously.
\value Asynchronous Load/compile the component in a background thread.
*/

void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)
{
Q_Q(QQmlComponent);
Expand All @@ -288,8 +298,10 @@ void QQmlComponentPrivate::typeDataReady(QQmlTypeData *)

fromTypeData(typeData);
typeData = 0;
progress = 1.0;

emit q->statusChanged(q->status());
emit q->progressChanged(progress);
}

void QQmlComponentPrivate::typeDataProgress(QQmlTypeData *, qreal p)
Expand Down Expand Up @@ -476,7 +488,24 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, QObject *paren
{
Q_D(QQmlComponent);
d->engine = engine;
loadUrl(url);
d->loadUrl(url);
}

/*!
Create a QQmlComponent from the given \a url and give it the
specified \a parent and \a engine. If \a mode is \l Asynchronous,
the component will be loaded and compiled asynchronously.
Ensure that the URL provided is full and correct, in particular, use
\l QUrl::fromLocalFile() when loading a file from the local filesystem.
\sa loadUrl()
*/QQmlComponent::QQmlComponent(QQmlEngine *engine, const QUrl &url, CompilationMode mode, QObject *parent)
: QObject(*(new QQmlComponentPrivate), parent)
{
Q_D(QQmlComponent);
d->engine = engine;
d->loadUrl(url, mode);
}

/*!
Expand All @@ -491,7 +520,23 @@ QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
{
Q_D(QQmlComponent);
d->engine = engine;
loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)));
}

/*!
Create a QQmlComponent from the given \a fileName and give it the specified
\a parent and \a engine. If \a mode is \l Asynchronous,
the component will be loaded and compiled asynchronously.
\sa loadUrl()
*/
QQmlComponent::QQmlComponent(QQmlEngine *engine, const QString &fileName,
CompilationMode mode, QObject *parent)
: QObject(*(new QQmlComponentPrivate), parent)
{
Q_D(QQmlComponent);
d->engine = engine;
d->loadUrl(d->engine->baseUrl().resolved(QUrl::fromLocalFile(fileName)), mode);
}

/*!
Expand Down Expand Up @@ -558,35 +603,63 @@ QQmlContext *QQmlComponent::creationContext() const
void QQmlComponent::loadUrl(const QUrl &url)
{
Q_D(QQmlComponent);
d->loadUrl(url);
}

d->clear();
/*!
Load the QQmlComponent from the provided \a url.
If \a mode is \l Asynchronous, the component will be loaded and compiled asynchronously.
Ensure that the URL provided is full and correct, in particular, use
\l QUrl::fromLocalFile() when loading a file from the local filesystem.
*/
void QQmlComponent::loadUrl(const QUrl &url, QQmlComponent::CompilationMode mode)
{
Q_D(QQmlComponent);
d->loadUrl(url, mode);
}

void QQmlComponentPrivate::loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode)
{
Q_Q(QQmlComponent);
clear();

if ((url.isRelative() && !url.isEmpty())
|| url.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
d->url = d->engine->baseUrl().resolved(url);
if ((newUrl.isRelative() && !newUrl.isEmpty())
|| newUrl.scheme() == QLatin1String("file")) // Workaround QTBUG-11929
url = engine->baseUrl().resolved(newUrl);
else
d->url = url;
url = newUrl;

if (url.isEmpty()) {
if (newUrl.isEmpty()) {
QQmlError error;
error.setDescription(tr("Invalid empty URL"));
d->state.errors << error;
error.setDescription(q->tr("Invalid empty URL"));
state.errors << error;
return;
}

QQmlTypeData *data = QQmlEnginePrivate::get(d->engine)->typeLoader.get(d->url);
if (progress != 0.0) {
progress = 0.0;
emit q->progressChanged(progress);
}

QQmlDataLoader::Mode loaderMode = (mode == QQmlComponent::Asynchronous)
? QQmlDataLoader::Asynchronous
: QQmlDataLoader::PreferSynchronous;

QQmlTypeData *data = QQmlEnginePrivate::get(engine)->typeLoader.get(url, loaderMode);

if (data->isCompleteOrError()) {
d->fromTypeData(data);
d->progress = 1.0;
fromTypeData(data);
progress = 1.0;
} else {
d->typeData = data;
d->typeData->registerCallback(d);
d->progress = data->progress();
typeData = data;
typeData->registerCallback(this);
progress = data->progress();
}

emit statusChanged(status());
emit progressChanged(d->progress);
emit q->statusChanged(q->status());
if (progress != 0.0)
emit q->progressChanged(progress);
}

/*!
Expand Down
6 changes: 6 additions & 0 deletions src/qml/qml/qqmlcomponent.h
Expand Up @@ -73,10 +73,15 @@ class Q_QML_EXPORT QQmlComponent : public QObject
Q_PROPERTY(QUrl url READ url CONSTANT)

public:
Q_ENUMS(CompilationMode)
enum CompilationMode { PreferSynchronous, Asynchronous };

QQmlComponent(QObject *parent = 0);
QQmlComponent(QQmlEngine *, QObject *parent=0);
QQmlComponent(QQmlEngine *, const QString &fileName, QObject *parent = 0);
QQmlComponent(QQmlEngine *, const QString &fileName, CompilationMode mode, QObject *parent = 0);
QQmlComponent(QQmlEngine *, const QUrl &url, QObject *parent = 0);
QQmlComponent(QQmlEngine *, const QUrl &url, CompilationMode mode, QObject *parent = 0);
virtual ~QQmlComponent();

Q_ENUMS(Status)
Expand Down Expand Up @@ -108,6 +113,7 @@ class Q_QML_EXPORT QQmlComponent : public QObject

public Q_SLOTS:
void loadUrl(const QUrl &url);
void loadUrl(const QUrl &url, CompilationMode mode);
void setData(const QByteArray &, const QUrl &baseUrl);

Q_SIGNALS:
Expand Down
2 changes: 2 additions & 0 deletions src/qml/qml/qqmlcomponent_p.h
Expand Up @@ -86,6 +86,8 @@ class Q_QML_PRIVATE_EXPORT QQmlComponentPrivate : public QObjectPrivate, public
public:
QQmlComponentPrivate() : typeData(0), progress(0.), start(-1), cc(0), engine(0), creationContext(0), profiler(0) {}

void loadUrl(const QUrl &newUrl, QQmlComponent::CompilationMode mode = QQmlComponent::PreferSynchronous);

QObject *beginCreate(QQmlContextData *);
void completeCreate();
void initializeObjectWithInitialProperties(v8::Handle<v8::Object> qmlGlobal, v8::Handle<v8::Object> valuemap, QObject *toCreate);
Expand Down
4 changes: 2 additions & 2 deletions src/qml/qml/qqmltypeloader.cpp
Expand Up @@ -1171,7 +1171,7 @@ This enum defines the options that control the way type data is handled.
/*!
Returns a QQmlTypeData for the specified \a url. The QQmlTypeData may be cached.
*/
QQmlTypeData *QQmlTypeLoader::get(const QUrl &url)
QQmlTypeData *QQmlTypeLoader::get(const QUrl &url, Mode mode)
{
Q_ASSERT(!url.isRelative() &&
(QQmlEnginePrivate::urlToLocalFileOrQrc(url).isEmpty() ||
Expand All @@ -1184,7 +1184,7 @@ QQmlTypeData *QQmlTypeLoader::get(const QUrl &url)
if (!typeData) {
typeData = new QQmlTypeData(url, None, this);
m_typeCache.insert(url, typeData);
QQmlDataLoader::load(typeData);
QQmlDataLoader::load(typeData, mode);
}

typeData->addref();
Expand Down
2 changes: 1 addition & 1 deletion src/qml/qml/qqmltypeloader_p.h
Expand Up @@ -236,7 +236,7 @@ class Q_QML_PRIVATE_EXPORT QQmlTypeLoader : public QQmlDataLoader
};
Q_DECLARE_FLAGS(Options, Option)

QQmlTypeData *get(const QUrl &url);
QQmlTypeData *get(const QUrl &url, Mode mode = PreferSynchronous);
QQmlTypeData *get(const QByteArray &, const QUrl &url, Options = None);
void clearCache();

Expand Down
27 changes: 23 additions & 4 deletions src/qml/qml/v8/qqmlbuiltinfunctions.cpp
Expand Up @@ -1120,7 +1120,7 @@ v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args)
}

/*!
\qmlmethod object Qt::createComponent(url)
\qmlmethod object Qt::createComponent(url, mode)
Returns a \l Component object created using the QML file at the specified \a url,
or \c null if an empty string was given.
Expand All @@ -1129,6 +1129,12 @@ The returned component's \l Component::status property indicates whether the
component was successfully created. If the status is \c Component.Error,
see \l Component::errorString() for an error description.
If the optional \a mode parameter is set to \c Component.Asynchronous, the
component will be loaded in a background thread. The Component::status property
will be \c Component.Loading while it is loading. The status will change to
\c Component.Ready if the component loads successfully, or \c Component.Error
if loading fails.
Call \l {Component::createObject()}{Component.createObject()} on the returned
component to create an object instance of the component.
Expand All @@ -1143,8 +1149,9 @@ use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}.
*/
v8::Handle<v8::Value> createComponent(const v8::Arguments &args)
{
if (args.Length() != 1)
V8THROW_ERROR("Qt.createComponent(): Invalid arguments");
const char *invalidArgs = "Qt.createComponent(): Invalid arguments";
if (args.Length() < 1 || args.Length() > 2)
V8THROW_ERROR(invalidArgs);

QV8Engine *v8engine = V8ENGINE();
QQmlEngine *engine = v8engine->engine();
Expand All @@ -1159,8 +1166,20 @@ v8::Handle<v8::Value> createComponent(const v8::Arguments &args)
if (arg.isEmpty())
return v8::Null();

QQmlComponent::CompilationMode compileMode = QQmlComponent::PreferSynchronous;
if (args.Length() == 2) {
if (args[1]->IsInt32()) {
int mode = args[1]->Int32Value();
if (mode != int(QQmlComponent::PreferSynchronous) && mode != int(QQmlComponent::Asynchronous))
V8THROW_ERROR(invalidArgs);
compileMode = QQmlComponent::CompilationMode(mode);
} else {
V8THROW_ERROR(invalidArgs);
}
}

QUrl url = context->resolvedUrl(QUrl(arg));
QQmlComponent *c = new QQmlComponent(engine, url, engine);
QQmlComponent *c = new QQmlComponent(engine, url, compileMode, engine);
QQmlComponentPrivate::get(c)->creationContext = effectiveContext;
QQmlData::get(c, true)->setImplicitDestructible();
return v8engine->newQObject(c);
Expand Down
9 changes: 7 additions & 2 deletions src/quick/items/qquickloader.cpp
Expand Up @@ -344,7 +344,8 @@ void QQuickLoader::loadFromSource()
}

if (isComponentComplete()) {
d->component = new QQmlComponent(qmlEngine(this), d->source, this);
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
d->load();
}
}
Expand Down Expand Up @@ -711,7 +712,8 @@ void QQuickLoader::componentComplete()
QQuickItem::componentComplete();
if (active()) {
if (d->loadingFromSource) {
d->component = new QQmlComponent(qmlEngine(this), d->source, this);
QQmlComponent::CompilationMode mode = d->asynchronous ? QQmlComponent::Asynchronous : QQmlComponent::PreferSynchronous;
d->component = new QQmlComponent(qmlEngine(this), d->source, mode, this);
}
d->load();
}
Expand Down Expand Up @@ -752,6 +754,9 @@ qreal QQuickLoader::progress() const
This property holds whether the component will be instantiated asynchronously.
When used in conjunction with the \l source property, loading and compilation
will also be performed in a background thread.
Loading asynchronously creates the objects declared by the component
across multiple frames, and reduces the
likelihood of glitches in animation. When loading asynchronously the status
Expand Down

0 comments on commit 31467e8

Please sign in to comment.