Modify thread control
[platform/core/appfw/launchpad.git] / src / lib / launchpad / thread_control.cc
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
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
17 #include "launchpad/thread_control.hh"
18
19 #include <sys/types.h>
20 #include <unistd.h>
21
22 #include <filesystem>
23 #include <fstream>
24 #include <string>
25 #include <vector>
26
27 #include "launchpad/log_private.hh"
28
29 namespace launchpad {
30 namespace {
31
32 const int SIGRTINT = SIGRTMIN + 2;
33
34 std::vector<pid_t> GetTasks() {
35   std::vector<pid_t> tasks;
36   std::filesystem::path task_path("/proc/self/task");
37   try {
38     for (auto& entry : std::filesystem::directory_iterator(task_path)) {
39       if (!std::isdigit(entry.path().filename().c_str()[0]))
40         continue;
41
42       tasks.push_back(std::stoi(entry.path().filename().string()));
43     }
44   } catch (const std::filesystem::filesystem_error& e) {
45     _E("Exception occurs. error: %s", e.what());
46   }
47
48   return tasks;
49 }
50
51 std::string GetThreadName(pid_t tid) {
52   std::string path = "/proc/" + std::to_string(tid) + "/comm";
53   std::ifstream proc_file(path);
54   if (proc_file.is_open()) {
55     std::string name;
56     std::getline(proc_file, name);
57     proc_file.close();
58     return name;
59   }
60
61   _E("Failed to read name. tid(%d)", tid);
62   return "";
63 }
64
65 int GetThreadCountWithoutGmain(const std::vector<pid_t>& tasks) {
66   int count = tasks.size() - 1;
67   for (auto tid : tasks) {
68     if (getpid() != tid) {
69       // Unfortunately, the signal handler of the gmain thread is not invoked.
70       // The gmain thread always calls poll().
71       // To avoid delay issue of calling usleep(), this function decreases
72       // the count if the gmain threads exists.
73       if (GetThreadName(tid) == "gmain") {
74         _D("%d is gmain thread", tid);
75         count--;
76       }
77     }
78   }
79
80   return count;
81 }
82
83 }  // namespace
84
85 ThreadControl& ThreadControl::GetInst() {
86   static ThreadControl inst;
87   return inst;
88 }
89
90 ThreadControl::~ThreadControl() {
91   UnblockThreads();
92 }
93
94 int ThreadControl::BlockThreads() {
95   if (blocked_)
96     return 0;
97
98   if (!ChangeSigaction())
99     return -1;
100
101   InterruptThreads();
102   WaitThreads();
103   blocked_ = true;
104   return 0;
105 }
106
107 int ThreadControl::UnblockThreads() {
108   if (!blocked_)
109     return 0;
110
111   ContinueThreads();
112   WaitThreads();
113   RestoreSigaction();
114   blocked_ = false;
115   return 0;
116 }
117
118 bool ThreadControl::IsBlocked() const {
119   return blocked_;
120 }
121
122 int ThreadControl::GetCount() {
123   return GetTasks().size();
124 }
125
126 bool ThreadControl::ChangeSigaction() {
127   struct sigaction act;
128   memset(&act, '\0', sizeof(struct sigaction));
129   sigemptyset(&act.sa_mask);
130   act.sa_flags = SA_RESTART | SA_SIGINFO;
131   act.sa_handler = SignalHandler;
132
133   if (sigaction(SIGRTINT, &act, &old_action_) != 0) {
134     _E("sigaction() is failed. errno(%d)", errno);
135     return false;
136   }
137
138   return true;
139 }
140
141 void ThreadControl::InterruptThreads() {
142   pid_t pid = getpid();
143   auto tasks = GetTasks();
144   count_ = GetThreadCountWithoutGmain(tasks);
145   _D("tasks count: %d", count_);
146   for (auto& tid : tasks) {
147     if (tid != pid) {
148       _D("Send signal to thread(%d)", tid);
149       if (tgkill(pid, tid, SIGRTINT) != 0) {
150         _E("tgkill() is failed. tid(%d), errno(%d)", tid, errno);
151         count_--;
152       }
153     }
154   }
155 }
156
157 void ThreadControl::ContinueThreads() {
158   std::unique_lock<std::mutex> lock(mutex_);
159   count_ = GetThreadCountWithoutGmain(GetTasks());
160   _D("tasks count: %d", count_);
161   done_ = true;
162   cond_.notify_all();
163 }
164
165 void ThreadControl::WaitThreads() {
166   for (int i = 1000; count_ && i; --i)
167     usleep(2000);
168 }
169
170 void ThreadControl::RestoreSigaction() {
171   if (sigaction(SIGRTINT, &old_action_, nullptr) != 0)
172     _E("sigaction() is failed. errno(%d)", errno);
173 }
174
175 void ThreadControl::SignalHandler(int signo) {
176   _D("Block");
177   auto& inst = ThreadControl::GetInst();
178   std::unique_lock<std::mutex> lock(inst.mutex_);
179   inst.count_--;
180   if (inst.cond_.wait_for(lock, std::chrono::seconds(5),
181                           [&] { return inst.done_; }))
182     _D("Unblock");
183   else
184     _E("Timed out");
185   inst.count_--;
186 }
187
188 }  // namespace launchpad