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