javascriptmultithreadingconcurrencysynchronizationgreasemonkey

Synchronizing multiple instances of same Greasemonkey script


I have a Greasemonkey userscript, that operates on task queue. This queue is bunch of serialized JSON objects, scattered across script private preference space, to be accessed with GM_getValue, GM_setValue and GM_listValues.

I'd like to make this script run simultaneously (same machine, same browser, different tabs), while still keeping shared queue. I know Javascript mostly runs in a single thread (except for Web Workers), but I suppose it's not the case for pages opened in separate tabs, so it has to run concurrently.

In this case, how can I properly organize shared access to such an object, to prevent race conditions? Will race conditions appear at all?


Solution

  • Much to my own surprise, it seems there are no race conditions, because Javascript is run single thread in whole browser. I had tested it with following userscript:

    // ==UserScript==
    // @name        test-concurrency
    // @namespace   mw.tools
    // @include     /^http://localhost//
    // @version     1
    // @grant       GM_getValue
    // @grant       GM_setValue
    // ==/UserScript==
    
    var BLOCK = 1000;
    var ATOM = 'foo';
    switch (window.location.search) {
        case '?inc':
            setInterval(function() {
                var i;
    
                for (i = 0; i < BLOCK; i++) {
                    GM_setValue(ATOM, GM_getValue(ATOM, 0) + 1);
                }
    
                document.title = '' + GM_getValue(ATOM, 0) + ' || ' + (GM_getValue(ATOM, 0) % BLOCK);
            }, 1);
            break;
    
        case '?ver':
            setInterval(function() {
                var v;
                var v0;
    
                v0 = GM_getValue(ATOM);
                for (i = 0; i < BLOCK; i++) {
                    v = GM_getValue(ATOM);
                    if (v != v0) {
                        document.title = v0 + ' || broken';
                        alert('BORKEH!!1111oneone');
                    }
                }
                document.title = v0;
            }, 1);
            break;
    }
    

    With large BLOCK values, browser appears to be mostly unresponsive, regardless of which tab or window offending script is running in.

    So, for synchronizing scripts, it's enough to keep some shared flag telling structure is in use already, and do either random interval polls for it to become free (easy method, but can starve), or use postWindow() broadcasting and maintain pending queue of those who want to acquire "mutex", implementing a Monitor (sort of, because each script invocation is in a critical section).