The aim of this extension it's to provide the Swiss Tool for Concurrency in ThingWorx.
It publishes standard Java concurrency features in order to be used easily on ThingWorx Server Side Javascript. Also, it may try to solve typical concurrency problems like doing an autoincrement counter.
ThingWorx 7.3 and above. It's set to minimum ThingWorx 6.5 and built with ThingWorx 6.5 SDK but I didn't tested on it.
Import the extension (ConcurrencyExtension.zip) with ThingWorx Composer Import Extension feature.
We implemented first a mutex ThingShape with Java ReentrantLook with fair execution enabled which allows to synchronize and block thread execution. Blocking it's done at Thing's level, it means that each Thing which implements the wupMutexTS ThingShape has it's own ReentrantLook (on a lazy loading way).
We had to change the ThingShape implementation to a ScriptFunction based approach, we left the existing Mutex via ThingShape for the record, but it's recommended to use the ScriptFunction based one, which it's more error prone. With the ThingShape implementation, if Thing Restarts exactly when you where doing the Unlock it crashes and lefts the block Mutex locked forever. You can get an almost error prone version of the ThingShape version whith a try/catch approach:
for(var i=0;i<120;i++) { try { Things[meName].Unlock_wupMutexTS(); break; } catch(err) { pause(1000); } }
but you don't get a clean code, it's slower on the remote case of a restart, and also on an extreme case can fail (after 2 minutes of trying without success).
You just need ot use the Script Functions (Lock_wupMutexTS, Unlock_wupMutexTS, TryLock_wupMutexTS):
In order to lock a piece of code in Javascript, and ensure that only one thread its entering on it at a time for the given Thing:
var meName = me.name;
Lock_wupMutexTS(meName);
try {
// -- whatever code that needs to be mutex
} finally {
Unlock_wupMutexTS(meName);
}
You can also tryLock a piece of code, in order to allow one thread and only one and discard the others. For instance it may be interesting if you have a timer which triggers once in a while and you don't want that two consecutive triggers are executed at the same time:
var meName = me.name;
if (TryLock_wupMutexTS(meName)) {
try {
// -- whatever code that needs to be mutex
} finally {
Unlock_wupMutexTS(meName);
}
} else {
// -- The lock was already got and previous code its skipped
}
You can create more than one mutex per thing, just add the sub-block id after the thing name to have a more quirurgic mutex. Each different passed "id" will create its own ReentrantLook. Sample with previous code but with a specific lock for one specific timer.
var meName = me.name;
if (TryLock_wupMutexTS(meName+"/timer1")===true) {
try {
// -- whatever code that needs to be mutex
} finally {
Unlock_wupMutexTS(meName+"/timer1");
}
} else {
// -- The lock was already got and previous code it's skipped
}
A thread safe "autoincrement" ThingShape. It provides a "counter" property and the corresponding services in order to increase (one by one) it's value.
To increase the counter value, no worries about having any amount of threads incrementing the property value, all will get it's own and unique value:
var newCounterValue = me.Increase_wupCounterTS();
You can reset or reset counter value with Set_wupCounterTS method:
me.Set_wupCounterTS({ value: 0 });
The counter it's stored and update on a persistent property named: counter_wupCounterTS
Just add the wupMutexTS to the Thing or ThingTemplate to whom you want to add mutex blocking features.
On the Mutex Usage samples you will see the usage of copying the me.name in memory? The reason, it's that in between your locking a ThingRestart/ThingDesignSave may happen, and if garbage collector takes place "me" it's not valid anymore as the Thing had restarted and it's another instance in memory and you will get a "zombie" mutex blocking any execution on it!
In order to lock a piece of code in Javascript, and ensure that only one thread its entering on it at a time:
var meName = me.name;
me.Lock_wupMutexTS();
try {
// -- whatever code that needs to be mutex
} finally {
for(var i=0;i<120;i++) {
try {
Things[meName].Unlock_wupMutexTS();
break;
} catch(err) {
pause(1000);
}
}
}
You can also tryLock a piece of code, in order to allow one thread and only one and discard the others. For instance it may be interesting if you have a timer which triggers once in a while and you don't want that two consecutive triggers are executed at the same time:
var meName = me.name;
if (me.TryLock_wupMutexTS()===true) {
try {
// -- whatever code that needs to be mutex
} finally {
for(var i=0;i<120;i++) {
try {
Things[meName].Unlock_wupMutexTS();
break;
} catch(err) {
pause(1000);
}
}
}
} else {
// -- The lock was already got and previous code its skipped
}
You can create more than one mutex per thing, all wupMutexTS services has an optional "id" parameter, which allows to create a more quirurgic mutex. Each different passed "id" will create its own ReentrantLook. Sample with previous code but with a specific lock for one specific timer.
var meName = me.name;
if (me.TryLock_wupMutexTS({ id: "timer1" })===true) {
try {
// -- whatever code that needs to be mutex
} finally {
for(var i=0;i<120;i++) {
try {
Things[meName].Unlock_wupMutexTS({ id: "timer1" });
break;
} catch(err) {
pause(1000);
}
}
}
} else {
// -- The lock was already got and previous code it's skipped
}
A thread safe "autoincrement" ThingShape. It provides a "counter" property and the corresponding services in order to increase (one by one) it's value.
To increase the counter value, no worries about having any amount of threads incrementing the property value, all will get it's own and unique value:
var newCounterValue = me.Increase_wupCounterTS();
You can reset or reset counter value with Set_wupCounterTS method:
me.Set_wupCounterTS({ value: 0 });
The counter it's stored and update on a persistent property named: counter_wupCounterTS
List of script helper functions related with this concurrency extension. This services should go on a subsystem like entity, but subsystems on ThingWorx can't be created through extensions :(
Returns the total active locks, in the whole ThingWorx running system.
Returns the total active threads which are waiting on a lock, in the whole ThingWorx running system.
Returns the total number of mutex created on Things (ReentranLocks), in the whole ThingWorx running system since last start.
Creates/Reuses a mutex with the given id and blocks execution until it gets the mutex. See Mutex Section.
Unlocks a mutex with the given id. See Mutex Section.
Creates/Reuses a mutex with the given id and blocks execution until it gets the mutex or the given timeout happens. See Mutex Section.
Checks if the mutex with the given id it's locked right now.
If you need to build it, it's built with ant and java 8 on a MacOS, the scripts are on the repository. Version change it's done by hand and should be automated.
I've started from the code posted by @antondorf on the ThingWorx Developer Community.