Today I found a “solution” to this that does not make sense: if I start the softioc (the call to softioc.iotInit()) in the first thread, and not in the main process, everything works as expected.
Our engineers are unable to explain this behavior. I hope there’s someone here on the list that might know why!
Example code that demonstrates the failure:
--------------------
import os
import time
import threading
os.environ['EPICS_CA_SERVER_PORT'] = '5100'
os.environ['EPICS_CA_ADDR_LIST'] = 'localhost:5100'
os.environ['EPICS_CA_AUTO_ADDR_LIST'] = 'NO'
try:
import epicscorelibs.path.pyepics
except ImportError:
pass
import epics
from softioc import softioc, builder
# Create a long record for this demo
long = builder.longOut('prefix:long', initial_value=1)
builder.LoadDatabase()
softioc.iocInit()
def thread1():
while True:
val = long.get()
long.set(val + 1)
print(f'thread1: {val}')
time.sleep(1)
def thread2():
pv = epics.PV('prefix:long')
while True:
val = pv.get()
print(f'thread2: {val}')
time.sleep(1)
t1 = threading.Thread(target=thread1, daemon=True)
t2 = threading.Thread(target=thread2, daemon=True)
t1.start()
t2.start()
time.sleep(10)
Example that works; note the only change is the movement of the .iocInit() line down into thread1:
--------------------
import os
import time
import threading
os.environ['EPICS_CA_SERVER_PORT'] = '5100'
os.environ['EPICS_CA_ADDR_LIST'] = 'localhost:5100'
os.environ['EPICS_CA_AUTO_ADDR_LIST'] = 'NO'
try:
import epicscorelibs.path.pyepics
except ImportError:
pass
import epics
from softioc import softioc, builder
# Create a long record for this demo
long = builder.longOut('prefix:long', initial_value=1)
builder.LoadDatabase()
def thread1():
softioc.iocInit()
while True:
val = long.get()
long.set(val + 1)
print(f'thread1: {val}')
time.sleep(1)
def thread2():
pv = epics.PV('prefix:long')
while True:
val = pv.get()
print(f'thread2: {val}')
time.sleep(1)
t1 = threading.Thread(target=thread1, daemon=True)
t2 = threading.Thread(target=thread2, daemon=True)
t1.start()
t2.start()
time.sleep(10)
The output when it fails:
$ python3 ./demoFailingDaemon.py
Starting iocInit
############################################################################
## EPICS 7.0.6.0
## Rev. 7.0.6.99.1.0
############################################################################
iocRun: All initialization complete
thread1: 1
thread1: 2
thread1: 3
thread2: None
thread1: 4
thread1: 5
thread2: None
thread1: 6
thread1: 7
thread1: 8
thread2: None
thread1: 9
thread1: 10
thread1: 11
The output when it works:
$ python3 ./demoWorkingDaemon.py
Starting iocInit
############################################################################
## EPICS 7.0.6.0
## Rev. 7.0.6.99.1.0
############################################################################
iocRun: All initialization complete
thread1: 1
thread2: 2
thread1: 2
thread2: 3
thread1: 3
thread2: 4
thread1: 4
thread2: 5
thread1: 5
thread2: 6
thread1: 6
thread2: 7
thread1: 7
thread2: 8
thread1: 8
thread2: 9
thread1: 9
thread2: 10
thread1: 10
thread2: 11
Paul
|
Paul Richards
Software Engineer
W. M. Keck Observatory
65-1120 Mamalahoa Hwy.
Kamuela, Hawai'i 96743
|
Main: (808) 885-7887
Direct: (808) 881-3537
Fax: (808) 885-3535
|