I don't have any gateway change script that call the function directly. All calls come from a system.util.invokeAsync() and should say 'script-invoke-async'.
I have some gateway tag changes that all call the same functions with system.util.invokeAsync().
To fix the problem of the multiple executions we have a lock saved in system.util.getGlobals(). Each tag change script has it's own unique id.
This mean that the code will do something like this:
lock_id = id # Id passed by param
lock = system.util.getGlobals().get(lock_id)
if not isinstance(lock, threading.Lock):
system.util.getGlobals()[lock_id] = threading.Lock()
lock = system.util.getGlobals().get(lock_id)
if not lock.acquire(False):
return
However, sometimes whenever the code it's trying to enter the function the thread mentioned above it's blocking it.
Also, not sure if it's related yet or not, but this isn't thread safe like you're hoping:
lock_id = id # Id passed by param
lock = system.util.getGlobals().get(lock_id)
if not isinstance(lock, threading.Lock):
system.util.getGlobals()[lock_id] = threading.Lock()
lock = system.util.getGlobals().get(lock_id)
if not lock.acquire(False):
return
Two threads could trivially arrive inside the isinstance check at the same time and assign different lock instances or retrieve different lock instances depending on how execution played out from there.
Consider not using locks, and not using system.util.invokeAsynchronous(). Put all of the tags that would use the same lock into the same list of tags in a gateway tag change event. Make as many gateway tag change events as you need. Each will run with one thread, with the least possible amount of contention. And you will avoid the cost of thread creation.
If you must use locks, you must .setdefault to set it up in globals. Also, always use with to safely acquire and release locks. (You may need RLock instead of Lock, too.)
but it's not, because for some reason globals is a PyStringMap (why????) that does not expose its internal ConcurrentMap(though it looks like this changed in 8.3).
edit: it has getMap(), not sure why it doesn't work
Because it must be compatible with Jython code module globals(). Gaining access to the single, JVM-wide legacy scope globals() is what system.util.getGlobals() was originally created to provide.
Anyways, jython code modules require PyStringMaps as identifier dictionaries. (Both globals() and locals().) Those are keyed with jython identifiers, so other types of immutable keys are not acceptable.
Dropping legacy jython scope makes expanding the signature of .getGlobals() possible.
I normally write instantiation like so:
from threading import RLock
# Fast Path
someLock = system.util.getGlobals().get('someLockId')
if not someLock:
# Slow path. The RLock instance might be thrown away if threads
# are racing
someLock = system.util.getGlobals().setdefault('someLockId', RLock())