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 | 1171 | // NOTE: This is private because it must be called on the same thread that created the timers, which is why |
| 1172 | 1172 | // we want to only call it in our own run "shutdown" processing. |
| 1173 | 1173 | void ScriptManager::stopAllTimers() { |
| 1174 | − QMutableHashIterator<QTimer*, CallbackData> i(_timerFunctionMap); | |
| 1175 | − int j {0}; | |
| 1176 | − while (i.hasNext()) { | |
| 1177 | − i.next(); | |
| 1178 | − QTimer* timer = i.key(); | |
| 1179 | − qCDebug(scriptengine) << getFilename() << "stopAllTimers[" << j++ << "]"; | |
| 1180 | − stopTimer(timer); | |
| 1174 | + for (const auto& [handle, value] : _timerFunctionMap) { | |
| 1175 | + QTimer* timer = std::get<0>(value); | |
| 1176 | + | |
| 1177 | + timer->stop(); | |
| 1178 | + delete timer; | |
| 1181 | 1179 | } |
| 1180 | + | |
| 1181 | + _timerFunctionMap.clear(); | |
| 1182 | + _timerHandleCounter = 1; | |
| 1182 | 1183 | } |
| 1183 | 1184 | |
| 1184 | 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 | − QVector<QTimer*> toDelete; | |
| 1187 | − QMutableHashIterator<QTimer*, CallbackData> i(_timerFunctionMap); | |
| 1188 | − while (i.hasNext()) { | |
| 1189 | − i.next(); | |
| 1190 | − if (i.value().definingEntityIdentifier != entityID) { | |
| 1191 | − continue; | |
| 1192 | − } | |
| 1193 | − QTimer* timer = i.key(); | |
| 1194 | − toDelete << timer; // don't delete while we're iterating. save it. | |
| 1186 | + std::vector<int> toDelete; | |
| 1187 | + | |
| 1188 | + for (const auto& [handle, value] : _timerFunctionMap) { | |
| 1189 | + const auto& [timer, callbackData] = value; | |
| 1190 | + | |
| 1191 | + if (callbackData.definingEntityIdentifier != entityID) { continue; } | |
| 1192 | + | |
| 1193 | + timer->stop(); | |
| 1194 | + delete timer; | |
| 1195 | + | |
| 1196 | + toDelete.push_back(handle); | |
| 1195 | 1197 | } |
| 1196 | − for (auto timer:toDelete) { // now reap 'em | |
| 1197 | − stopTimer(timer); | |
| 1198 | + | |
| 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 | 1207 | void ScriptManager::stop(bool marshal) { |
| @@ -1220,7 +1225,7 @@ void ScriptManager::updateMemoryCost(const qint64& deltaSize) { · 17 unmodified lines | ||
| 1220 | 1225 | _engine->updateMemoryCost(deltaSize); |
| 1221 | 1226 | } |
| 1222 | 1227 | |
| 1223 | −void ScriptManager::timerFired() { | |
| 1228 | +void ScriptManager::timerFired(int handle) { | |
| 1224 | 1229 | if (isStopped()) { |
| 1225 | 1230 | scriptWarningMessage("Script.timerFired() while shutting down is ignored... parent script:" + getFilename(), getFilename(), -1); |
| 1226 | 1231 | return; // bail early |
| @@ -1242,25 +1247,29 @@ void ScriptManager::timerFired() { · 15 unmodified lines | ||
| 1242 | 1247 | callTimer.start(); |
| 1243 | 1248 | #endif |
| 1244 | 1249 | |
| 1245 | − QTimer* callingTimer = reinterpret_cast<QTimer*>(sender()); | |
| 1246 | − CallbackData timerData = _timerFunctionMap.value(callingTimer); | |
| 1250 | + if (!_timerFunctionMap.contains(handle)) { | |
| 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 | 1258 | // this timer is done, we can kill it |
| 1250 | − _timerFunctionMap.remove(callingTimer); | |
| 1251 | − delete callingTimer; | |
| 1259 | + _timerFunctionMap.erase(handle); | |
| 1260 | + delete timer; | |
| 1252 | 1261 | } |
| 1253 | 1262 | |
| 1254 | 1263 | // call the associated JS function, if it exists |
| 1255 | − if (timerData.function.isValid()) { | |
| 1264 | + if (data.function.isValid()) { | |
| 1256 | 1265 | PROFILE_RANGE(script, __FUNCTION__); |
| 1257 | 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 | 1268 | auto postTimer = p_high_resolution_clock::now(); |
| 1260 | 1269 | auto elapsed = (postTimer - preTimer); |
| 1261 | 1270 | _totalTimerExecution += std::chrono::duration_cast<std::chrono::microseconds>(elapsed); |
| 1262 | 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 | 1275 | #ifdef SCRIPT_TIMER_PERFORMANCE_STATISTICS |
| @@ -1268,7 +1277,10 @@ void ScriptManager::timerFired() { · 1 unmodified line | ||
| 1268 | 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 | 1284 | // create the timer, add it to the map, and start it |
| 1273 | 1285 | QTimer* newTimer = new QTimer(this); |
| 1274 | 1286 | newTimer->setSingleShot(isSingleShot); |
| @@ -1279,14 +1291,13 @@ QTimer* ScriptManager::setupTimerWithInterval(const ScriptValue& function, int i · 4 unmodified lines | ||
| 1279 | 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 | 1296 | // make sure the timer stops when the script does |
| 1285 | 1297 | connect(this, &ScriptManager::scriptEnding, newTimer, &QTimer::stop); |
| 1286 | 1298 | |
| 1287 | − | |
| 1288 | 1299 | CallbackData timerData = { function, currentEntityIdentifier, currentSandboxURL }; |
| 1289 | − _timerFunctionMap.insert(newTimer, timerData); | |
| 1300 | + _timerFunctionMap.insert({ handle, { newTimer, timerData } }); | |
| 1290 | 1301 | |
| 1291 | 1302 | if (intervalMS < 0) { |
| 1292 | 1303 | int lineNumber = -1; |
| @@ -1302,10 +1313,10 @@ QTimer* ScriptManager::setupTimerWithInterval(const ScriptValue& function, int i · 9 unmodified lines | ||
| 1302 | 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 | 1320 | if (isStopped()) { |
| 1310 | 1321 | int lineNumber = -1; |
| 1311 | 1322 | QString fileName = getFilename(); |
| @@ -1315,13 +1326,13 @@ QTimer* ScriptManager::setInterval(const ScriptValue& function, int intervalMS) · 3 unmodified lines | ||
| 1315 | 1326 | fileName = context->currentFileName(); |
| 1316 | 1327 | } |
| 1317 | 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 | 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 | 1336 | if (isStopped()) { |
| 1326 | 1337 | int lineNumber = -1; |
| 1327 | 1338 | QString fileName = getFilename(); |
| @@ -1331,19 +1342,20 @@ QTimer* ScriptManager::setTimeout(const ScriptValue& function, int timeoutMS) { · 3 unmodified lines | ||
| 1331 | 1342 | fileName = context->currentFileName(); |
| 1332 | 1343 | } |
| 1333 | 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 | 1348 | return setupTimerWithInterval(function, timeoutMS, true); |
| 1338 | 1349 | } |
| 1339 | 1350 | |
| 1340 | −void ScriptManager::stopTimer(QTimer *timer) { | |
| 1341 | − if (_timerFunctionMap.contains(timer)) { | |
| 1351 | +void ScriptManager::stopTimer(int handle) { | |
| 1352 | + if (_timerFunctionMap.contains(handle)) { | |
| 1353 | + auto [timer, callbackData] = _timerFunctionMap.at(handle); | |
| 1342 | 1354 | timer->stop(); |
| 1343 | − _timerFunctionMap.remove(timer); | |
| 1355 | + _timerFunctionMap.erase(handle); | |
| 1344 | 1356 | delete timer; |
| 1345 | 1357 | } else { |
| 1346 | − qCDebug(scriptengine) << "stopTimer -- not in _timerFunctionMap" << timer; | |
| 1358 | + qCDebug(scriptengine) << "stopTimer -- not in _timerFunctionMap" << handle; | |
| 1347 | 1359 | } |
| 1348 | 1360 | } |
| 1349 | 1361 | |