History

Adapt from std::map locks to simple QHash for flagging busy scriptURLs

humbletim committed 9 years ago

Commit c89d2031 parent 3ad3fd42 files · +44 −27Browse files

Showing the changes to libraries/script-engine/src/ScriptEngine.cpp (1 of 2 files). Show all

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+43−26
@@ -1494,6 +1494,7 @@ bool ScriptEngine::getEntityScriptDetails(const EntityItemID& entityID, EntitySc · 1493 unmodified lines
14941494
14951495 const auto RETRY_ATTEMPT_BATCH = 100; //glm::clamp(qgetenv("RETRY_ATTEMPT_BATCH").toInt(), 1, 50);
14961496 const auto RETRY_ATTEMPT_MSECS = 250; //glm::clamp(qgetenv("RETRY_ATTEMPT_MSECS").toInt(), 50, 1000);
1497+const static EntityItemID BAD_SCRIPT_UUID_PLACEHOLDER { "{20170224-dead-face-0000-cee000021114}" };
14971498
14981499 void ScriptEngine::deferLoadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
14991500 const auto needTimer = _pendingLoadEntities.isEmpty();
@@ -1513,12 +1514,18 @@ void ScriptEngine::deferLoadEntityScript(const EntityItemID& entityID, const QSt · 13 unmodified lines
15131514 if (details.status != EntityScriptStatus::PENDING) {
15141515 qCDebug(scriptengine) << "deferLoadEntityScript.retry -- entity status no longer PENDING; " << details.status;
15151516 } else {
1516− auto &locker = _lockPerUniqueScriptURL[retry.entityScript];
1517− if (locker.tryLock()) {
1518− locker.unlock();
1517+ if (!_occupiedScriptURLs.contains(retry.entityScript)) {
1518+ // the URL is no longer busy with another Entity, so now it's our turn
15191519 loadEntityScript(retry.entityID, retry.entityScript, false);
15201520 } else {
1521− stillPending << retry;
1521+ auto entityID = _occupiedScriptURLs[retry.entityScript];
1522+ if (entityID == BAD_SCRIPT_UUID_PLACEHOLDER) {
1523+ // the URL was marked bad by an earlier load attempt, so abort the retry mission
1524+ qCDebug(scriptengine) << "... pending load of " << retry.entityID << " cancelled";
1525+ } else {
1526+ // the URL is still occupied by another Entity, so add to next retry pass
1527+ stillPending << retry;
1528+ }
15221529 }
15231530 }
15241531 }
@@ -1543,7 +1550,7 @@ void ScriptEngine::deferLoadEntityScript(const EntityItemID& entityID, const QSt · 18 unmodified lines
15431550
15441551 // processDeferredEntities gets wrapped as a "regular" JavaScript callback so that it can then
15451552 // be scheduled using setInterval per usual -- allowing the script engine to own its lifecycle.
1546− // we stil need to call clearInterval manually when done, so its passed to calle() above as a property.
1553+ // we stil need to call clearInterval manually when done, so its passed to callee() above as a property.
15471554
15481555 QScriptValue function = newLambdaFunction(processDeferredEntities, "processDeferredEntities-loader");
15491556 QObject *interval = setInterval( function, RETRY_ATTEMPT_MSECS );
@@ -1577,14 +1584,28 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 27 unmodified lines
15771584 updateEntityScriptStatus(entityID, EntityScriptStatus::PENDING, "...pending...");
15781585 }
15791586
1580− // This lock/defer approach allows multiple Entities to boot from the same script URL while still taking
1587+ // This "occupied" approach allows multiple Entities to boot from the same script URL while still taking
15811588 // full advantage of cacheable require modules. This only affects Entities literally coming in back-to-back
15821589 // before the first one has time to finish loading.
1583− auto &locker = _lockPerUniqueScriptURL[entityScript];
1584− if (!locker.tryLock()) {
1585− qCDebug(scriptengine) << QString("loadEntityScript.deferring[%1] entity: %2 script: %3")
1586− .arg( _pendingLoadEntities.size()).arg(entityID.toString()).arg(entityScript);
1587− deferLoadEntityScript(entityID, entityScript, forceRedownload);
1590+ if (!_occupiedScriptURLs.contains(entityScript)) {
1591+ // the scriptURL is available -- flag as in-use and proceed
1592+ _occupiedScriptURLs[entityScript] = entityID;
1593+ } else {
1594+ // another Entity is either still booting from this script URL or has failed and marked it "out of order"
1595+ auto currentEntityID = _occupiedScriptURLs[entityScript];
1596+ if (currentEntityID == BAD_SCRIPT_UUID_PLACEHOLDER && !forceRedownload) {
1597+ // since not force-reloading, assume that the exact same input would produce the exact same output again
1598+ // note: this state gets reset with "reload all scripts," leaving/returning to a Domain, clear cache, etc.
1599+ qCDebug(scriptengine) << QString("loadEntityScript.cancelled entity: %1 script: %2 (previous script failure)")
1600+ .arg(entityID.toString()).arg(entityScript);
1601+ updateEntityScriptStatus(entityID, EntityScriptStatus::ERROR_LOADING_SCRIPT,
1602+ "Cached Script was marked invalid by previous Entity load failure.");
1603+ } else {
1604+ // another entity is busy loading from this script URL so wait for them to finish
1605+ qCDebug(scriptengine) << QString("loadEntityScript.deferring[%0] entity: %1 script: %2")
1606+ .arg( _pendingLoadEntities.size()).arg(entityID.toString()).arg(entityScript);
1607+ deferLoadEntityScript(entityID, entityScript, forceRedownload);
1608+ }
15881609 return;
15891610 }
15901611
@@ -1598,19 +1619,18 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 7 unmodified lines
15981619 newDetails.scriptText = entityScript;
15991620 newDetails.status = EntityScriptStatus::LOADING;
16001621 newDetails.definingSandboxURL = currentSandboxURL;
1601− _entityScripts[entityID] = newDetails;
1622+ setEntityScriptDetails(entityID, newDetails);
16021623
16031624 auto scriptCache = DependencyManager::get<ScriptCache>();
16041625 scriptCache->getScriptContents(entityScript,
1605− [=, &locker](const QString& url, const QString& contents, bool isURL, bool success, const QString& status) {
1626+ [this, entityScript, entityID](const QString& url, const QString& contents, bool isURL, bool success, const QString& status) {
16061627 if (isStopping()) {
16071628 #ifdef DEBUG_ENTITY_STATES
16081629 qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- stopping";
16091630 #endif
1610− locker.unlock();
16111631 return;
16121632 }
1613− executeOnScriptThread([=, &locker]{
1633+ executeOnScriptThread([=]{
16141634 #ifdef DEBUG_ENTITY_STATES
16151635 qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << QUrl(url).fileName() << entityID.toString();
16161636 #endif
@@ -1621,7 +1641,10 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 4 unmodified lines
16211641 qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- aborting";
16221642 #endif
16231643 }
1624− locker.unlock();
1644+ // recheck whether us since may have been set to BAD_SCRIPT_UUID_PLACEHOLDER in entityScriptContentAvailable
1645+ if (_occupiedScriptURLs[entityScript] == entityID) {
1646+ _occupiedScriptURLs.remove(entityScript);
1647+ }
16251648 });
16261649 }, forceRedownload);
16271650 }
@@ -1666,16 +1689,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 38 unmodified lines
16661689 newDetails.status = status;
16671690 setEntityScriptDetails(entityID, newDetails);
16681691
1669− QMutableListIterator<DeferredLoadEntity> i(_pendingLoadEntities);
1692+ qCDebug(scriptengine) << "entityScriptContentAvailable -- flagging " << oldDetails.scriptText << " as BAD_SCRIPT_UUID_PLACEHOLDER";
1693+ // flag this scriptURL as unusuable
16701694 // note: we want the *original* entityScript string (not the potentially normalized one from ScriptCache results)
1671− const auto entityScript = oldDetails.scriptText;
1672− while (i.hasNext()) {
1673− auto retry = i.next();
1674− if (retry.entityScript == entityScript) {
1675− qCDebug(scriptengine) << "... pending load of " << retry.entityID << " cancelled";
1676− i.remove();
1677− }
1678− }
1695+ _occupiedScriptURLs[oldDetails.scriptText] = BAD_SCRIPT_UUID_PLACEHOLDER;
16791696 };
16801697
16811698 // NETWORK / FILESYSTEM ERRORS
@@ -1856,7 +1873,7 @@ void ScriptEngine::unloadAllEntityScripts() { · 174 unmodified lines
18561873 }
18571874 _entityScripts.clear();
18581875 emit entityScriptDetailsUpdated();
1859− _lockPerUniqueScriptURL.clear();
1876+ _occupiedScriptURLs.clear();
18601877
18611878 #ifdef DEBUG_ENGINE_STATE
18621879 _debugDump(