Crash when simultaneously read and remove attribute in NO_SYNC mode
With growing popularity of async modes in Python we discovering more and more problems with NO_SYNC mode.
Recently we got a complain, that device crashes, when somebody tries to simultaneously read and remove attribute in Asyncio server (which utilizes NO_SYNC mode of cppTango)
Basically what happens: even if in this mode such methods as remove_attribute are still protected, but the read_attribute - not. That means, that MultiAttribute object can be modified during read. Than in random place of Device_3Impl::read_attributes_no_except
it crashes
I think the only way is that we should in NO_SYNC somehow track how many times AutoTangoMonitor was requested, even if do not lock it. So when we enter methods, which force protection - we give then lock only if nobody else requested AutoTangoMonitor with force=false. And vise verse: if somebody forced AutoTangoMonitor in NO_SYNC - nobody else get it even with force=false
I will try to implement it, but not sure if I succeed :-)
This is the code to reproduce in Python
import time
from multiprocessing import Process, Queue
from tango import GreenMode, DeviceProxy
from tango.server import Device, command, attribute
from tango.test_context import DeviceTestContext
def add_delete_attribute(queue):
name = queue.get()
proxy = DeviceProxy(name)
while True:
proxy.add_dyn_attr()
time.sleep(0.5)
proxy.delete_dyn_attr()
def poll(queue):
name = queue.get()
proxy = DeviceProxy(name)
while True:
try:
proxy.read_attribute("dyn_attr")
except:
pass
class TestDevice(Device):
green_mode = GreenMode.Asyncio
@command
def add_dyn_attr(self):
attr = attribute(name="dyn_attr")
self.add_attribute(attr)
@command
def delete_dyn_attr(self):
self.remove_attribute("dyn_attr")
def read_dyn_attr(self, attr):
return 1
if __name__ == '__main__':
q = Queue()
p = Process(target=add_delete_attribute, args=(q,))
p.start()
poller = Process(target=poll, args=(q,))
poller.start()
context = DeviceTestContext(TestDevice, device_name="test/test/1")
with context:
q.put(context.get_device_access())
q.put(context.get_device_access())
p.join()