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
1494 1494
1495const auto RETRY_ATTEMPT_BATCH = 100; //glm::clamp(qgetenv("RETRY_ATTEMPT_BATCH").toInt(), 1, 50);1495const auto RETRY_ATTEMPT_BATCH = 100; //glm::clamp(qgetenv("RETRY_ATTEMPT_BATCH").toInt(), 1, 50);
1496const auto RETRY_ATTEMPT_MSECS = 250; //glm::clamp(qgetenv("RETRY_ATTEMPT_MSECS").toInt(), 50, 1000);1496const auto RETRY_ATTEMPT_MSECS = 250; //glm::clamp(qgetenv("RETRY_ATTEMPT_MSECS").toInt(), 50, 1000);
1497const static EntityItemID BAD_SCRIPT_UUID_PLACEHOLDER { "{20170224-dead-face-0000-cee000021114}" };
1497 1498
1498void ScriptEngine::deferLoadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {1499void ScriptEngine::deferLoadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
1499 const auto needTimer = _pendingLoadEntities.isEmpty();1500 const auto needTimer = _pendingLoadEntities.isEmpty();
@@ -1513,12 +1514,18 @@ void ScriptEngine::deferLoadEntityScript(const EntityItemID& entityID, const QSt · 13 unmodified lines
1513 if (details.status != EntityScriptStatus::PENDING) {1514 if (details.status != EntityScriptStatus::PENDING) {
1514 qCDebug(scriptengine) << "deferLoadEntityScript.retry -- entity status no longer PENDING; " << details.status;1515 qCDebug(scriptengine) << "deferLoadEntityScript.retry -- entity status no longer PENDING; " << details.status;
1515 } else {1516 } else {
1516 auto &locker = _lockPerUniqueScriptURL[retry.entityScript];1517 if (!_occupiedScriptURLs.contains(retry.entityScript)) {
1517 if (locker.tryLock()) {1518 // the URL is no longer busy with another Entity, so now it's our turn
1518 locker.unlock();
1519 loadEntityScript(retry.entityID, retry.entityScript, false);1519 loadEntityScript(retry.entityID, retry.entityScript, false);
1520 } else {1520 } 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 }
1522 }1529 }
1523 }1530 }
1524 }1531 }
@@ -1543,7 +1550,7 @@ void ScriptEngine::deferLoadEntityScript(const EntityItemID& entityID, const QSt · 18 unmodified lines
1543 1550
1544 // processDeferredEntities gets wrapped as a "regular" JavaScript callback so that it can then1551 // processDeferredEntities gets wrapped as a "regular" JavaScript callback so that it can then
1545 // be scheduled using setInterval per usual -- allowing the script engine to own its lifecycle.1552 // 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.
1547 1554
1548 QScriptValue function = newLambdaFunction(processDeferredEntities, "processDeferredEntities-loader");1555 QScriptValue function = newLambdaFunction(processDeferredEntities, "processDeferredEntities-loader");
1549 QObject *interval = setInterval( function, RETRY_ATTEMPT_MSECS );1556 QObject *interval = setInterval( function, RETRY_ATTEMPT_MSECS );
@@ -1577,14 +1584,28 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 27 unmodified lines
1577 updateEntityScriptStatus(entityID, EntityScriptStatus::PENDING, "...pending...");1584 updateEntityScriptStatus(entityID, EntityScriptStatus::PENDING, "...pending...");
1578 }1585 }
1579 1586
1580 // This lock/defer approach allows multiple Entities to boot from the same script URL while still taking1587 // This "occupied" approach allows multiple Entities to boot from the same script URL while still taking
1581 // full advantage of cacheable require modules. This only affects Entities literally coming in back-to-back1588 // full advantage of cacheable require modules. This only affects Entities literally coming in back-to-back
1582 // before the first one has time to finish loading.1589 // before the first one has time to finish loading.
1583 auto &locker = _lockPerUniqueScriptURL[entityScript];1590 if (!_occupiedScriptURLs.contains(entityScript)) {
1584 if (!locker.tryLock()) {1591 // the scriptURL is available -- flag as in-use and proceed
1585 qCDebug(scriptengine) << QString("loadEntityScript.deferring[%1] entity: %2 script: %3")1592 _occupiedScriptURLs[entityScript] = entityID;
1586 .arg( _pendingLoadEntities.size()).arg(entityID.toString()).arg(entityScript);1593 } else {
1587 deferLoadEntityScript(entityID, entityScript, forceRedownload);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 }
1588 return;1609 return;
1589 }1610 }
1590 1611
@@ -1598,19 +1619,18 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 7 unmodified lines
1598 newDetails.scriptText = entityScript;1619 newDetails.scriptText = entityScript;
1599 newDetails.status = EntityScriptStatus::LOADING;1620 newDetails.status = EntityScriptStatus::LOADING;
1600 newDetails.definingSandboxURL = currentSandboxURL;1621 newDetails.definingSandboxURL = currentSandboxURL;
1601 _entityScripts[entityID] = newDetails;1622 setEntityScriptDetails(entityID, newDetails);
1602 1623
1603 auto scriptCache = DependencyManager::get<ScriptCache>();1624 auto scriptCache = DependencyManager::get<ScriptCache>();
1604 scriptCache->getScriptContents(entityScript,1625 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) {
1606 if (isStopping()) {1627 if (isStopping()) {
1607#ifdef DEBUG_ENTITY_STATES1628#ifdef DEBUG_ENTITY_STATES
1608 qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- stopping";1629 qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- stopping";
1609#endif1630#endif
1610 locker.unlock();
1611 return;1631 return;
1612 }1632 }
1613 executeOnScriptThread([=, &locker]{1633 executeOnScriptThread([=]{
1614#ifdef DEBUG_ENTITY_STATES1634#ifdef DEBUG_ENTITY_STATES
1615 qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << QUrl(url).fileName() << entityID.toString();1635 qCDebug(scriptengine) << "loadEntityScript.contentAvailable" << status << QUrl(url).fileName() << entityID.toString();
1616#endif1636#endif
@@ -1621,7 +1641,10 @@ void ScriptEngine::loadEntityScript(const EntityItemID& entityID, const QString& · 4 unmodified lines
1621 qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- aborting";1641 qCDebug(scriptengine) << "loadEntityScript.contentAvailable -- aborting";
1622#endif1642#endif
1623 }1643 }
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 }
1625 });1648 });
1626 }, forceRedownload);1649 }, forceRedownload);
1627}1650}
@@ -1666,16 +1689,10 @@ void ScriptEngine::entityScriptContentAvailable(const EntityItemID& entityID, co · 38 unmodified lines
1666 newDetails.status = status;1689 newDetails.status = status;
1667 setEntityScriptDetails(entityID, newDetails);1690 setEntityScriptDetails(entityID, newDetails);
1668 1691
1669 QMutableListIterator<DeferredLoadEntity> i(_pendingLoadEntities);1692 qCDebug(scriptengine) << "entityScriptContentAvailable -- flagging " << oldDetails.scriptText << " as BAD_SCRIPT_UUID_PLACEHOLDER";
1693 // flag this scriptURL as unusuable
1670 // note: we want the *original* entityScript string (not the potentially normalized one from ScriptCache results)1694 // note: we want the *original* entityScript string (not the potentially normalized one from ScriptCache results)
1671 const auto entityScript = oldDetails.scriptText;1695 _occupiedScriptURLs[oldDetails.scriptText] = BAD_SCRIPT_UUID_PLACEHOLDER;
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 }
1679 };1696 };
1680 1697
1681 // NETWORK / FILESYSTEM ERRORS1698 // NETWORK / FILESYSTEM ERRORS
@@ -1856,7 +1873,7 @@ void ScriptEngine::unloadAllEntityScripts() { · 174 unmodified lines
1856 }1873 }
1857 _entityScripts.clear();1874 _entityScripts.clear();
1858 emit entityScriptDetailsUpdated();1875 emit entityScriptDetailsUpdated();
1859 _lockPerUniqueScriptURL.clear();1876 _occupiedScriptURLs.clear();
1860 1877
1861#ifdef DEBUG_ENGINE_STATE1878#ifdef DEBUG_ENGINE_STATE
1862 _debugDump(1879 _debugDump(