Javascript semaphore / test-and-set / lock?

JavascriptConcurrencySemaphore

Javascript Problem Overview


Is there such a thing as an atomic test-and-set, semaphore, or lock in Javascript?

I have javascript invoking async background processes via a custom protocol (the background process literally runs in a separate process, unrelated to the browser). I believe I'm running into a race condition; the background process returns between my test and my set, screwing things up on the javascript side. I need a test-and-set operation to make it a real semaphore.

Here's the javascript code that attempts to detect background processes and queue them up:

Call = function () {

var isRunning = true,
	queue = [];

return  {
    // myPublicProperty: "something",

    call: function (method) {
			if (isRunning) {
				console.log("Busy, pushing " + method);
				queue.push(method);
			} else {
				isRunning = true;
				objccall(method);
			}
		},
		
		done: function() {
	    	isRunning = false;
	    	if (queue.length > 0) {
	    		Call.call(queue.shift());
	    	}
	    }
    };
}();

Call is a singleton that implements the queuing; anybody that wants to invoke an external process does Call.call("something") .

Any ideas?

Javascript Solutions


Solution 1 - Javascript

JavaScript has no locking semantics because JS is not a multi threaded language. Multiple threads can only operate concurrently in completely distinct contexts -- eg. HTML5 Worker threads, or in things like multiple instances of JavaScriptCore API's context object (I assume SpiderMonkey has a similar concept). They can't have shared state, so in essence all execution is atomic.

Okay, as you have now provided some of your code i assume you have something akin to:

External Process:
<JSObject>.isRunning = true;
doSomething()
<JSObject>.done()

Or some such (using appropriate APIs). In which case I would expect the JS engine to block if JS is executing in the context of your js object (which is what JavaScriptCore would do), failing that you will probably need to put a manual lock in place around js execution.

What engine are you using to do all of this? I ask because based on your description it sounds like you're setting a flag from a secondary thread from a non-JS language using the C/C++ API provided by that language, and most JS engines assume that any state manipulation made via the API will be occurring on a single thread, typically the same thread that all execution occurs on.

Solution 2 - Javascript

First of all, while it is true that javaScript is single threaded, it is NOT true that no serialization mechanism is ever required by a javaScript application.

A simple example, is when a submit button should fade out for a set amount of time during which an Ajax request to a server is working. When the asynchronous Ajax request successfully completes then a message should appear where the button used to be.

While it would be nice to be able to cancel the button's fadeout and simply set its style to "display: none", as soon as the Ajax request completes, that is not possible in jQuery. Also, a solution could use Events to synchronize the two simultaneous activities, but that is essentially overkill for a simple problem.

A low-tech solution is to poll a lock and when the fadeout completes it is unlocked but the "server done" message is NOT displayed until the success callback, as set by $.post, executes.

var gl_lock;
var gl_selfID;

function poll_lock(message) {
     if (gl_lock === 0) {
          $('#output').text(message).fadeIn(200);
          window.clearInterval(gl_selfID);
     }
 } // end of poll_lock

 function worker() {

     // no one gets in or out
     gl_lock = 1;

     $.post(..., data,function() { 
           gl_selfID = window.setInterval(poll_lock, 40, data.message);
      }, "json");

     // end of fadeout unlock the semaphore
     $('#submit-button').fadeOut(400, function() { gl_lock = 0; });

  } // end of worker

Finally, I think this is more detailed answer, along the lines previously suggested in this discussion by perrohunter.

Solution 3 - Javascript

Maybe you could implement a basic integer semaphore, just add the variable into the DOM and lock/unlock it and make sure your functions keep checking it, else timeout them =)

If you are using a framework such as Mootools you could try to handle the flow of the app with events such as onComplete and so on.

Solution 4 - Javascript

I'm not really sure what the question is asking exactly, but check out my semaphore object here: https://github.com/agamemnus/semaphore.js.

Solution 5 - Javascript

I have ajax stuff which populates select lists, I needed it to be locked so I did something like this. I think you could probably do it simpler though using deferreds and pipes or something.

var semaphore=[];

function myFunc(arg){
   var dfd;
   $.when(semaphore[table]).done(
  	  function(){
		    dfd=myFuncInner(arg);
	  }
	  );
return dfd;
}

function myFuncInner(table){
semaphore[arg] = new $.Deferred();
... 
somethingasynchronous({
    semaphore[arg].resolve();
});

return  semaphore[arg];
}

Solution 6 - Javascript

I had the same issue, here is how I solved it. It works fine for two concurrent processes. If you have three processes or more, it is possible that two processes start together.

var isRunning = false;
...
var id = setInterval(function(){ //loop until isRunning becomes false
            if (!isRunning){
                isRunning = true;
                //do something synchronous or use a promise
                isRunning = false;
                clearInterval(id); // stop the loop here
            }
        , 10);

It is better than the while loop because it solves concurrency/async issues for reading/setting isRunning.

Solution 7 - Javascript

I used the async-mutex package to solve an issue with Expo SQLite not blocking BEGIN TRANSACTION in iOS. You create the mutex and wrap your critical sections in the runExclusive method

    return this.mutex.runExclusive(async () => {
      try {
        await this.executeSqlAsync('begin immediate transaction');
        const tx = new AsyncSQLTransaction(this);
        const rs = await callback(tx);
        await this.executeSqlAsync('commit');
        return rs;
      } catch (error) {
        await this.executeSqlAsync('rollback');
        throw error;
      }
    });

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionParandView Question on Stackoverflow
Solution 1 - JavascriptolliejView Answer on Stackoverflow
Solution 2 - JavascriptJackCColemanView Answer on Stackoverflow
Solution 3 - JavascriptperrohunterView Answer on Stackoverflow
Solution 4 - JavascriptAgamemnusView Answer on Stackoverflow
Solution 5 - JavascriptMark LesterView Answer on Stackoverflow
Solution 6 - JavascriptSébastien EskenaziView Answer on Stackoverflow
Solution 7 - JavascriptArchimedes TrajanoView Answer on Stackoverflow