fix #1: make signal handling async-safe