Showing the changes to libraries/script-engine/src/ScriptManager.cpp (1 of 3 files). Show all
Unified · Split
| @@ -1171,32 +1171,37 @@ void ScriptManager::run() { · 1170 unmodified lines | |||
| 1171 | // NOTE: This is private because it must be called on the same thread that created the timers, which is why | 1171 | // NOTE: This is private because it must be called on the same thread that created the timers, which is why |
| 1172 | // we want to only call it in our own run "shutdown" processing. | 1172 | // we want to only call it in our own run "shutdown" processing. |
| 1173 | void ScriptManager::stopAllTimers() { | 1173 | void ScriptManager::stopAllTimers() { |
| 1174 | QMutableHashIterator<QTimer*, CallbackData> i(_timerFunctionMap); | 1174 | for (const auto& [handle, value] : _timerFunctionMap) { |
| 1175 | int j {0}; | 1175 | QTimer* timer = std::get<0>(value); |
| 1176 | while (i.hasNext()) { | 1176 | |
| 1177 | i.next(); | 1177 | timer->stop(); |
| 1178 | QTimer* timer = i.key(); | 1178 | delete timer; |
| 1179 | qCDebug(scriptengine) << getFilename() << "stopAllTimers[" << j++ << "]"; | ||
| 1180 | stopTimer(timer); | ||
| 1181 | } | 1179 | } |
| 1180 | |||
| 1181 | _timerFunctionMap.clear(); | ||
| 1182 | _timerHandleCounter = 1; | ||
| 1182 | } | 1183 | } |
| 1183 | 1184 | ||
| 1184 | void ScriptManager::stopAllTimersForEntityScript(const EntityItemID& entityID) { | 1185 | void ScriptManager::stopAllTimersForEntityScript(const EntityItemID& entityID) { |
| 1185 | // We could maintain a separate map of entityID => QTimer, but someone will have to prove to me that it's worth the complexity. -HRS | 1186 | std::vector<int> toDelete; |
| 1186 | QVector<QTimer*> toDelete; | 1187 | |
| 1187 | QMutableHashIterator<QTimer*, CallbackData> i(_timerFunctionMap); | 1188 | for (const auto& [handle, value] : _timerFunctionMap) { |
| 1188 | while (i.hasNext()) { | 1189 | const auto& [timer, callbackData] = value; |
| 1189 | i.next(); | 1190 | |
| 1190 | if (i.value().definingEntityIdentifier != entityID) { | 1191 | if (callbackData.definingEntityIdentifier != entityID) { continue; } |
| 1191 | continue; | 1192 | |
| 1192 | } | 1193 | timer->stop(); |
| 1193 | QTimer* timer = i.key(); | 1194 | delete timer; |
| 1194 | toDelete << timer; // don't delete while we're iterating. save it. | 1195 | |
| 1196 | toDelete.push_back(handle); | ||
| 1195 | } | 1197 | } |
| 1196 | for (auto timer:toDelete) { // now reap 'em | 1198 | |
| 1197 | stopTimer(timer); | 1199 | for (auto handle : toDelete) { |
| 1200 | _timerFunctionMap.erase(handle); | ||
| 1198 | } | 1201 | } |
| 1199 | 1202 | ||
| 1203 | // the timer map is potentially shared across multiple entity scripts, | ||
| 1204 | // so don't clear it or reset the handle counter | ||
| 1200 | } | 1205 | } |
| 1201 | 1206 | ||
| 1202 | void ScriptManager::stop(bool marshal) { | 1207 | void ScriptManager::stop(bool marshal) { |
| @@ -1220,7 +1225,7 @@ void ScriptManager::updateMemoryCost(const qint64& deltaSize) { · 17 unmodified lines | |||
| 1220 | _engine->updateMemoryCost(deltaSize); | 1225 | _engine->updateMemoryCost(deltaSize); |
| 1221 | } | 1226 | } |
| 1222 | 1227 | ||
| 1223 | void ScriptManager::timerFired() { | 1228 | void ScriptManager::timerFired(int handle) { |
| 1224 | if (isStopped()) { | 1229 | if (isStopped()) { |
| 1225 | scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename(), getFilename(), -1); | 1230 | scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename(), getFilename(), -1); |
| 1226 | return; // bail early | 1231 | return; // bail early |
| @@ -1242,25 +1247,29 @@ void ScriptManager::timerFired() { · 15 unmodified lines | |||
| 1242 | callTimer.start(); | 1247 | callTimer.start(); |
| 1243 | #endif | 1248 | #endif |
| 1244 | 1249 | ||
| 1245 | QTimer* callingTimer = reinterpret_cast<QTimer*>(sender()); | 1250 | if (!_timerFunctionMap.contains(handle)) { |
| 1246 | CallbackData timerData = _timerFunctionMap.value(callingTimer); | 1251 | qCWarning(scriptengine) << "timerFired -- not in _timerFunctionMap" << handle; |
| 1252 | return; | ||
| 1253 | } | ||
| 1247 | 1254 | ||
| 1248 | if (!callingTimer->isActive()) { | 1255 | auto [timer, data] = _timerFunctionMap.at(handle); |
| 1256 | |||
| 1257 | if (!timer->isActive()) { | ||
| 1249 | // this timer is done, we can kill it | 1258 | // this timer is done, we can kill it |
| 1250 | _timerFunctionMap.remove(callingTimer); | 1259 | _timerFunctionMap.erase(handle); |
| 1251 | delete callingTimer; | 1260 | delete timer; |
| 1252 | } | 1261 | } |
| 1253 | 1262 | ||
| 1254 | // call the associated JS function, if it exists | 1263 | // call the associated JS function, if it exists |
| 1255 | if (timerData.function.isValid()) { | 1264 | if (data.function.isValid()) { |
| 1256 | PROFILE_RANGE(script, __FUNCTION__); | 1265 | PROFILE_RANGE(script, __FUNCTION__); |
| 1257 | auto preTimer = p_high_resolution_clock::now(); | 1266 | auto preTimer = p_high_resolution_clock::now(); |
| 1258 | callWithEnvironment(timerData.definingEntityIdentifier, timerData.definingSandboxURL, timerData.function, timerData.function, ScriptValueList()); | 1267 | callWithEnvironment(data.definingEntityIdentifier, data.definingSandboxURL, data.function, data.function, ScriptValueList()); |
| 1259 | auto postTimer = p_high_resolution_clock::now(); | 1268 | auto postTimer = p_high_resolution_clock::now(); |
| 1260 | auto elapsed = (postTimer - preTimer); | 1269 | auto elapsed = (postTimer - preTimer); |
| 1261 | _totalTimerExecution += std::chrono::duration_cast<std::chrono::microseconds>(elapsed); | 1270 | _totalTimerExecution += std::chrono::duration_cast<std::chrono::microseconds>(elapsed); |
| 1262 | } else { | 1271 | } else { |
| 1263 | qCWarning(scriptengine) << "timerFired -- invalid function" << timerData.function.toVariant().toString(); | 1272 | qCWarning(scriptengine) << "timerFired -- invalid function" << data.function.toVariant().toString(); |
| 1264 | } | 1273 | } |
| 1265 | 1274 | ||
| 1266 | #ifdef SCRIPT_TIMER_PERFORMANCE_STATISTICS | 1275 | #ifdef SCRIPT_TIMER_PERFORMANCE_STATISTICS |
| @@ -1268,7 +1277,10 @@ void ScriptManager::timerFired() { · 1 unmodified line | |||
| 1268 | #endif | 1277 | #endif |
| 1269 | } | 1278 | } |
| 1270 | 1279 | ||
| 1271 | QTimer* ScriptManager::setupTimerWithInterval(const ScriptValue& function, int intervalMS, bool isSingleShot) { | 1280 | int ScriptManager::setupTimerWithInterval(const ScriptValue& function, int intervalMS, bool isSingleShot) { |
| 1281 | int handle = _timerHandleCounter; | ||
| 1282 | _timerHandleCounter += 1; | ||
| 1283 | |||
| 1272 | // create the timer, add it to the map, and start it | 1284 | // create the timer, add it to the map, and start it |
| 1273 | QTimer* newTimer = new QTimer(this); | 1285 | QTimer* newTimer = new QTimer(this); |
| 1274 | newTimer->setSingleShot(isSingleShot); | 1286 | newTimer->setSingleShot(isSingleShot); |
| @@ -1279,14 +1291,13 @@ QTimer* ScriptManager::setupTimerWithInterval(const ScriptValue& function, int i · 4 unmodified lines | |||
| 1279 | newTimer->setTimerType(Qt::PreciseTimer); | 1291 | newTimer->setTimerType(Qt::PreciseTimer); |
| 1280 | } | 1292 | } |
| 1281 | 1293 | ||
| 1282 | connect(newTimer, &QTimer::timeout, this, &ScriptManager::timerFired); | 1294 | connect(newTimer, &QTimer::timeout, [this, handle]() { timerFired(handle); }); |
| 1283 | 1295 | ||
| 1284 | // make sure the timer stops when the script does | 1296 | // make sure the timer stops when the script does |
| 1285 | connect(this, &ScriptManager::scriptEnding, newTimer, &QTimer::stop); | 1297 | connect(this, &ScriptManager::scriptEnding, newTimer, &QTimer::stop); |
| 1286 | 1298 | ||
| 1287 | |||
| 1288 | CallbackData timerData = { function, currentEntityIdentifier, currentSandboxURL }; | 1299 | CallbackData timerData = { function, currentEntityIdentifier, currentSandboxURL }; |
| 1289 | _timerFunctionMap.insert(newTimer, timerData); | 1300 | _timerFunctionMap.insert({ handle, { newTimer, timerData } }); |
| 1290 | 1301 | ||
| 1291 | if (intervalMS < 0) { | 1302 | if (intervalMS < 0) { |
| 1292 | int lineNumber = -1; | 1303 | int lineNumber = -1; |
| @@ -1302,10 +1313,10 @@ QTimer* ScriptManager::setupTimerWithInterval(const ScriptValue& function, int i · 9 unmodified lines | |||
| 1302 | newTimer->start(intervalMS); | 1313 | newTimer->start(intervalMS); |
| 1303 | } | 1314 | } |
| 1304 | 1315 | ||
| 1305 | return newTimer; | 1316 | return handle; |
| 1306 | } | 1317 | } |
| 1307 | 1318 | ||
| 1308 | QTimer* ScriptManager::setInterval(const ScriptValue& function, int intervalMS) { | 1319 | int ScriptManager::setInterval(const ScriptValue& function, int intervalMS) { |
| 1309 | if (isStopped()) { | 1320 | if (isStopped()) { |
| 1310 | int lineNumber = -1; | 1321 | int lineNumber = -1; |
| 1311 | QString fileName = getFilename(); | 1322 | QString fileName = getFilename(); |
| @@ -1315,13 +1326,13 @@ QTimer* ScriptManager::setInterval(const ScriptValue& function, int intervalMS) · 3 unmodified lines | |||
| 1315 | fileName = context->currentFileName(); | 1326 | fileName = context->currentFileName(); |
| 1316 | } | 1327 | } |
| 1317 | scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename(), fileName, lineNumber); | 1328 | scriptWarningMessage("Script.setInterval() while shutting down is ignored... parent script:" + getFilename(), fileName, lineNumber); |
| 1318 | return NULL; // bail early | 1329 | return 0; // bail early |
| 1319 | } | 1330 | } |
| 1320 | 1331 | ||
| 1321 | return setupTimerWithInterval(function, intervalMS, false); | 1332 | return setupTimerWithInterval(function, intervalMS, false); |
| 1322 | } | 1333 | } |
| 1323 | 1334 | ||
| 1324 | QTimer* ScriptManager::setTimeout(const ScriptValue& function, int timeoutMS) { | 1335 | int ScriptManager::setTimeout(const ScriptValue& function, int timeoutMS) { |
| 1325 | if (isStopped()) { | 1336 | if (isStopped()) { |
| 1326 | int lineNumber = -1; | 1337 | int lineNumber = -1; |
| 1327 | QString fileName = getFilename(); | 1338 | QString fileName = getFilename(); |
| @@ -1331,19 +1342,20 @@ QTimer* ScriptManager::setTimeout(const ScriptValue& function, int timeoutMS) { · 3 unmodified lines | |||
| 1331 | fileName = context->currentFileName(); | 1342 | fileName = context->currentFileName(); |
| 1332 | } | 1343 | } |
| 1333 | scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename(), fileName, lineNumber); | 1344 | scriptWarningMessage("Script.setTimeout() while shutting down is ignored... parent script:" + getFilename(), fileName, lineNumber); |
| 1334 | return NULL; // bail early | 1345 | return 0; // bail early |
| 1335 | } | 1346 | } |
| 1336 | 1347 | ||
| 1337 | return setupTimerWithInterval(function, timeoutMS, true); | 1348 | return setupTimerWithInterval(function, timeoutMS, true); |
| 1338 | } | 1349 | } |
| 1339 | 1350 | ||
| 1340 | void ScriptManager::stopTimer(QTimer *timer) { | 1351 | void ScriptManager::stopTimer(int handle) { |
| 1341 | if (_timerFunctionMap.contains(timer)) { | 1352 | if (_timerFunctionMap.contains(handle)) { |
| 1353 | auto [timer, callbackData] = _timerFunctionMap.at(handle); | ||
| 1342 | timer->stop(); | 1354 | timer->stop(); |
| 1343 | _timerFunctionMap.remove(timer); | 1355 | _timerFunctionMap.erase(handle); |
| 1344 | delete timer; | 1356 | delete timer; |
| 1345 | } else { | 1357 | } else { |
| 1346 | qCDebug(scriptengine) << "stopTimer -- not in _timerFunctionMap" << timer; | 1358 | qCDebug(scriptengine) << "stopTimer -- not in _timerFunctionMap" << handle; |
| 1347 | } | 1359 | } |
| 1348 | } | 1360 | } |
| 1349 | 1361 | ||