Skip to content

Fix: join the control thread before closing the CAN socket - #81

Open
tendant wants to merge 1 commit into
i2rt-robotics:mainfrom
tendant:join-control-thread-before-close
Open

Fix: join the control thread before closing the CAN socket#81
tendant wants to merge 1 commit into
i2rt-robotics:mainfrom
tendant:join-control-thread-before-close

Conversation

@tendant

@tendant tendant commented Aug 1, 2026

Copy link
Copy Markdown

DMChainCanInterface.close() sets running = False and closes the CAN socket in the same breath, without waiting for the control loop to notice:

def close(self) -> None:
    self.running = False
    self.motor_interface.close()

A thread already inside _set_commands then sends on a socket that has just been closed, and dies on every clean shutdown:

Robot closed with all torques set to zero.
ERROR:root:1th motor at DMChainCanInterface(channel=can0) failed with info [2, 'DM4340']
DM Error in control loop: file descriptor cannot be a negative integer (-1)
Exception in thread Thread-1 (_set_torques_and_update_state):
  File "i2rt/motor_drivers/can_interface.py", line 54, in _send_message_get_response
    self.bus.send(message)
ValueError: file descriptor cannot be a negative integer (-1)

It could not be joined, because start_thread() assigned the handle to a local and dropped it. Keep it, and join it in close() with a timeout so close() stays bounded if the loop is wedged — in that case behaviour is unchanged.

MotorChainRobot.close() already does this for its own thread one level up, so this brings the motor chain in line with the pattern beside it.

Cosmetic: it fires after torques are zeroed, so the arm is safe either way. But it is noisy enough to bury anything else printed around a shutdown, and it reproduces on every disconnect — including a plain connect/read/disconnect with no motion commanded.

DMChainCanInterface.close() sets running = False and closes the CAN socket in
the same breath, without waiting for the control loop to notice:

    def close(self) -> None:
        self.running = False
        self.motor_interface.close()

A thread already inside _set_commands then sends on a socket that has just
been closed, and dies on every clean shutdown:

    Robot closed with all torques set to zero.
    ERROR:root:1th motor at DMChainCanInterface(channel=can0) failed with info [2, 'DM4340']
    DM Error in control loop: file descriptor cannot be a negative integer (-1)
    Exception in thread Thread-1 (_set_torques_and_update_state):
      File "i2rt/motor_drivers/can_interface.py", line 54, in _send_message_get_response
        self.bus.send(message)
    ValueError: file descriptor cannot be a negative integer (-1)

It could not be joined, because start_thread() assigned the handle to a local
and dropped it. Keep it, and join it in close() with a timeout so close()
stays bounded if the loop is wedged — in that case behaviour is unchanged.

MotorChainRobot.close() already does this for its own thread one level up, so
this brings the motor chain in line with the pattern beside it.

Cosmetic: it fires after torques are zeroed, so the arm is safe either way.
But it is noisy enough to bury anything else printed around a shutdown, and it
reproduces on every disconnect — including a plain connect/read/disconnect
with no motion commanded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant