How to implement a lock in JavaScript - javascript

How could something equivalent to lock in C# be implemented in JavaScript?
So, to explain what I'm thinking a simple use case is:
User clicks button B. B raises an onclick event. If B is in event-state the event waits for B to be in ready-state before propagating. If B is in ready-state, B is locked and is set to event-state, then the event propagates. When the event's propagation is complete, B is set to ready-state.
I could see how something close to this could be done, simply by adding and removing the class ready-state from the button. However, the problem is that a user can click a button twice in a row faster than the variable can be set, so this attempt at a lock will fail in some circumstances.
Does anyone know how to implement a lock that will not fail in JavaScript?

Lock is a questionable idea in JS which is intended to be threadless and not needing concurrency protection. You're looking to combine calls on deferred execution. The pattern I follow for this is the use of callbacks. Something like this:
var functionLock = false;
var functionCallbacks = [];
var lockingFunction = function (callback) {
if (functionLock) {
functionCallbacks.push(callback);
} else {
$.longRunning(function(response) {
while(functionCallbacks.length){
var thisCallback = functionCallbacks.pop();
thisCallback(response);
}
});
}
}
You can also implement this using DOM event listeners or a pubsub solution.

JavaScript is, with a very few exceptions (XMLHttpRequest onreadystatechange handlers in some versions of Firefox) event-loop concurrent. So you needn't worry about locking in this case.
JavaScript has a concurrency model based on an "event loop". This model is quite different than the model in other languages like C or Java.
...
A JavaScript runtime contains a message queue, which is a list of messages to be processed. To each message is associated a function. When the stack is empty, a message is taken out of the queue and processed. The processing consists of calling the associated function (and thus creating an initial stack frame) The message processing ends when the stack becomes empty again.
...
Each message is processed completely before any other message is processed. This offers some nice properties when reasoning about your program, including the fact that whenever a function runs, it cannot be pre-empted and will run entirely before any other code runs (and can modify data the function manipulates). This differs from C, for instance, where if a function runs in a thread, it can be stopped at any point to run some other code in another thread.
A downside of this model is that if a message takes too long to complete, the web application is unable to process user interactions like click or scroll. The browser mitigates this with the "a script is taking too long to run" dialog. A good practice to follow is to make message processing short and if possible cut down one message into several messages.
For more links on event-loop concurrency, see E

I've had success mutex-promise.
I agree with other answers that you might not need locking in your case. But it's not true that one never needs locking in Javascript. You need mutual exclusivity when accessing external resources that do not handle concurrency.

Locks are a concept required in a multi-threaded system. Even with worker threads, messages are sent by value between workers so that locking is unnecessary.
I suspect you need to just set a semaphore (flagging system) between your buttons.

Here's a simple lock mechanism, implemented via closure
const createLock = () => {
let lockStatus = false
const release = () => {
lockStatus = false
}
const acuire = () => {
if (lockStatus == true)
return false
lockStatus = true
return true
}
return {
lockStatus: lockStatus,
acuire: acuire,
release: release,
}
}
lock = createLock() // create a lock
lock.acuire() // acuired a lock
if (lock.acuire()){
console.log("Was able to acuire");
} else {
console.log("Was not to acuire"); // This will execute
}
lock.release() // now the lock is released
if(lock.acuire()){
console.log("Was able to acuire"); // This will execute
} else {
console.log("Was not to acuire");
}
lock.release() // Hey don't forget to release

If it helps anyone in 2022+, all major browsers now support Web Locks API although experimental.
To quote the example in MDN:
await do_something_without_lock();
// Request the lock.
await navigator.locks.request('my_resource', async (lock) => {
// The lock has been acquired.
await do_something_with_lock();
await do_something_else_with_lock();
// Now the lock will be released.
});
// The lock has been released.
await do_something_else_without_lock();
Lock is automatically released when the callback returns
Locks are scoped to origins (https://example.com != https://example.org:8080), and work across tabs/workers.
Lock requests are queued (first come-first served); (unlike in some other languages where the lock is passed to some thread at random)
navigator.locks.query() can be used to see what has the lock, and who are in the queue to acquire the lock
There is a mode="shared" to implement a readers-writers lock if you need it

Why don't you disable the button and enable it after you finish the event?
<input type="button" id="xx" onclick="checkEnableSubmit('true');yourFunction();">
<script type="text/javascript">
function checkEnableSubmit(status) {
document.getElementById("xx").disabled = status;
}
function yourFunction(){
//add your functionality
checkEnableSubmit('false');
}
</script>
Happy coding !!!

Some addition to JoshRiver's answer according to my case;
var functionCallbacks = [];
var functionLock = false;
var getData = function (url, callback) {
if (functionLock) {
functionCallbacks.push(callback);
} else {
functionLock = true;
functionCallbacks.push(callback);
$.getJSON(url, function (data) {
while (functionCallbacks.length) {
var thisCallback = functionCallbacks.pop();
thisCallback(data);
}
functionLock = false;
});
}
};
// Usage
getData("api/orders",function(data){
barChart(data);
});
getData("api/orders",function(data){
lineChart(data);
});
There will be just one api call and these two function will consume same result.

Locks still have uses in JS. In my experience I only needed to use locks to prevent spam clicking on elements making AJAX calls.
If you have a loader set up for AJAX calls then this isn't required (as well as disabling the button after clicking).
But either way here is what I used for locking:
var LOCK_INDEX = [];
function LockCallback(key, action, manual) {
if (LOCK_INDEX[key])
return;
LOCK_INDEX[key] = true;
action(function () { delete LOCK_INDEX[key] });
if (!manual)
delete LOCK_INDEX[key];
}
Usage:
Manual unlock (usually for XHR)
LockCallback('someKey',(delCallback) => {
//do stuff
delCallback(); //Unlock method
}, true)
Auto unlock
LockCallback('someKey',() => {
//do stuff
})

Related

JavaScript async vs C# async

EDIT: found the answer - https://www.youtube.com/watch?v=8aGhZQkoFbQ
_
Okay, so I have some C# background and C# "async environment" is a bit of a mixed bag of concurrency and "small parallelism" i.e. when it comes to heavy async environment you can have race conditions, deadlock and you need to protect shared resources.
Now, I'm trying to understand how does JavaScript/ES6 async environment works. Consider following code:
// current context: background "page"
// independent from content page
let busy = false;
// This is an event handler that receives event from content page
// It can happen at any time
runtimeObj.onMessage.addListener((request) =>
{
if(request.action === 'AddEntry')
{
AddEntry(request);
return true;
}
return false;
} );
function AddEntry(data)
{
if (!busy)
group.push({url: data.url, time: Date.now(), session: data.session});
else
setTimeout(AddEntry(data),10000) // simulating Semaphore wait
}
// called from asynchronous function setInterval()
function SendPOST()
{
if (groups.length < 1 || groups === undefined)
return;
busy = true; // JS has no semaphores so I "simulate it"
let del = [];
groups.forEach(item =>
{
if (Date.now() - item.time > 3600000)
{
del.push(item);
let xhr = new XMLHttpRequest();
let data = new FormData();
data.append('action', 'leave');
data.append('sessionID', item.session);
xhr.withCredentials = true;
http.onreadystatechange = function()
{
if(http.readyState == 4 && http.status !== 200) {
console.log(`Unable to part group ${item.url}! Reason: ${http.status}. Leave group manually.`)
}
}
xhr.open('POST', item.url, true);
xhr.send(data);
}
});
del.forEach(item => groups.slice(item,1));
busy = false;
}
setInterval(SendPOST, 60000);
This is not really the best example, since it does not have bunch of async keyword paired with functions but in my understanding both sendPost() and AddEntry() arent really pure sequential operations. Nonetheless, I've been told that in AddEntry(), busy always will be false because:
sendPost is queued to execute in a minute
an event is add to the event loop
the event is processed and AddEntry is called
since busy = false the group is pushed
a minute passes and SendPost is added to the event loop
the event is process and SendPost is called
groups.length === 1 so it continues
busy = true
each group causes a request to get queued
an event comes in
busy = false
the event is processed, AddEntry is called
busy is false, like it will always be
group is pushed
eventually the requests from before are resolved, and t
he onreadystatechange callbacks are put on the event loop
eventually each of the callbacks are processed and the logging statements are executed
Is this correct? From what I understand it essentially means there can be no race conditions or deadlock or I ever need to protect shared resource.
If I'm two write a similar code for C# runtime where sendPost() and AddEntry() are asynchronous task methods that can be called in non-blocking way from different event sources there could situation when I access shared resource while the iteration context is temporally suspended in context switch by Thread Sheduler.
Is this correct?
Yes, it describes adequately whats happening.
JS runs in a single threaded way, which means that a function will always run till it's end.
setTimeout(function concurrently() {
console.log("this will never run");
});
while(true) console.log("because this is blocking the only thread JS has");
there can be no race conditions ...
There can (in a logical way). However, they cannot appear in synchronous code, as JavaScript runs single threaded¹. If you add a callback to something, or await a promise, then other code might run in the meantime (but never at the same time!), which might cause a race condition:
let block = false;
async function run() {
if(block) return // only run this once
// If we'd do block = true here, this would be totally safe
await Promise.resolve(); // things might get out of track here
block = true;
console.log("whats going on?");
}
run(); run();
... nor deadlocks ...
Yes, they are quite impossible (except if you use Atomics¹).
I never need to protect shared resource.
Yes. Because there are no shared ressources (okay, except SharedBuffers¹).
¹: By using WebWorkers (or threads on NodeJS), you actually control multiple JS threads. However each thread runs its own JS code, so variables are not shared. These threads can pass messages between each other and they can also share a specific memory construct (a SharedArrayBuffer). That can then be accessed concurrently, and all the effects of concurrent access apply (but you will rarely use it, so ...).

How to port older (not event driven) software to javascript

I want to port some old software to javascript. These programs are typically not event driven and just turn in a loop. They only pause to get input from the input stream. I can't just convert them to javascript since there is no equivalent for a classic pascal or c read instruction. I thought it would be possible to use an input field which would fire an onchange event. My program would then be suspended until the event fires. But apparently you can't suspend a JS program.
My second attempt was to set a flag on the onchange event. My program stays in a loop until the flag is set and then reads the value of the input field. But to prevent the browser from getting blocked by this loop I need some sleep functionality between two polls. Apparently there is no equivalent of a sleep function in JS.
How can this be done ?
You could use Promises and await/async to create code that looks like what you know from blocking code.
But it is important to note that this is not blocking code. At the await other code waiting to be executed can interleave.
function waitForIntput(id) {
// create a Promise that resolves at the input event
return new Promise((resolve, reject) => {
let elm = document.getElementById(id)
function listener(evt) {
// remove the listener so that no memory leaking occures
elm.removeEventListener('input', listener)
// resolve the promise with the current value of the element
resolve(elm.value)
}
// call the listener on the input event
elm.addEventListener('input', listener, false);
})
}
(async function() {
while(true) {
console.log('before waitForIntput')
console.log(await waitForIntput('test'))
console.log('after waitForIntput')
}
}())
<input id="test">
If it is a good idea to solve it that way depends on the exact use-case. In general you should check how the task you want to perform should be solved in the new environment, instead of forcing the old style into the new environment.
If you're coming from a language that does something like...
while(true) {
x = readInput();
processInput(x);
}
Then you're correct, there is no direct equivalent in JavaScript. You need to forget about looping, and instead think of everything that happens in your loop before it blocks on user input as one part that sets up an event, and everything that happens after as a callback that handles that event.
The above (very trivial) program would be rewritten in JavaScript as something like:
readInput().then((x) => { processInput(x) });

Do I need to be concerned with race conditions with asynchronous Javascript?

Suppose I load some Flash movie that I know at some point in the future will call window.flashReady and will set window.flashReadyTriggered = true.
Now I have a block of code that I want to have executed when the Flash is ready. I want it to execute it immediately if window.flashReady has already been called and I want to put it as the callback in window.flashReady if it has not yet been called. The naive approach is this:
if(window.flashReadyTriggered) {
block();
} else {
window.flashReady = block;
}
So the concern I have based on this is that the expression in the if condition is evaluated to false, but then before block() can be executed, window.flashReady is triggered by the external Flash. Consequently, block is never called.
Is there a better design pattern to accomplish the higher level goal I'm going for (e.g., manually calling the flashReady callback)? If not, am I safe, or are there other things I should do?
All Javascript event handler scripts are handled from one master event queue system. This means that event handlers run one at a time and one runs until completion before the next one that's ready to go starts running. As such, there are none of the typical race conditions in Javascript that one would see in a multithreaded language where multiple threads of the language can be running at once (or time sliced) and create real-time conflict for access to variables.
Any individual thread of execution in javascript will run to completion before the next one starts. That's how Javascript works. An event is pulled from the event queue and then code starts running to handle that event. That code runs by itself until it returns control to the system where the system will then pull the next event from the event queue and run that code until it returns control back to the system.
Thus the typical race conditions that are caused by two threads of execution going at the same time do not happen in Javascript.
This includes all forms of Javascript events including: user events (mouse, keys, etc..), timer events, network events (ajax callbacks), etc...
The only place you can actually do multi-threading in Javascript is with the HTML5 Web Workers or Worker Threads (in node.js), but they are very isolated from regular javascript (they can only communicate with regular javascript via message passing) and cannot manipulate the DOM at all and must have their own scripts and namespace, etc...
While I would not technically call this a race condition, there are situations in Javascript because of some of its asynchronous operations where you may have two or more asynchronous operations in flight at the same time (not actually executing Javascript, but the underlying asynchronous operation is running native code at the same time) and it may be unpredictable when each operation will complete relative to the others. This creates an uncertainty of timing which (if the relative timing of the operations is important to your code) creates something you have to manually code for. You may need to sequence the operations so one runs and you literally wait for it to complete before starting the next one. Or, you may start all three operations and then have some code that collects all three results and when they are all ready, then your code proceeds.
In modern Javascript, promises are generally used to manage these types of asynchronous operations.
So, if you had three asynchronous operations that each return a promise (like reading from a database, fetching a request from another server, etc...), you could manually sequence then like this:
a().then(b).then(c).then(result => {
// result here
}).catch(err => {
// error here
});
Or, if you wanted them all to run together (all in flight at the same time) and just know when they were all done, you could do:
Promise.all([a(), b(), c()])..then(results => {
// results here
}).catch(err => {
// error here
});
While I would not call these race conditions, they are in the same general family of designing your code to control indeterminate sequencing.
There is one special case that can occur in some situations in the browser. It's not really a race condition, but if you're using lots of global variables with temporary state, it could be something to be aware of. When your own code causes another event to occur, the browser will sometimes call that event handler synchronously rather than waiting until the current thread of execution is done. An example of this is:
click
the click event handler changes focus to another field
that other field has an event handler for onfocus
browser calls the onfocus event handler immediately
onfocus event handler runs
the rest of the click event handler runs (after the .focus() call)
This isn't technically a race condition because it's 100% known when the onfocus event handler will execute (during the .focus() call). But, it can create a situation where one event handler runs while another is in the middle of execution.
JavaScript is single threaded. There are no race conditions.
When there is no more code to execute at your current "instruction pointer", the "thread" "passes the baton", and a queued window.setTimeout or event handler may execute its code.
You will get better understanding for Javascript's single-threading approach reading node.js's design ideas.
Further reading:
Why doesn't JavaScript support multithreading?
It is important to note that you may still experience race conditions if you eg. use multiple async XMLHttpRequest. Where the order of returned responses is not defined (that is responses may not come back in the same order they were send). Here the output depends on the sequence or timing of other uncontrollable events (server latency etc.). This is a race condition in a nutshell.
So even using a single event queue (like in JavaScript) does not prevent events coming in uncontrollable order and your code should take care of this.
Sure you need. It happens all the time:
<button onClick=function() {
const el = document.getElementById("view");
fetch('/some/api').then((data) => {
el.innerHTML = JSON.stringify(data);
})
}>Button 1</button>
<button onClick=function() {
const el = document.getElementById("view");
fetch('/some/other/api').then((data) => {
el.innerHTML = JSON.stringify(data);
})
}>Button 2</button>
Some people don't view it as a race condition.
But it really is.
Race condition is broadly defined as "the behavior of an electronic, software, or other system where the output is dependent on the sequence or timing of other uncontrollable events".
If user clicks these 2 buttons in a brief period, the output is not guaranteed to depend of the order of clicking. It depends on which api request will be resolved sooner. Moreover, the DOM element you're referencing can be removed by some other event (like changing route).
You can mitigate this race condition by disabling button or showing some spinner when loading operation in progress, but that's cheating. You should have some mutex/counter/semaphore at the code level to control your asynchronous flow.
To adapt it to your question, it depends on what "block()" is. If it's a synchronous function, you don't need to worry. But if it's asynchronous, you have to worry:
function block() {
window.blockInProgress = true;
// some asynchronous code
return new Promise(/* window.blockInProgress = false */);
}
if(!window.blockInProgress) {
block();
} else {
window.flashReady = block;
}
This code makes sense you want to prevent block from being called multiple times. But if you don't care, or the "block" is synchronous, you shouldn't worry. If you're worried about that a global variable value can change when you're checking it, you shouldn't be worried, it's guaranteed to not change unless you call some asynchronous function.
A more practical example. Consider we want to cache AJAX requests.
fetchCached(params) {
if(!dataInCache()) {
return fetch(params).then(data => putToCache(data));
} else {
return getFromCache();
}
}
So happens if we call this code multiple times? We don't know which data will return first, so we don't know which data will be cached. The first 2 times it will return fresh data, but the 3rd time we don't know the shape of response to be returned.
Yes, of course there are race conditions in Javascript. It is based on the event loop model and hence exhibits race conditions for async computations. The following program will either log 10 or 16 depending on whether incHead or sqrHead is completed first:
const rand = () => Math.round(Math.random() * 100);
const incHead = xs => new Promise((res, rej) =>
setTimeout(ys => {
ys[0] = ys[0] + 1;
res(ys);
}, rand(), xs));
const sqrHead = xs => new Promise((res, rej) =>
setTimeout(ys => {
ys[0] = ys[0] * ys[0];
res(ys);
}, rand(), xs))
const state = [3];
const foo = incHead(state);
const bar = sqrHead(state);
Promise.all([foo, bar])
.then(_ => console.log(state));

Node.js and Mutexes

I'm wondering if mutexes/locks are required for data access within Node.js. For example, lets say I've created a simple server. The server provides a couple protocol methods to add to and remove from an internal array. Do I need to protect the internal array with some type of mutex?
I understand Javascript (and thus Node.js) is single threaded. I'm just not clear on how events are handled. Do events interrupt? If that is the case, my app could be in the middle of reading the array, get interrupted to run an event callback which changes the array, and then continue processing the array which has now been changed by the event callback.
Locks and mutexes are indeed necessary sometimes, even if Node.js is single-threaded.
Suppose you have two files that must have the same content and not having the same content is considered an inconsistent state. Now suppose you need to change them without blocking the server. If you do this:
fs.writeFile('file1', 'content', function (error) {
if (error) {
// ...
} else {
fs.writeFile('file2', 'content', function (error) {
if (error) {
// ...
} else {
// ready to continue
}
});
}
});
you fall in an inconsistent state between the two calls, when another function in the same script may be able to read the two files.
The rwlock module is perfect to handle these cases.
I'm wondering if mutexes/locks are required for data access within Node.js.
Nope! Events are handled the moment there's no other code to run, this means there will be no contention, as only the currently running code has access to that internal array. As a side-effect of node being single-threaded, long computations will block all other events until the computation is done.
I understand Javascript (and thus Node.js) is single threaded. I'm just not clear on how events are handled. Do events interrupt?
Nope, events are not interrupted. For example, if you put a while(true){} into your code, it would stop any other code from being executed, because there is always another iteration of the loop to be run.
If you have a long-running computation, it is a good idea to use process.nextTick, as this will allow it to be run when nothing else is running (I'm fuzzy on this: the example below shows that I'm probably right about it running uninterrupted, probably).
If you have any other questions, feel free to stop into #node.js and ask questions. Also, I asked a couple people to look at this and make sure I'm not totally wrong ;)
var count = 0;
var numIterations = 100;
while(numIterations--) {
process.nextTick(function() {
count = count + 1;
});
}
setTimeout(function() {
console.log(count);
}, 2);
//
//=> 100
//
Thanks to AAA_awright of #node.js :)
I was looking for solution for node mutexes. Mutexes are sometimes necessary - you could be running multiple instances of your node application and may want to assure that only one of them is doing some particular thing. All solutions I could find were either not cross-process or depending on redis.
So I made my own solution using file locks: https://github.com/Perennials/mutex-node
Mutexes are definitely necessary for a lot of back end implementations. Consider a class where you need to maintain synchronicity of async execution by constructing a promise chain.
let _ = new WeakMap();
class Foobar {
constructor() {
_.set(this, { pc : Promise.resolve() } );
}
doSomething(x) {
return new Promise( (resolve,reject) => {
_.get(this).pc = _.get(this).pc.then( () => {
y = some value gotten asynchronously
resolve(y);
})
})
}
}
How can you be sure that a promise is not left dangling via race condition? It's frustrating that node hasn't made mutexes native since javascript is so inherently asynchronous and bringing third party modules into the process space is always a security risk.

javascript Web worker - pass data to page thread

If I have a var on my main page, and have a worker thread trying to set this var, is there a way the page can access it? Assuming everything is synchronized?
var routeWorker = new Worker('getroute.js');
var checkPatrolRouteFoundTimer;
var rw_resultRoute;
var routeFound = false;
routeWorker.onmessage = function(e) {
rw_resultRoute = e.data.route;
routeFound = true;
}
function checkPatrolReady() {
if(!routeFound)
checkPatrolRouteFoundTimer = setTimeout("checkPatrolReady()", 1000);
}
function ForcePatrol(index) {
routeWorker.postMessage(index);
checkPatrolReady();
...
//do work on route
...
}
in this case, the var I'm talking about is rw_resultRoute, and I can see it get set correctly when debugging. But the only thing is that it's set in the worker thread, not in the page thread.
I flow through the ForcePatrol() method the way i'm expecting to, and it looks like the rw_resultRoute is being set, since routeFound evaluates to true after the worker finishes.
Technically, it doesn't make sense, since routeFound can be set by the worker and read by the page thread, but rw_resultRoute can only be accessed by the worker.
I truly hope this is possible, otherwise I don't see a purpose for worker threads other than showing alert() messages and updating page HTML.
I truly hope this is possible, otherwise I don't see a purpose for worker threads other than showing alert() messages and updating page HTML.
It is meant to handle processing that would normally lock up the browser. Great for crunching numbers for canvas and running hashing.
in this case, the var I'm talking about is rw_resultRoute, and I can see it get set correctly when debugging. But the only thing is that it's set in the worker thread, not in the page thread.
The worker is separate from the page that spawns it. Only way to pass data is through messaging. You need to send the data with postMessage and have the onMessage handle the result. If you are handling different things, set up a switch statement to handle the different message types.
I solved the problem. There was some synchronization I wasn't doing correctly. I was using the setTimeout in the wrong way.
var routeWorker = new Worker('getroute.js');
var checkPatrolRouteFoundTimer;
var rw_resultRoute;
var routeFound = false;
routeWorker.onmessage = function(e) {
rw_resultRoute = e.data.route;
routeFound = true;
}
function checkPatrolReady() {
if(routeFound) {
...
//do work on route
...
clearInterval(checkPatrolRouteFoundTimer);
} else {
// do any maint here?
}
}
function ForcePatrol(index) {
routeWorker.postMessage(index);
checkPatrolRouteFoundTimer = setInterval("checkPatrolReady()", 1000);
}
Any call to setTimeout/setInterval will flow through, and in the first example i was using setTimeout instead of setInterval.
In the new way, calling ForcePatrol will setup the timer, and checkPatrolReady() will evaluate the flag, doing the work and clearing the timer if it is true.
So there is indeed nothing fancy in getting the results from web workers, but I was essentially creating a race condition with the worker results.

Categories