# Gateway Timer Events vs. Threading?

**URL:** <https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546>\
**Category:** Ignition\
**Created:** [December 9, 2019, 4:48pm UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546 "2019-12-09T16:48:29Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![byrnep](https://avatars.discourse-cdn.com/v4/letter/b/db5fbb/32.png) [@byrnep](https://forum.inductiveautomation.com/u/byrnep)\
**Post date:** [December 9, 2019, 4:48pm UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/1 "2019-12-09T16:48:29Z")

</div>

Hi all,

So having been a little let down by the behavior of Gateway Events in V8.0:

> [@Gateway Timer Event Scripts in 8.0.6](http://forum.inductiveautomation.com/t/gateway-timer-event-scripts-in-8-0-6/30168):
>
> So, we just upgraded from 7.9 to 8.0 and most things are working well. First things first, the online manual has a typo: [https://docs.inductiveautomation.com/display/DOC80/Gateway+Event+Scripts](https://docs.inductiveautomation.com/display/DOC80/Gateway+Event+Scripts) The timer scripts example has a Hello World example that will not compile system.util.getLogger(“TestScript”).info.(“Hello”) should be system.util.getLogger(“TestScript”).info(“Hello”) To get to the more complicated issue, I’m seeing our Gateway timers executing far more rapidly than they should be.…

And lacking a solution for a few weeks I reverted to the following solution to get the 'trigger' property on a Sound Player to toggle on and off: I basically spawn a thread with an enclosed sleep timer, which on initiation sets a property on the SoundPlayer and on conclusion resets it. I have a propertyChanged script which tracks changes in numbers of alarms, as well as the current state of the thread which times out the alarm.

It works as well as the old gateway timer did, and given the divergences (in my opinion) between how Gateway Events work now and how the 8.0 manual says they ought, I'd prefer to leave my solution in place, since I 100% understand how it works and I control how or if its behavior changes. I'm really only looking for serious concerns about possible mismanagement of memory or other resources. This is the first time I've mucked around with threads in Python, and I haven't much experience with them in other languages either. So I can very easily see myself setting up another issue down the road.

Apologies for the unPythonic Grammar and the probably needlessly long code:

```auto
import time
from threading import Thread

def sleepFunc(secs,triggerState):
        #This function waits, then sets two Sound Player properties
	time.sleep(secs)
	event.source.trigger = triggerState
	event.source.ThreadClear = True

if event.propertyName == 'AlarmTotal':
       #if there are Alarms, turn on RingToggle and set the Sound Player on.
       #if there aren't, turn off RingToggle and trigger on the Sound Player
	if event.newValue != 0:
		event.source.RingToggle = True
	else:
		event.source.RingToggle = False
    event.source.trigger = bool(event.source.AlarmTotal)

elif (event.propertyName == 'trigger') or (event.propertyName == 'ThreadClear'):
        #Once 'Ring Toggle' is on the player will cycle 'trigger' on and off till it is removed
        #Each cycle will be spawned as a thread, and a new one will not start till the old one
        #concludes.
	if event.source.ThreadClear:
		event.source.ThreadClear = False
		if ( (event.source.AlarmTotal != 0) and (event.source.trigger) ):
			alarmThread = Thread(target = sleepfunc, args = (0.25,False) )
			alarmThread.start()
		elif ( (event.source.AlarmTotal != 0) and (not event.source.trigger) ):
			alarmThread = Thread(target = sleepfunc, args = (1.75,True) )
			alarmThread.start()
        if event.source.AlarmTotal = 0:
            event.source.trigger = False

```

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [December 9, 2019, 4:59pm UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/2 "2019-12-09T16:59:42Z")

</div>

There’s nothing wrong with using java threads from jython in Ignition, as long as you take care to destroy and recreate them upon script module edits, and take care to only create the number of threads desired.

If the thread will run indefinitely, you need some form of object tracking in the persistent dictionary from system.util.getGlobals(). But:

Since gateway event script behavior hasn’t changed in v8 other than the addition of inheritance, and you had working scripts in v7.9, you probably don’t need to do any this. (Go back and look at Kevin’s comment about how you messed up your global project.)

---

<div class="post-metadata">

**Author:** ![byrnep](https://avatars.discourse-cdn.com/v4/letter/b/db5fbb/32.png) [@byrnep](https://forum.inductiveautomation.com/u/byrnep)\
**Post date:** [December 9, 2019, 9:14pm UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/3 "2019-12-09T21:14:11Z")

</div>

Thanks Phil,

Again, there’s nothing I did to the global project; I neither created it, nor assigned it inheritors, nor gave my gateway events to it. to be run simultaneously by all inheritors. The behavior may be standard, but it was a shift from the behavior in the previous version, one that if it was flagged I didn’t notice it. It also doesn’t seem to be documented in the current 8.0 manual discussion of how gateway events function.

While I’m aware of how to detangle my gateway events from my client events now, under the new 8.0 rubric, I’m leery of such changes to how things work coming through on future updates, and I’d prefer in that case to keep any unexpected behavior due to bugs in my own, self-maintained code. This was a minor issue, but it was a minor issue in our plant’s alarming project, and that’s a larger cause for concern.

I believe my current code, which only permits a thread to be created if the prior thread has terminated successfully (swapping the ThreadComplete flag on the component as last command) should limit myself to one thread at a time. Again I was concerned about committing some obvious ‘newb’ error that would eventually crash my gateway, but it sounds like we should be fine. There’s no indication that memory useage on the gateway has been anything but pancake flat for the last several days. More granular analysis, for windows 10 at least, seems to require installation of a program called ‘process explorer’. I’d like to keep the gateway as bare of extraneous software as possible however.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [December 9, 2019, 9:57pm UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/4 "2019-12-09T21:57:27Z")

</div>

> [@byrnep](#):
>
> process explorer

I don't know if you're aware, but this app is only a few kB 😁 it's also designed as a portable app, so no need to install

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 15, 2025, 12:32am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/5 "2025-05-15T00:32:05Z")

</div>

> [@pturmel](#):
>
> as long as you take care to destroy and recreate them upon script module edits

Hi Phil, is the reason for this simply in case the code running in the thread has been modified, so it must be destroyed and re-started up again to start running the new definition?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 15, 2025, 12:33am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/6 "2025-05-15T00:33:54Z")

</div>

Big memory leak if you don't clean out threads running old code--the entire interpreter is stuck in memory (and running) until all of its threads die. (And all persistent objects that point at any old code objects are gone, too.)

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 15, 2025, 12:50am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/7 "2025-05-15T00:50:43Z")

</div>

Ah, not good!

Although now I have more questions...  
In this example here:

> [@Managing multiple asynchronous threads](https://forum.inductiveautomation.com/t/managing-multiple-asynchronous-threads/37185/6):
>
> No, more like this: persist = system.util.persistent("someUniqueConstantName") oldFileWatcher = persist.pop("fw") persist['fw'] = FileWatcher(oldFileWatcher) def getWatcher(): return persist['fw'] Note the oldFileWatcher passed to the new one so the new one’s thread can tell it to shut down.

i.e.

```python
persist = system.util.persistent("someUniqueConstantName")
oldFileWatcher = persist.pop("fw")
persist['fw'] = FileWatcher(oldFileWatcher)

def getWatcher():
    return persist['fw']

```

My understanding from this is that when the script engine starts up again after saving the project, the new engine gets the old engine's file watcher object in order to interrupt it, kill it, and then replace it. What I don't understand though is how two interpretters can be running side-by-side with the ability to talk to each other? i.e. how can "fw" in the new interpretter point to the old interpretter's object? To me, this is like the Designer's running Python code referencing the gateway's running Python code, which I didn't think was possible. e.g. like `system.util.getGlobals('var1')` being accessible as the same object from both Designer and Gateway scopes. I'm obviously missing something fundamental here...

[I think it's time to finally do a course that goes through the nitty gritty of Python...]

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 15, 2025, 12:56am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/8 "2025-05-15T00:56:58Z")

</div>

The two interpreters are running in the same JVM, so not like designer vs. gateway scopes at all.

And jython is fully multi-threaded. Persistent dictionaries like `system.util.getGlobals()` and my alternatives are **java** objects, sharable across interpreters.

If you never start your own asynchronous threads, and never put things in persistent dictionaries, and never add listeners to gateway scope infrastructure, and never use infinite loops, interpreter cleanup is assured.

But that is quite the set of handcuffs.

(The nitty gritty details of python won't help--jython is a separate implementation.)

I should mention that when java infrastructure calls a jython code object that is implementing a java interface, the calling thread will adopt that code's declaring intepreter and drive on. If a new interpreter calls an old code object through persistence, then old code will run in the new interpreter and things get _ **weird** _.

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 15, 2025, 1:22am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/9 "2025-05-15T01:22:29Z")

</div>

Ok, that makes a _lot_ more sense now, thanks!!

Actually one more thing. What are the issues with `system.util.getGlobals()`?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 15, 2025, 1:31am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/10 "2025-05-15T01:31:26Z")

</div>

It is actually the same dictionary that holds global names in the legacy-scoped gateway events (all of them together, across all projects). So, lots of opportunities for clashes. To avoid that:

- Make all gateway events one-liners calling project library scripts.

- Use a better persistent object storage tool. \* _Cough_ \*

---

<div class="post-metadata">

**Author:** ![nminchin](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/nminchin/32/6911_2.png) [@nminchin](https://forum.inductiveautomation.com/u/nminchin)\
**Post date:** [May 15, 2025, 4:10am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/11 "2025-05-15T04:10:19Z")

</div>

In your [https://www.automation-pros.com/ignition/queueUtil.py](https://www.automation-pros.com/ignition/queueUtil.py), is there anything here that needs to added to protect from this? (I presume not, but just thought I'd ask).

The `managementMap` in the global dict (queueUtilPersistence) is a dict whose keys contain LinkedBlockingQueue Java objects. The init method on the WrappedBlockingQueue, which adds new LBQ objects into the global dict, first checks if the key already exists, and if it does it just sets the class' `_queue` class variable's value to the existing queue (see arrow in screenshot). This is the part i'm not sure about. Wouldn't this then be using the old interpretter's version? or does it not matter because these are Java objects?

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/5/b/5b38c796273d2887e033bb57e9f321d05217a21e.png)

**Is there any way to tell how many interpretter instances are running?**

> [@pturmel](#):
>
> Use a better persistent object storage tool. \* _Cough_ \*

What does your `system.util.getGlobalVars()` implementation solve/improve that limits getGlobals()? (in layman's terms if possible 😛 )

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [May 15, 2025, 11:30am UTC](https://forum.inductiveautomation.com/t/gateway-timer-events-vs-threading/30546/12 "2025-05-15T11:30:56Z")

</div>

> [@nminchin](#):
>
> tell how many interpretter instances

Not that I know of, at runtime. A heap dump would show something.

> [@nminchin](#):
>
> What does your

1. Independent of legacy scope,

2. Requires an application key string to separate uses.

3. Delivers a java `ConcurrentHashMap` instead of a python dictionary, to expose extra methods. Particularly `.putIfAbsent()` and `.computeIfAbsent()`.
