# How protect critical section in node

**URL:** <https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086>\
**Category:** General\
**Created:** [22 May 2021 09:46 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086 "2021-05-22T09:46:14Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 09:46 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/1 "2021-05-22T09:46:14Z")

</div>

I've just started to work with Node-RED and I've found a case where there is a potential critical section that I'd like to control (that's something easy to handle with mutex in low-level languages but I don't know if something like that can be done here).

I'm trying to get some data from a remote web server periodically and I'm using an **inject** node with repetitions to start the sequence (let's say once per minute). However there is a chance that I don't get the right answer and I want to retry, so I've created a sort of loop so that the same round is repeated again as fast as possible instead of having to wait for the next minute check.

In order to prevent both executions to collide, I'm using a variable stored in the global context indicating whether the action is already in process (maybe the periodic line wakes up in the middle of a retry). So both the retry loop and the inject node are connected to a function node where I check this:

```auto
if (!context.global.workInProgress) {
    context.global.workInProgress = true;
    msg.canContinue = true;
}
else {
    msg.canContinue = false;
}

```

After that, the output of the function node is linked to a **switch** node that discards the execution coming from the path which didn't manage to get control of the lock (so to speak), meaning there is an output connector for the message with _msg.canContinue = true_.

Is this enough to handle the critical section? In a pure multithreaded program it wouldn't be and I'd have to protect this code with a mutex, I know. Do I have to add some extra protection code to guarantee the thread-safety of this section?

Oh, and I forgot to mention that I set _context.global.workInProgress = false_ after the operation is finished no matter if the result is correct (end of loop, I use another switch node to control that) or not (retry).

Regards

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [22 May 2021 10:24 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/2 "2021-05-22T10:24:10Z")

</div>

Something like this should do it for you. It uses the subflow describes [here](https://flows.nodered.org/flow/05e6d61f14ef6af763ec4cfd1049ab61). It wasn't designed for exactly that, but I think using it like this should do exactly what you want.

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/4/c/4ca00bab8e75e4eba16102b139d6050e10126d04.png)

Replace the function nodes with your flow and when it succeeds send it to the OK node, and when it fails send it to the FAIL node. When it succeeds the Guaranteed Delivery subflow will wait for the next trigger, when it fails it will retry every second (or as configured in the subflow properties). I have set the queue size limit to 1 so that it won't queue up multiple triggers in the situation where the failure occurs over an extended period.

You can import this to test and play with it.

```auto
[{"id":"149380c1.63e107","type":"subflow","name":"Delivery subflow master copy","info":"","category":"","in":[{"x":60,"y":80,"wires":[{"id":"6a3f78ab.f6b8e"}]}],"out":[{"x":420,"y":80,"wires":[{"id":"6a3f78ab.f6b8e","port":0}]}],"env":[{"name":"controlProperty","type":"str","value":"control","ui":{"label":{"en-US":"Property for OK or FAIL"},"type":"input","opts":{"types":["str","env"]}}},{"name":"OKValue","type":"str","value":"OK","ui":{"label":{"en-US":"Value of success"},"type":"input","opts":{"types":["str","num","bool","env"]}}},{"name":"FAILValue","type":"str","value":"FAIL","ui":{"label":{"en-US":"Value for failure"},"type":"input","opts":{"types":["str","num","bool","env"]}}},{"name":"retrySecs","type":"num","value":"60","ui":{"label":{"en-US":"Retry period (secs)"},"type":"input","opts":{"types":["num","env"]}}},{"name":"maxQueue","type":"num","value":"100","ui":{"label":{"en-US":"Max messages in queue"},"type":"input","opts":{"types":["str","num","env"]}}},{"name":"contextStore","type":"str","value":"default","ui":{"label":{"en-US":"Context Store to use"},"type":"input","opts":{"types":["str","env"]}}}],"color":"#DDAA99","status":{"x":420,"y":160,"wires":[{"id":"ed779289.25b5d8","port":0}]}},{"id":"6a3f78ab.f6b8e","type":"function","z":"149380c1.63e107","name":"State machine","func":"let store = env.get(\"contextStore\")\nif (store === \"default\") store = null\nlet stat = context.get(\"stat\", store) || {state: \"initial\", queue: []}\n// can't use a switch here I think as need to compare against env value\nconst control = msg[env.get(\"controlProperty\")]\n\nif (control === env.get(\"OKValue\")) {\n handleOK(stat)\n} else if (control === env.get(\"FAILValue\")) {\n handleFAIL(stat)\n} else if (control === \"__trigger\") {\n handleTrigger(stat)\n} else {\n // no valid control value so must be incoming message\n handleMessage(msg, stat)\n}\n//node.warn(`state: ${stat.state}`)\n// decide what to do next based on the new state\nswitch (stat.state) {\n case \"initial\":\n case \"waitingForMsg\":\n sendMsg(stat) // send next message if any\n break;\n \n case \"waitingForTrigger\":\n case \"waitingForOKFail\":\n // do nothing\n break;\n}\nnode.status( `${stat.queue.length} ${stat.state}` )\ncontext.set(\"stat\", stat, store)\nreturn null;\n\n// Called when message to be queued is received\nfunction handleMessage(msg, stat) {\n //node.warn(\"handleMessage\")\n // push a clone onto the queue\n stat.queue.push(RED.util.cloneMessage(msg))\n // limit number in queue\n const max = Number(env.get(\"maxQueue\"))\n if (!isNaN(max) && max > 0) {\n // max length hit, remove oldest\n if (stat.queue.length > max) stat.queue.shift()\n }\n // Simulate a trigger event to handle any state change needed\n handleTrigger(stat)\n}\n\n// Called to send the next message off the queue if any, but leaves it on queue\nfunction sendMsg(stat) {\n //node.warn(\"sendMsg\")\n let thisMsg = stat.queue[0]\n if (thisMsg) {\n // send a clone\n //node.warn(\"sending\")\n node.send(RED.util.cloneMessage(thisMsg))\n stat.state = \"waitingForOKFail\"\n } else {\n // nothing in queue\n stat.state = \"waitingForMsg\"\n }\n}\n\n// Called when OK response received\nfunction handleOK(stat) {\n //node.warn(\"handleOK\")\n // ignore if in wrong state\n if (stat.state === \"waitingForOKFail\") {\n // OK received so drop the top message \n stat.queue.shift()\n // set the state to waiting for message, which will allow the next one to be sent\n stat.state = \"waitingForMsg\"\n } else {\n node.warn(\"Ignoring unnexpected OK\")\n }\n}\n\n// Called when FAIL response received\nfunction handleFAIL(stat) {\n //node.warn(\"handleFAIL\")\n // ignore if in wrong state\n if (stat.state === \"waitingForOKFail\") {\n // FAIL received so go to waiting for trigger state\n stat.state = \"waitingForTrigger\"\n } else {\n node.warn(\"Ignoring unnexpected FAIL\")\n }\n}\n\n// Called when a trigger message is received or after a new incoming message is queued\nfunction handleTrigger(stat) {\n //node.warn(\"handleTrigger\")\n if (stat.state === \"waitingForTrigger\") {\n //node.warn(\"state to waiting\")\n // set it to watitingForMsg in order to trigger send \n stat.state = \"waitingForMsg\"\n }\n // ignore for other states\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","x":280,"y":80,"wires":[[]]},{"id":"602725f6.15eee4","type":"inject","z":"149380c1.63e107","name":"Retry ","props":[{"p":"${controlProperty}","v":"__trigger","vt":"str"}],"repeat":"${retrySecs}","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":110,"y":180,"wires":[["6a3f78ab.f6b8e"]]},{"id":"ed779289.25b5d8","type":"status","z":"149380c1.63e107","name":"","scope":["6a3f78ab.f6b8e"],"x":300,"y":160,"wires":[[]]},{"id":"52001913.9013b","type":"change","z":"143d969b.e1a4a9","name":"FAIL","rules":[{"t":"set","p":"control","pt":"msg","to":"FAIL","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":480,"wires":[["b6271c4b.aa1db"]]},{"id":"8c4facb1.1360c8","type":"change","z":"143d969b.e1a4a9","name":"OK","rules":[{"t":"set","p":"control","pt":"msg","to":"OK","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":670,"y":420,"wires":[["b6271c4b.aa1db"]]},{"id":"b6271c4b.aa1db","type":"link out","z":"143d969b.e1a4a9","name":"","links":["643a211d.6b4cb8"],"x":795,"y":420,"wires":[]},{"id":"643a211d.6b4cb8","type":"link in","z":"143d969b.e1a4a9","name":"","links":["b6271c4b.aa1db"],"x":215,"y":360,"wires":[["cbcc3efc.0d91a8"]]},{"id":"cbcc3efc.0d91a8","type":"subflow:149380c1.63e107","z":"143d969b.e1a4a9","name":"Guaranteed delivery","env":[{"name":"retrySecs","value":"1","type":"num"},{"name":"maxQueue","value":"1","type":"num"}],"x":420,"y":300,"wires":[["89ce5ba7.f87fa8"]]},{"id":"5a4269a2.da4008","type":"inject","z":"143d969b.e1a4a9","name":"One Minute repeat","props":[{"p":"payload"}],"repeat":"60","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":160,"y":300,"wires":[["cbcc3efc.0d91a8"]]},{"id":"5544de95.93cde","type":"comment","z":"143d969b.e1a4a9","name":"This is the flow that needs protecting","info":"","x":300,"y":500,"wires":[]},{"id":"89ce5ba7.f87fa8","type":"function","z":"143d969b.e1a4a9","name":"","func":"\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":220,"y":440,"wires":[["6cd065be.a37e84"]]},{"id":"6cd065be.a37e84","type":"function","z":"143d969b.e1a4a9","name":"","func":"\nreturn msg;","outputs":2,"noerr":0,"initialize":"","finalize":"","libs":[],"x":380,"y":440,"wires":[["8c4facb1.1360c8"],["52001913.9013b"]]}]

```

---

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 11:40 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/3 "2021-05-22T11:40:46Z")

</div>

Ok, I've found another solution that might be a direct mapping from the mutex approach I was looking for. Just let me know how it sounds to you.

1. Install external library async-mutex  
In $HOME/.node-red run:  
**sudo npm install async-mutex**

2. Enable external modules in function nodes:

Set _functionExternalModules = true_ in **settings.js**

1. Import the library in the function node in the **Setup** tab, click on **+add** and load it. The configuration of this entry could look like this:  
**const = asyncMutextModule**  
**require = async-mutex**

2. Create the Mutex. This can be stored either in this specific function node or in the global context.

4.1 Store it in the node  
In the **Start** tab of the function node where I'm going to use it create it with:

```auto
myMutex = new asyncMutexModule.Mutex();

```

4.2 Store it in the context  
In the _loadConfig_ path I run at start triggered by an inject node (just one execution), do it inside a function node that will also have the library imported with:

```auto
context.global.myMutex = new asyncMutexModule.Mutex();

```

1. Use it at the original function node in the **On Message** tab with:

Coming from 4.1

```auto
await myMutex.runExclusive(async () => {
    if (!context.global.workInProgress) {
        context.global.workInProgress = true;
        msg.canContinue = true;
    }
    else {
        msg.canContinue = false;
    }
});

```

Coming from 4.2

```auto
await context.global.myMutex.runExclusive(async () => {
    if (!context.global.workInProgress) {
        context.global.workInProgress = true;
        msg.canContinue = true;
    }
    else {
        msg.canContinue = false;
    }
});

```

How does it sound?

Regards

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [22 May 2021 11:52 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/4 "2021-05-22T11:52:07Z")

</div>

Have you seen...

"node-red-contrib-semaphore (node) - Node-RED" [node-red-contrib-semaphore (node) - Node-RED](https://flows.nodered.org/node/node-red-contrib-semaphore)

---

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 11:58 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/5 "2021-05-22T11:58:57Z")

</div>

Oh, that's exactly what I was looking for, a semaphore.

Thx!

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [22 May 2021 12:29 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/6 "2021-05-22T12:29:04Z")

</div>

> [@Steve-Mcl](#):
>
> node-red-contrib-semaphore

I have used that and it does (or did) work. I stopped recommending it as it hasn't been updated in a long time. That may not be an issue, but it makes me nervous.

@Mamonetti is the flow you end up with using the semaphore node simpler than the one I suggested?

---

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 12:40 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/7 "2021-05-22T12:40:26Z")

</div>

I wouldn't say simpler, that's for sure, but I'd say it's more intuitive for people like me who come from the C/C++ world.

Take a look.

 ![semaphore](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/a/7/a715112624ed845635995e65c5de4d9dc8cad9b2.jpeg)

Now I can compact all the **lock + check inProgress + unlock + check canContinue** part in one subflow and the **lock2 + clear inProgress + unlock2** part in another. I think this will simplify the main flow a lot.

Regards

---

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 12:57 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/8 "2021-05-22T12:57:48Z")

</div>

> [@Colin](#):
>
> I have used that and it does (or did) work. I stopped recommending it as it hasn't been updated in a long time. That may not be an issue, but it makes me nervous.

I don't think this has to be an issue at all. Nowadays people are too obsessed with updates and some stuff doesn't need any as its behavior is not expected to change.

Take a look at some low level API of sockets. I've been using documentation coming from 10 to 15 years ago and I know it works perfectly. Why would a semaphore have to work differently 5/10 years later? A semaphore does what it does, nothing more.

Regards

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [22 May 2021 13:36 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/9 "2021-05-22T13:36:22Z")

</div>

> [@Mamonetti](#):
>
> I don't think this has to be an issue at all.

It doesn't has to be no, but it is not uncommon for nodes to stop working following an upgrade to nodejs.

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [22 May 2021 13:47 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/10 "2021-05-22T13:47:16Z")

</div>

> [@Steve-Mcl](#):
>
> node-red-contrib-semaphore

I have just checked it with nodejs 16 so it should be ok for a couple of years at least.

---

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 14:16 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/11 "2021-05-22T14:16:58Z")

</div>

In order to get a more general perspective of node-red, how is the code executed in the background? is it fully multithreaded? if it is, can two threads be running code from the same node at the same time, or a node has a sort of automatic lock that prevents two execution lines to be inside in parallel?

Regards

---

<div class="post-metadata">

**Author:** ![Steve-Mcl](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/steve-mcl/32/4826_2.png) [@Steve-Mcl](https://discourse.nodered.org/u/Steve-Mcl)\
**Post date:** [22 May 2021 14:24 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/12 "2021-05-22T14:24:53Z")

</div>

Node-red runs on node.js which is single threaded. It has an event loop which you can read about [here](https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/)

These copy+paste quotes do some justice to briefly explain what's going on...

> The way it operates and queues the event loop via callbacks can give the illusion of multi-threading but at it's core only one bit of the code written by the developer is being executed at a time.

And...

> The programming model in Node.js is a single threaded event loop with access to asynchronous operations that use native code to implement asynchronous behavior for some operations (disk I/O, networking, timers, some crypto operations, etc...)

---

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 14:35 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/13 "2021-05-22T14:35:39Z")

</div>

It means for my specific case it would be enough by using the global var without having to worry about the critical section based on the code I use to guarantee that only one execution line is in control:

```auto
if (!context.global.workInProgress) {
    context.global.workInProgress = true;
    msg.canContinue = true;
}
else {
    msg.canContinue = false;
}

```

Considering I have no timers, callbacks, etc. in there, that should be enough. I'll thing about what to do because even though right now node.js is single threaded, this might change in the future.

Well, now that I think about it, this might not be true if the execution of the code of a node with no waits such as this one wasn't completed in one go.

Regards

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [22 May 2021 14:43 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/14 "2021-05-22T14:43:58Z")

</div>

In a function node if you `return null` or just `return` then no message is sent, so if you do that you may not need the switch node to discard the message.

---

<div class="post-metadata">

**Author:** ![Mamonetti](https://avatars.discourse-cdn.com/v4/letter/m/c6cbf5/32.png) [@Mamonetti](https://discourse.nodered.org/u/Mamonetti)\
**Post date:** [22 May 2021 14:46 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/15 "2021-05-22T14:46:26Z")

</div>

That's good to know, thx.

Regards

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/1X/d073cd938eafa2e558d7c2cd59003b3ef4963033.png) [@system](https://discourse.nodered.org/u/system)\
**Post date:** [21 July 2021 14:46 UTC](https://discourse.nodered.org/t/how-protect-critical-section-in-node/46086/16 "2021-07-21T14:46:29Z")

</div>

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.
