Refactor Launchpad Library
[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 <string>
24 #include <vector>
25
26 #include "launchpad/log_private.hh"
27
28 namespace launchpad {
29 namespace {
30
31 const int SIGRTINT = SIGRTMIN + 2;
32
33 std::vector<pid_t> GetTasks() {
34   std::vector<pid_t> tasks;
35   std::filesystem::path task_path("/proc/self/task");
36   try {
37     for (auto& entry : std::filesystem::directory_iterator(task_path)) {
38       if (!std::isdigit(entry.path().filename().c_str()[0]))
39         continue;
40
41       tasks.push_back(std::stoi(entry.path().filename().string()));
42     }
43   } catch (const std::filesystem::filesystem_error& e) {
44     _E("Exception occurs. error: %s", e.what());
45   }
46
47   return tasks;
48 }
49
50 }  // namespace
51
52 ThreadControl& ThreadControl::GetInst() {
53   static ThreadControl inst;
54   return inst;
55 }
56
57 ThreadControl::~ThreadControl() {
58   UnblockThreads();
59 }
60
61 int ThreadControl::BlockThreads() {
62   if (blocked_)
63     return 0;
64
65   if (!ChangeSigaction())
66     return -1;
67
68   InterruptThreads();
69   WaitThreads();
70   blocked_ = true;
71   return 0;
72 }
73
74 int ThreadControl::UnblockThreads() {
75   if (!blocked_)
76     return 0;
77
78   ContinueThreads();
79   WaitThreads();
80   RestoreSigaction();
81   blocked_ = false;
82   return 0;
83 }
84
85 bool ThreadControl::IsBlocked() const {
86   return blocked_;
87 }
88
89 int ThreadControl::GetCount() {
90   return GetTasks().size();
91 }
92
93 bool ThreadControl::ChangeSigaction() {
94   struct sigaction act;
95   memset(&act, '\0', sizeof(struct sigaction));
96   sigemptyset(&act.sa_mask);
97   act.sa_flags = SA_RESTART | SA_SIGINFO;
98   act.sa_handler = SignalHandler;
99
100   if (sigaction(SIGRTINT, &act, &old_action_) != 0) {
101     _E("sigaction() is failed. errno(%d)", errno);
102     return false;
103   }
104
105   return true;
106 }
107
108 void ThreadControl::InterruptThreads() {
109   pid_t pid = getpid();
110   auto tasks = GetTasks();
111   count_ = tasks.size() - 1;
112   _D("tasks count: %d", count_);
113   for (auto& tid : tasks) {
114     if (tid != pid) {
115       _D("Send signal to thread(%d)", tid);
116       if (tgkill(pid, tid, SIGRTINT) != 0) {
117         _E("tgkill() is failed. tid(%d), errno(%d)", tid, errno);
118         count_--;
119       }
120     }
121   }
122 }
123
124 void ThreadControl::ContinueThreads() {
125   std::unique_lock<std::mutex> lock(mutex_);
126   count_ = GetTasks().size() - 1;
127   _D("tasks count: %d", count_);
128   done_ = true;
129   cond_.notify_all();
130 }
131
132 void ThreadControl::WaitThreads() {
133   for (int i = 1000; count_ && i; --i)
134     usleep(2000);
135 }
136
137 void ThreadControl::RestoreSigaction() {
138   if (sigaction(SIGRTINT, &old_action_, nullptr) != 0)
139     _E("sigaction() is failed. errno(%d)", errno);
140 }
141
142 void ThreadControl::SignalHandler(int signo) {
143   _W("Block");
144   auto& inst = ThreadControl::GetInst();
145   std::unique_lock<std::mutex> lock(inst.mutex_);
146   inst.count_--;
147   inst.cond_.wait(lock, [&] { return inst.done_; });
148   inst.count_--;
149   _W("Unblock");
150 }
151
152 }  // namespace launchpad