Discussions » Development

Lock a script

§
Posted: 10-05-2015

Lock a script

Someone complained about the script I've written is account kicking. It was launched on many tabs/windows. 15-20 tabs loading 300Ko concurrently...
What would be a smart way to lock it in 1 (better 2) process max in different tabs ?
It has to be uneasy changeable.

§
Posted: 10-05-2015

Scripts can share data between tabs in at least two ways:

(1) Greasemonkey storage (GM_getValue, GM_setValue)
(2) Local DOM storage (but the site could conceivably see or clear this)

Therefore, you could have your script log what it is doing and check whether it is doing too much. But you also would need a function for clearing old log entries when a tab is closed or the user logs out of the site. Seems a little tricky...

woxxomMod
§
Posted: 10-05-2015
Edited: 10-05-2015

Agreed.

Here's my take on it.

  1. when the request is allowed (see below) the script stores the time: GM_setValue('script'+scriptID, lastRequestTimestamp) where scriptID is set on script startup to something random
  2. to check whether the request is allowed right now or should be postponed, list the values (GM_listValues()) and look at the stored times to see if enough time has passed since the most recent request
  3. if the request is allowed go to #1
  4. if the request should be postponed set a timeout function for a random amount of time between 50 and 250 ms for example - when the timer function is called, recheck the conditions: go to #2
  5. do a roll call to cleanup orphaned GM values for no longer running scripts once per script
scriptID = Math.round(Math.random() * 1000000000);
setTimeout(cleanupGMstorage, 1000 + Math.random() * 10000);

......................
queueRequest(function() {
    GM_xmlhttpRequest({
        method: 'GET',
        url: '........',
        onload: function(response) {
            ..........
        }
    })
});
......................

function queueRequest(callback) {
    var values = GM_listValues();
    var lastRequestTime = 0;
    for (var i=0; i<values.length; i++) {
        if (values[i].indexOf('script') == 0) {
            lastRequestTime = Math.max(lastRequestTime, GM_getValue(values[i]));
        }
    }
    var now = new Date().getTime();
    if (now - lastRequestTime > 1000) {
        GM_setValue('script' + scriptID, now);
        callback();
    } else {
        setTimeout(queueRequest.bind(this, callback), 50 + Math.random() * 200);
    }
}

function cleanupGMstorage() {
    var token = GM_getValue('roll_call');
    if (token) {
        var tokenTime = token.split('|')[1];
        // don't run cleanup more often than each minute
        if (new Date().getTime() - tokenTime <= 60000) {
            setTimeout(cleanupGMstorage, 60000);
            return;
        }
    }
    token = Math.random() + '|' + new Date().getTime();
    GM_setValue('roll_call', token);
    setTimeout(function() {
        var values = GM_listValues();
        var confirmed = {};
        // collect all roll_call#### responses
        for (var i=0; i<values.length; i++) {
            if (values[i].indexOf('roll_call') == 0 && GM_getValue(values[i]) == token) {
                confirmed[values[i].substr('roll_call'.length)] = true;
                GM_deleteValue(values[i]);
            }
        }
        // delete the script#### values which weren't confirmed
        for (var i=0; i<values.length; i++) {
            if (values[i].indexOf('script') == 0) {
                if (!confirmed[values[i].substr('script'.length)]) {
                    GM_deleteValue(values[i]);
                }
            }
        }
        GM_deleteValue('roll_call');
    }, 2000); // 2 seconds should be enough because roll_call listener runs each second
}

// roll_call listener
setInterval(function() {
    var token = GM_getValue('roll_call');
    if (!token)
        return;
    // delete the token if it's outdated
    // (could happen if the initiator tab was closed before 2 seconds passed)
    if (new Date().getTime() - token.split('|')[1] > 60000) {
        GM_deleteValue('roll_call');
        return;
    }
    // confirm we're running
    GM_setValue('roll_call' + scriptID, token);
}, 1000);
§
Posted: 12-05-2015

Ok, Thanks.
I tough these values (GM getter/setter) have an entry in the menu, why the "uneasy changeable".

§
Posted: 12-05-2015
I tough these values (GM getter/setter) have an entry in the menu, why the "uneasy changeable".

What menu? Where do you see it?

§
Posted: 13-05-2015

It was a mistake. The menu in question is the GM one (through the GM icon OR tools menu), the one where "menu commands" are visible. My bad.

Post reply

Đăng nhập để bình luận