splintered-reality / py_trees

Python implementation of behaviour trees.
Other
416 stars 140 forks source link

How to call setup() method from non-main thread? #342

Closed JakobDomislovic closed 1 year ago

JakobDomislovic commented 2 years ago

I am doing setup() method (from _pytrees.trees.BehaviourTree()) in non-main thread and I am getting following error: ValueError: signal only works in main thread. How to deal with this without transferring setup() method in the main thread? I am also providing the whole trace below :)

File "/usr/src/***/***/***/behavior_trees/tree.py", line 40, in test_tree
    behaviour_tree.setup(timeout=15.0)
    │              └ <function BehaviourTree.setup at 0x7f59a7feb3a0>
    └ <py_trees.trees.BehaviourTree object at 0x7f59a4068d60>

  File "/usr/local/lib/python3.8/site-packages/py_trees/trees.py", line 324, in setup
    setup(
    └ <function setup at 0x7f59a7fe3280>
  File "/usr/local/lib/python3.8/site-packages/py_trees/trees.py", line 109, in setup
    signal.signal(
    │      └ <function signal at 0x7f59b537e3a0>
    └ <module 'signal' from '/usr/local/lib/python3.8/signal.py'>
  File "/usr/local/lib/python3.8/signal.py", line 47, in signal
    handler = _signal.signal(_enum_to_int(signalnum), _enum_to_int(handler))
              │       │      │            │           │            └ functools.partial(<function setup.<locals>.signal_handler at 0x7f59a40d9ca0>, original_signal_handler=<Handlers.SIG_DFL: 0>)
              │       │      │            │           └ <function _enum_to_int at 0x7f59b537e310>
              │       │      │            └ <Signals.SIGUSR1: 10>
              │       │      └ <function _enum_to_int at 0x7f59b537e310>
              │       └ <built-in function signal>
              └ <module '_signal' (built-in)>

ValueError: signal only works in main thread
stonier commented 2 years ago

You could subclass BehaviourTree and write your own setup method that handles things as you need to. There's actually not much to the setup function, so this should not be too hard. See https://github.com/splintered-reality/py_trees/blob/devel/py_trees/trees.py#L47 - it's just using a threaded timer that relays to a callback that triggers the kill signal.

You could instead replace that with something that logs to the screen (downside: doesn't save you from a setup which hangs - you'll have to kill it from the outside).

Alternatively, if your paradigm permits it, you can use the multiprocessing module. See https://github.com/splintered-reality/py_trees/commit/d7be59dd9944ecbd5ecfb41d8f0fd00fe99daad1 for details.

stonier commented 2 years ago

If you decide on something generic that you think others might make use of, let me know - we can replace, or at least implement an alternative pathway for setup in this library.