gazebosim / gz-common

An audio-visual library supports processing audio and video files, a graphics library can load a variety 3D mesh file formats into a generic in-memory representation, and the core library of Gazebo Common contains functionality that spans Base64 encoding/decoding to thread pools.
https://gazebosim.org
Apache License 2.0
15 stars 39 forks source link

Use self-pipe trick to implement signal handlers #618

Closed azeey closed 3 months ago

azeey commented 4 months ago

🦟 Bug fix

Fixes #530

Summary

This uses the self-pipe trick to implement our signal handling so that downstream users can use any function in a callback registerd to gz::common::SignalHandler.

TODO

Checklist

Note to maintainers: Remember to use Squash-Merge and edit the commit message to match the pull request summary while retaining Signed-off-by messages.

azeey commented 3 months ago

The advantage of a self-pipe is that it should be able to handle multiple signals fired in quick succession right? Maybe we could expand the test to cover the case when multiple signals are fired at the same time and verify the pipe still works fine.

No, the main advantage is that our callbacks can freely use any function they want including printing to the console. Previously, since the callbacks were called in a signal handler, they were limited to using async-signal-safe functions. However, most of our callbacks did not comply with this and so risked causing a deadlock when a signal was fired. For example, malloc or new is not async-signal-safe, but one of the signal handlers in gz-sim emits a Stop event which requires allocating memory. If before the signal was fired, another thread was allocating memory, trying to emit this event inside the signal handler will lock up since it'll internally try to lock a mutex when calling malloc/new. The same is true for using functions like gzdbg to print to the console which is not permitted inside a signal hander.

I could add a test that allocates memory inside a callback, but I don't think the test would fail if we had a regression. For example, if we went back to the previous implementation, the test would still pass most of the time. So I'm not sure if it's worth the effort.

I wonder if the implementation is cross platform (windows..)?

I've added (hopefully) windows support in https://github.com/gazebosim/gz-common/pull/618/commits/258a565ac1d22810cbcc92def29bcd5cdaceded9. I doubt we can use condition variables since none of the pthread_mutex_* or pthread_cond_* functions are async-signal-safe.

iche033 commented 3 months ago

oh ok I was thinking of just a simple test to check if the handler is re-entrant. If it's not worth the effort then we can leave it. The new updates look good to me.

azeey commented 3 months ago

oh ok I was thinking of just a simple test to check if the handler is re-entrant. If it's not worth the effort then we can leave it. The new updates look good to me.

Add a rapid fire test in https://github.com/gazebosim/gz-common/pull/618/commits/a6c47dea6d6696be1b1d23ae72c01d8bf589d025 and https://github.com/gazebosim/gz-common/pull/618/commits/06d0586f40e26d36cf96fe1dbc210294238ddcb4