Make ^C kill shm event loops
This is done unconditinally. There are very few times when we don't
want ^C to kill the loop nicely, so just do it until someone complains.
Also, graceful exit is much better than getting terminated and
potentially core dumping.
Change-Id: Ib156ca779c1c5a8d1597c1531a4b12ec14ae0314
diff --git a/aos/events/BUILD b/aos/events/BUILD
index 50a5263..5aef05a 100644
--- a/aos/events/BUILD
+++ b/aos/events/BUILD
@@ -198,6 +198,7 @@
"//aos:realtime",
"//aos/ipc_lib:lockless_queue",
"//aos/ipc_lib:signalfd",
+ "//aos/stl_mutex",
"//aos/util:phased_loop",
],
)
diff --git a/aos/events/ping.cc b/aos/events/ping.cc
index 66c8f12..5a79635 100644
--- a/aos/events/ping.cc
+++ b/aos/events/ping.cc
@@ -7,19 +7,17 @@
#include "glog/logging.h"
int main(int argc, char **argv) {
- FLAGS_logtostderr = true;
- google::InitGoogleLogging(argv[0]);
- ::gflags::ParseCommandLineFlags(&argc, &argv, true);
+ aos::InitGoogle(&argc, &argv);
aos::FlatbufferDetachedBuffer<aos::Configuration> config =
aos::configuration::ReadConfig("aos/events/pingpong_config.json");
- ::aos::ShmEventLoop event_loop(&config.message());
+ aos::ShmEventLoop event_loop(&config.message());
aos::Ping ping(&event_loop);
event_loop.Run();
- ::aos::Cleanup();
+ aos::Cleanup();
return 0;
}
diff --git a/aos/events/shm_event_loop.cc b/aos/events/shm_event_loop.cc
index bd6d37c..f5feea9 100644
--- a/aos/events/shm_event_loop.cc
+++ b/aos/events/shm_event_loop.cc
@@ -17,6 +17,7 @@
#include "aos/ipc_lib/lockless_queue.h"
#include "aos/ipc_lib/signalfd.h"
#include "aos/realtime.h"
+#include "aos/stl_mutex/stl_mutex.h"
#include "aos/util/phased_loop.h"
#include "glog/logging.h"
@@ -526,9 +527,100 @@
}
}
+// RAII class to mask signals.
+class ScopedSignalMask {
+ public:
+ ScopedSignalMask(std::initializer_list<int> signals) {
+ sigset_t sigset;
+ PCHECK(sigemptyset(&sigset) == 0);
+ for (int signal : signals) {
+ PCHECK(sigaddset(&sigset, signal) == 0);
+ }
+
+ PCHECK(sigprocmask(SIG_BLOCK, &sigset, &old_) == 0);
+ }
+
+ ~ScopedSignalMask() { PCHECK(sigprocmask(SIG_SETMASK, &old_, nullptr) == 0); }
+
+ private:
+ sigset_t old_;
+};
+
+// Class to manage the static state associated with killing multiple event
+// loops.
+class SignalHandler {
+ public:
+ // Gets the singleton.
+ static SignalHandler *global() {
+ static SignalHandler loop;
+ return &loop;
+ }
+
+ // Handles the signal with the singleton.
+ static void HandleSignal(int) { global()->DoHandleSignal(); }
+
+ // Registers an event loop to receive Exit() calls.
+ void Register(ShmEventLoop *event_loop) {
+ // Block signals while we have the mutex so we never race with the signal
+ // handler.
+ ScopedSignalMask mask({SIGINT, SIGHUP, SIGTERM});
+ std::unique_lock<stl_mutex> locker(mutex_);
+ if (event_loops_.size() == 0) {
+ // The first caller registers the signal handler.
+ struct sigaction new_action;
+ sigemptyset(&new_action.sa_mask);
+ // This makes it so that 2 control c's to a stuck process will kill it by
+ // restoring the original signal handler.
+ new_action.sa_flags = SA_RESETHAND;
+ new_action.sa_handler = &HandleSignal;
+
+ PCHECK(sigaction(SIGINT, &new_action, &old_action_int_) == 0);
+ PCHECK(sigaction(SIGHUP, &new_action, &old_action_hup_) == 0);
+ PCHECK(sigaction(SIGTERM, &new_action, &old_action_term_) == 0);
+ }
+
+ event_loops_.push_back(event_loop);
+ }
+
+ // Unregisters an event loop to receive Exit() calls.
+ void Unregister(ShmEventLoop *event_loop) {
+ // Block signals while we have the mutex so we never race with the signal
+ // handler.
+ ScopedSignalMask mask({SIGINT, SIGHUP, SIGTERM});
+ std::unique_lock<stl_mutex> locker(mutex_);
+
+ event_loops_.erase(std::find(event_loops_.begin(), event_loops_.end(), event_loop));
+
+ if (event_loops_.size() == 0u) {
+ // The last caller restores the original signal handlers.
+ PCHECK(sigaction(SIGINT, &old_action_int_, nullptr) == 0);
+ PCHECK(sigaction(SIGHUP, &old_action_hup_, nullptr) == 0);
+ PCHECK(sigaction(SIGTERM, &old_action_term_, nullptr) == 0);
+ }
+ }
+
+ private:
+ void DoHandleSignal() {
+ // We block signals while grabbing the lock, so there should never be a
+ // race. Confirm that this is true using trylock.
+ CHECK(mutex_.try_lock()) << ": sigprocmask failed to block signals while "
+ "modifing the event loop list.";
+ for (ShmEventLoop *event_loop : event_loops_) {
+ event_loop->Exit();
+ }
+ mutex_.unlock();
+ }
+
+ // Mutex to protect all state.
+ stl_mutex mutex_;
+ std::vector<ShmEventLoop *> event_loops_;
+ struct sigaction old_action_int_;
+ struct sigaction old_action_hup_;
+ struct sigaction old_action_term_;
+};
+
void ShmEventLoop::Run() {
- // TODO(austin): Automatically register ^C with a sigaction so 2 in a row send
- // an actual control C.
+ SignalHandler::global()->Register(this);
std::unique_ptr<ipc_lib::SignalFd> signalfd;
@@ -590,6 +682,8 @@
epoll_.DeleteFd(signalfd->fd());
signalfd.reset();
}
+
+ SignalHandler::global()->Unregister(this);
}
void ShmEventLoop::Exit() { epoll_.Quit(); }
diff --git a/aos/ipc_lib/shared_mem.cc b/aos/ipc_lib/shared_mem.cc
index 93ced7d..3bb7267 100644
--- a/aos/ipc_lib/shared_mem.cc
+++ b/aos/ipc_lib/shared_mem.cc
@@ -123,16 +123,17 @@
}
void aos_core_free_shared_mem() {
- void *shm_address = global_core->shared_mem;
- PCHECK(munmap((void *)SHM_START, SIZEOFSHMSEG) != -1)
- << ": munmap(" << shm_address << ", 0x" << std::hex
- << (size_t)SIZEOFSHMSEG << ") failed";
- if (global_core->owner) {
- PCHECK(shm_unlink(global_core->shm_name) == 0)
- << ": shared_mem: shm_unlink(" << global_core->shm_name << ") failed";
+ if (global_core != nullptr) {
+ void *shm_address = global_core->shared_mem;
+ PCHECK(munmap((void *)SHM_START, SIZEOFSHMSEG) != -1)
+ << ": munmap(" << shm_address << ", 0x" << std::hex
+ << (size_t)SIZEOFSHMSEG << ") failed";
+ if (global_core->owner) {
+ PCHECK(shm_unlink(global_core->shm_name) == 0)
+ << ": shared_mem: shm_unlink(" << global_core->shm_name << ") failed";
+ }
+ global_core = nullptr;
}
}
-int aos_core_is_init(void) {
- return global_core != NULL;
-}
+int aos_core_is_init(void) { return global_core != NULL; }