allow AsyncSignalHandler to attach and detach from an EventBase
[folly.git] / folly / io / async / AsyncSignalHandler.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <folly/io/async/AsyncSignalHandler.h>
17
18 #include <folly/io/async/EventBase.h>
19
20 #include <folly/Conv.h>
21
22 using std::make_pair;
23 using std::pair;
24 using std::string;
25
26 namespace folly {
27
28 AsyncSignalHandler::AsyncSignalHandler(EventBase* eventBase)
29   : eventBase_(eventBase) {
30 }
31
32 AsyncSignalHandler::~AsyncSignalHandler() {
33   // Unregister any outstanding events
34   for (SignalEventMap::iterator it = signalEvents_.begin();
35        it != signalEvents_.end();
36        ++it) {
37     event_del(&it->second);
38   }
39 }
40
41 void AsyncSignalHandler::attachEventBase(EventBase* eventBase) {
42   assert(eventBase_ == nullptr);
43   assert(signalEvents_.empty());
44   eventBase_ = eventBase;
45 }
46
47 void AsyncSignalHandler::detachEventBase() {
48   assert(eventBase_ != nullptr);
49   assert(signalEvents_.empty());
50   eventBase_ = nullptr;
51 }
52
53 void AsyncSignalHandler::registerSignalHandler(int signum) {
54   pair<SignalEventMap::iterator, bool> ret =
55     signalEvents_.insert(make_pair(signum, event()));
56   if (!ret.second) {
57     // This signal has already been registered
58     throw std::runtime_error(folly::to<string>(
59                                "handler already registered for signal ",
60                                signum));
61   }
62
63   struct event* ev = &(ret.first->second);
64   try {
65     signal_set(ev, signum, libeventCallback, this);
66     if (event_base_set(eventBase_->getLibeventBase(), ev) != 0 ) {
67       throw std::runtime_error(folly::to<string>(
68                                  "error initializing event handler for signal ",
69                                  signum));
70     }
71
72     if (event_add(ev, nullptr) != 0) {
73       throw std::runtime_error(folly::to<string>(
74                                  "error adding event handler for signal ",
75                                  signum));
76     }
77   } catch (...) {
78     signalEvents_.erase(ret.first);
79     throw;
80   }
81 }
82
83 void AsyncSignalHandler::unregisterSignalHandler(int signum) {
84   SignalEventMap::iterator it = signalEvents_.find(signum);
85   if (it == signalEvents_.end()) {
86     throw std::runtime_error(folly::to<string>(
87                                "unable to unregister handler for signal ",
88                                signum, ": signal not registered"));
89   }
90
91   event_del(&it->second);
92   signalEvents_.erase(it);
93 }
94
95 void AsyncSignalHandler::libeventCallback(libevent_fd_t signum,
96                                           short /* events */,
97                                           void* arg) {
98   AsyncSignalHandler* handler = static_cast<AsyncSignalHandler*>(arg);
99   handler->signalReceived(int(signum));
100 }
101
102 } // folly