1 /** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef PLATFORMS_UNIX_LIBPANDABASE_SIGNAL_H 17 #define PLATFORMS_UNIX_LIBPANDABASE_SIGNAL_H 18 19 #include <csignal> 20 #include <functional> 21 #include <thread> 22 #include <condition_variable> 23 #include "libpandabase/macros.h" 24 #include "libpandabase/utils/logger.h" 25 #include "libpandabase/os/thread.h" 26 #include "libpandabase/os/failure_retry.h" 27 28 namespace panda::os::unix { 29 30 class SignalCtl { 31 public: SignalCtl(std::initializer_list<int> signal_list = {})32 SignalCtl(std::initializer_list<int> signal_list = {}) // NOLINT(cppcoreguidelines-pro-type-member-init) 33 { 34 LOG_IF(::sigemptyset(&sigset_) == -1, FATAL, COMMON) << "sigemptyset failed"; 35 for (int sig : signal_list) { 36 Add(sig); 37 } 38 } 39 ~SignalCtl() = default; 40 NO_MOVE_SEMANTIC(SignalCtl); 41 NO_COPY_SEMANTIC(SignalCtl); 42 Add(int sig)43 void Add(int sig) 44 { 45 LOG_IF(::sigaddset(&sigset_, sig) == -1, FATAL, COMMON) << "sigaddset failed"; 46 } 47 Delete(int sig)48 void Delete(int sig) 49 { 50 LOG_IF(::sigdelset(&sigset_, sig) == -1, FATAL, COMMON) << "sigaddset failed"; 51 } 52 IsExist(int sig) const53 bool IsExist(int sig) const 54 { 55 int ret = ::sigismember(&sigset_, sig); 56 LOG_IF(ret == -1, FATAL, COMMON) << "sigismember failed"; 57 return ret == 1; 58 } 59 Block()60 void Block() 61 { 62 LOG_IF(::pthread_sigmask(SIG_BLOCK, &sigset_, nullptr) == -1, FATAL, COMMON) << "pthread_sigmask failed"; 63 } 64 Unblock()65 void Unblock() 66 { 67 LOG_IF(::pthread_sigmask(SIG_UNBLOCK, &sigset_, nullptr) == -1, FATAL, COMMON) << "pthread_sigmask failed"; 68 } 69 Wait() const70 int Wait() const 71 { 72 int sig = 0; 73 LOG_IF(PANDA_FAILURE_RETRY(sigwait(&sigset_, &sig)) == -1, FATAL, COMMON) << "sigwait failed"; 74 return sig; 75 } 76 GetCurrent(SignalCtl &out)77 static void GetCurrent(SignalCtl &out) // NOLINT(google-runtime-references) 78 { 79 LOG_IF(::pthread_sigmask(SIG_SETMASK, nullptr, &out.sigset_) == -1, FATAL, COMMON) << "pthread_sigmask failed"; 80 } 81 82 private: 83 sigset_t sigset_; 84 }; 85 86 class SignalCatcherThread { 87 public: SignalCatcherThread(std::initializer_list<int> signals_list = {SIGUSR1})88 SignalCatcherThread(std::initializer_list<int> signals_list = {SIGUSR1}) : signal_ctl_(signals_list) 89 { 90 ASSERT(signals_list.size() > 0); 91 92 // Use the first signal as the stop catcher thread signal 93 stop_chatcher_thread_signal_ = *signals_list.begin(); 94 } 95 ~SignalCatcherThread() = default; 96 NO_MOVE_SEMANTIC(SignalCatcherThread); 97 NO_COPY_SEMANTIC(SignalCatcherThread); 98 CatchOnlyCatcherThread()99 void CatchOnlyCatcherThread() 100 { 101 ASSERT(catcher_thread_ == 0 && "Use CatchOnlyCatcherThread() before StartThread()"); 102 catch_only_catcher_thread_ = true; 103 } 104 SetupCallbacks(std::function<void()> after_thread_start_callback, std::function<void()> before_thread_stop_callback)105 void SetupCallbacks(std::function<void()> after_thread_start_callback, 106 std::function<void()> before_thread_stop_callback) 107 { 108 after_thread_start_callback_ = std::move(after_thread_start_callback); 109 before_thread_stop_callback_ = std::move(before_thread_stop_callback); 110 } 111 SendSignal(int sig)112 void SendSignal(int sig) 113 { 114 ASSERT(catcher_thread_ != 0); 115 thread::ThreadSendSignal(catcher_thread_, sig); 116 } 117 118 template <typename SigAction, typename... Args> StartThread(SigAction *sig_action, Args... args)119 void StartThread(SigAction *sig_action, Args... args) 120 { 121 ASSERT(catcher_thread_ == 0); 122 ASSERT(!is_running_); 123 124 if (!catch_only_catcher_thread_) { 125 signal_ctl_.Block(); 126 } 127 128 // Start catcher_thread_ 129 catcher_thread_ = thread::ThreadStart(&SignalCatcherThread::Run<SigAction, Args...>, this, sig_action, args...); 130 131 // Wait until the catcher_thread_ is started 132 std::unique_lock<std::mutex> cv_unique_lock(cv_lock_); 133 cv_.wait(cv_unique_lock, [this]() -> bool { return is_running_; }); 134 } 135 StopThread()136 void StopThread() 137 { 138 ASSERT(catcher_thread_ != 0); 139 ASSERT(is_running_); 140 141 // Stop catcher_thread_ 142 is_running_ = false; 143 SendSignal(stop_chatcher_thread_signal_); 144 145 // Wait for catcher_thread_ to finish 146 void **ret_val = nullptr; 147 thread::ThreadJoin(catcher_thread_, ret_val); 148 catcher_thread_ = 0; 149 150 if (!catch_only_catcher_thread_) { 151 signal_ctl_.Unblock(); 152 } 153 } 154 155 private: 156 template <typename SigAction, typename... Args> Run(SignalCatcherThread *self, SigAction *sig_action, Args... args)157 static void Run(SignalCatcherThread *self, SigAction *sig_action, Args... args) 158 { 159 LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: Starting the signal catcher thread"; 160 161 if (self->after_thread_start_callback_ != nullptr) { 162 self->after_thread_start_callback_(); 163 } 164 165 if (self->catch_only_catcher_thread_) { 166 self->signal_ctl_.Block(); 167 } 168 169 { 170 std::lock_guard<std::mutex> lock_guard(self->cv_lock_); 171 self->is_running_ = true; 172 } 173 self->cv_.notify_one(); 174 while (true) { 175 LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: waiting"; 176 177 int sig = self->signal_ctl_.Wait(); 178 if (!self->is_running_) { 179 LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: exit loop, cause signal catcher thread was stopped"; 180 break; 181 } 182 183 LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: signal[" << sig << "] handling begins"; 184 sig_action(sig, args...); 185 LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: signal[" << sig << "] handling ends"; 186 } 187 188 if (self->catch_only_catcher_thread_) { 189 self->signal_ctl_.Unblock(); 190 } 191 192 if (self->before_thread_stop_callback_ != nullptr) { 193 self->before_thread_stop_callback_(); 194 } 195 196 LOG(DEBUG, COMMON) << "SignalCatcherThread::Run: Finishing the signal catcher thread"; 197 } 198 199 std::mutex cv_lock_; 200 std::condition_variable cv_; 201 202 SignalCtl signal_ctl_; 203 thread::native_handle_type catcher_thread_ {0}; 204 int stop_chatcher_thread_signal_ {SIGUSR1}; 205 bool catch_only_catcher_thread_ {false}; 206 std::atomic_bool is_running_ {false}; 207 208 std::function<void()> after_thread_start_callback_ {nullptr}; 209 std::function<void()> before_thread_stop_callback_ {nullptr}; 210 }; 211 212 } // namespace panda::os::unix 213 214 #endif // PLATFORMS_UNIX_LIBPANDABASE_SIGNAL_H 215