PKG_CHECK_MODULES(TIZEN_SHARED_QUEUE_DEPS REQUIRED tizen-shared-queue)
PKG_CHECK_MODULES(TTRACE_DEPS REQUIRED ttrace)
PKG_CHECK_MODULES(VCONF_DEPS REQUIRED vconf)
+PKG_CHECK_MODULES(PARCEL_DEPS REQUIRED parcel)
ENABLE_TESTING()
ADD_TEST(NAME ${TARGET_LAUNCHPAD_PROCESS_POOL_UNITTEST}
BuildRequires: pkgconfig(tizen-shared-queue)
BuildRequires: pkgconfig(ttrace)
BuildRequires: pkgconfig(vconf)
+BuildRequires: pkgconfig(parcel)
Requires(post): /sbin/ldconfig
Requires(post): /usr/bin/systemctl
#include <unistd.h>
#include <filesystem>
+#include <utility>
#include <aul_keys.hh>
#include <plugin.hh>
namespace launchpad {
namespace fs = std::filesystem;
+namespace {
-AppExecutor::AppExecutor() : Executor(this) {
+std::shared_ptr<AppPacket> CreateAppPacketFromAppInfo(const AppInfo* app_info) {
+ auto raw = const_cast<tizen_base::Bundle&>(app_info->GetBundle()).ToRaw();
+ std::vector<uint8_t> data(raw.first.get(), raw.first.get() + raw.second);
+ return std::shared_ptr<AppPacket>(new AppPacket(0, 0, std::move(data)));
+}
+
+std::shared_ptr<AppInfo> CreateAppInfoFromAppPacket(
+ const AppPacket* app_packet) {
+ auto& data = app_packet->GetData();
+ std::string raw(data.begin(), data.end());
+ tizen_base::Bundle b(raw);
+ return std::shared_ptr<AppInfo>(AppInfo::Create(std::move(b)));
+}
+
+} // namespace
+
+AppExecutor::AppExecutor()
+ : Executor(this), process_pool_(new ProcessPool(2, this)) {
LauncherInfoInflator inflator;
launcher_infos_ = inflator.Inflate("/usr/share/aul");
}
pid_t AppExecutor::Execute(const AppInfo* app_info) {
+ if (process_pool_->IsPrepared()) {
+ auto app_packet = CreateAppPacketFromAppInfo(app_info);
+ return process_pool_->Execute(std::move(app_packet));
+ }
+
app_info_ = app_info;
return Executor::Execute();
}
}
}
+void AppExecutor::OnRequestReceived(std::shared_ptr<AppPacket> app_packet) {
+ _W("Request received");
+ auto app_info = CreateAppInfoFromAppPacket(app_packet.get());
+ app_info_ = app_info.get();
+ OnExecution();
+}
+
int AppExecutor::Prepare() {
for (auto& func : prepare_funcs_) {
if (func() != 0)
#include <functional>
#include <string>
#include <vector>
+#include <memory>
#include <app_info.hh>
#include "launchpad-process-pool/executor.hh"
#include "launchpad-process-pool/launcher_info.hh"
+#include "launchpad-process-pool/process_pool.hh"
namespace launchpad {
class AppExecutor : public Executor::Delegator,
- public Executor {
+ public Executor,
+ public ProcessPool::IEvent {
public:
AppExecutor();
private:
void OnExecution() override;
+ void OnRequestReceived(std::shared_ptr<AppPacket> app_packet) override;
int Prepare();
int StepCreateNewSession();
private:
using PrepareFunc = std::function<int()>;
+ std::unique_ptr<ProcessPool> process_pool_;
std::vector<LauncherInfoPtr> launcher_infos_;
std::vector<PrepareFunc> prepare_funcs_;
const AppInfo* app_info_ = nullptr;
#include <utility>
+#include <parcel.hh>
+
#include "launchpad-process-pool/log_private.hh"
#include "launchpad-process-pool/signal_manager.hh"
#include "lib/common/inc/launchpad_common.h"
#include "lib/common/inc/launchpad_types.h"
namespace launchpad {
+namespace {
+
+std::shared_ptr<AppPacket> CreateAppPacketFromArgs(
+ const std::vector<std::string>& args) {
+ tizen_base::Bundle b;
+ b.Add("LOADER_ARGS", args);
+ auto raw = b.ToRaw();
+ std::vector<uint8_t> data(raw.first.get(), raw.first.get() + raw.second);
+ return std::shared_ptr<AppPacket>(new AppPacket(0, 0, std::move(data)));
+}
-LoaderExecutor::LoaderExecutor() : Executor(this) {}
+std::vector<std::string> CreateArgsFromAppPacket(const AppPacket* app_packet) {
+ auto& data = app_packet->GetData();
+ std::string raw(data.begin(), data.end());
+ tizen_base::Bundle b(raw);
+ return b.GetStringArray("LOADER_ARGS");
+}
+
+} // namespace
+
+LoaderExecutor::LoaderExecutor()
+ : Executor(this), process_pool_(new ProcessPool(2, this)) {}
LoaderExecutor& LoaderExecutor::GetInst() {
static LoaderExecutor inst;
pid_t LoaderExecutor::Execute(const LoaderContext* loader_context,
int priority) {
- loader_context_ = loader_context;
+ loader_argv_ = CreateLoaderArgv(loader_context);
+ if (process_pool_->IsPrepared()) {
+ auto app_packet = CreateAppPacketFromArgs(loader_argv_);
+ return process_pool_->Execute(std::move(app_packet));
+ }
+
return Executor::Execute(priority);
}
void LoaderExecutor::OnExecution() {
- std::vector<std::string> argv = CreateLoaderArgv();
- char** loader_argv = static_cast<char**>(
- calloc(argv.size() + 1, sizeof(char*)));
- if (loader_argv == nullptr) {
- _E("Out of memory");
- exit(EXIT_FAILURE);
- }
-
- int loader_argc = argv.size();
+ std::vector<char*> loader_argv(loader_argv_.size() + 1);
+ int loader_argc = loader_argv_.size();
for (int i = 0; i < loader_argc; ++i) {
- loader_argv[i] = const_cast<char*>(argv[i].c_str());
+ loader_argv[i] = const_cast<char*>(loader_argv_[i].c_str());
if ((i + 1) != loader_argc)
SECURE_LOGD("loader argument %d : %s##", i, loader_argv[i]);
}
_close_all_fds();
_setup_stdio(basename(loader_argv[LOADER_ARG_PATH]));
- if (execv(loader_argv[LOADER_ARG_PATH], loader_argv) < 0) {
+ if (execv(loader_argv[LOADER_ARG_PATH], loader_argv.data()) < 0) {
char err_buf[1024];
fprintf(stderr, "Failed to execute a file. path: %s, errno: %d:%s\n",
- loader_argv[LOADER_ARG_PATH], errno,
- strerror_r(errno, err_buf, sizeof(err_buf)));
+ loader_argv[LOADER_ARG_PATH], errno,
+ strerror_r(errno, err_buf, sizeof(err_buf)));
exit(EXIT_FAILURE);
}
}
-std::vector<std::string> LoaderExecutor::CreateLoaderArgv() {
+void LoaderExecutor::OnRequestReceived(std::shared_ptr<AppPacket> app_packet) {
+ _W("Request received");
+ loader_argv_ = CreateArgsFromAppPacket(app_packet.get());
+ OnExecution();
+}
+
+std::vector<std::string> LoaderExecutor::CreateLoaderArgv(
+ const LoaderContext* loader_context) {
std::string dummy(LOADER_ARG_LEN - 1, ' ');
std::vector<std::string> argv(LOADER_ARG_DUMMY + 1);
argv[LOADER_ARG_DUMMY] = std::move(dummy);
- argv[LOADER_ARG_PATH] = loader_context_->GetLoaderPath();
- argv[LOADER_ARG_TYPE] = std::to_string(loader_context_->GetType());
- argv[LOADER_ARG_ID] = std::to_string(loader_context_->GetLoaderId());
- argv[LOADER_ARG_HYDRA] = loader_context_->IsHydraMode() ? "1" : "0";
- argv[LOADER_ARG_EXTRA] = loader_context_->GetLoaderExtra();
+ argv[LOADER_ARG_PATH] = loader_context->GetLoaderPath();
+ argv[LOADER_ARG_TYPE] = std::to_string(loader_context->GetType());
+ argv[LOADER_ARG_ID] = std::to_string(loader_context->GetLoaderId());
+ argv[LOADER_ARG_HYDRA] = loader_context->IsHydraMode() ? "1" : "0";
+ argv[LOADER_ARG_EXTRA] = loader_context->GetLoaderExtra();
return argv;
}
#ifndef LAUNCHPAD_PROCESS_POOL_LOADER_EXECUTOR_HH_
#define LAUNCHPAD_PROCESS_POOL_LOADER_EXECUTOR_HH_
+#include <memory>
#include <string>
#include <vector>
#include "launchpad-process-pool/executor.hh"
#include "launchpad-process-pool/loader_context.hh"
+#include "launchpad-process-pool/process_pool.hh"
namespace launchpad {
class LoaderExecutor : public Executor::Delegator,
- public Executor {
+ public Executor,
+ public ProcessPool::IEvent {
public:
LoaderExecutor(const LoaderExecutor&) = delete;
LoaderExecutor& operator=(const LoaderExecutor&) = delete;
~LoaderExecutor() = default;
void OnExecution() override;
- std::vector<std::string> CreateLoaderArgv();
+ void OnRequestReceived(std::shared_ptr<AppPacket> app_packet) override;
+ std::vector<std::string> CreateLoaderArgv(
+ const LoaderContext* loader_context);
private:
- const LoaderContext* loader_context_ = nullptr;
+ std::unique_ptr<ProcessPool> process_pool_;
+ std::vector<std::string> loader_argv_;
};
} // namespace launchpad
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "launchpad-process-pool/process_pool.hh"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <utility>
+
+#include <parcel.hh>
+#include <procfs.hh>
+
+#include "launchpad-process-pool/log_private.hh"
+
+namespace launchpad {
+
+ProcessPool::ProcessPool(int num_processes, IEvent* event_listener = nullptr)
+ : Executor(this),
+ num_processes_(num_processes),
+ event_listener_(event_listener) {
+ PrepareProcess();
+}
+
+ProcessPool::~ProcessPool() {
+ while (!queue_.empty()) {
+ auto process = std::move(queue_.front());
+ queue_.pop();
+ process->Kill();
+ }
+}
+
+bool ProcessPool::IsPrepared() const {
+ return !queue_.empty();
+}
+
+pid_t ProcessPool::Execute(std::shared_ptr<AppPacket> app_packet) {
+ SetTimer();
+ if (!IsPrepared())
+ return -1;
+
+ auto process = std::move(queue_.front());
+ queue_.pop();
+ process->Send(std::move(app_packet));
+ return process->GetPid();
+}
+
+ProcessPool::Process::Process(pid_t pid, int fd)
+ : pid_(pid), socket_(new Socket(fd)) {
+}
+
+pid_t ProcessPool::Process::GetPid() const {
+ return pid_;
+}
+
+int ProcessPool::Process::Send(std::shared_ptr<AppPacket> app_packet) {
+ _W("Send execution request. process ID: %d", pid_);
+ tizen_base::Parcel parcel;
+ app_packet->WriteToParcel(&parcel);
+ return socket_->Write(parcel.GetData(), parcel.GetDataSize());
+}
+
+void ProcessPool::Process::Kill() {
+ socket_->Close();
+ if (kill(pid_, SIGKILL) == -1) {
+ _E("Failed to send kill signal to the process. pid(%d), errno(%d)",
+ pid_, errno);
+ }
+}
+
+void ProcessPool::OnExecution() {
+ _W("Candidate Process");
+ Procfs::SetComm(getpid(), "process-pool+");
+ close(pipe_fd_[1]);
+ int ret = WaitForRequest(std::make_unique<Socket>(pipe_fd_[0]));
+ exit(ret);
+}
+
+void ProcessPool::PrepareProcess() {
+ int current_process_count = static_cast<int>(queue_.size());
+ for (int i = current_process_count; i < num_processes_; ++i) {
+ pipe_fd_[0] = -1;
+ pipe_fd_[1] = -1;
+ if (pipe(pipe_fd_) == -1) {
+ _E("Failed to create pipe. errno(%d)", errno);
+ return;
+ }
+
+ if (fcntl(pipe_fd_[0], F_SETPIPE_SZ, AppPacket::kMaxPacketSize) == -1)
+ _E("Failed to set pipe size. errno(%d)", errno);
+
+ if (fcntl(pipe_fd_[1], F_SETPIPE_SZ, AppPacket::kMaxPacketSize) == -1)
+ _E("Failed to set pipe size. errno(%d)", errno);
+
+ pid_t pid = Executor::Execute();
+ if (pid == -1) {
+ _E("Failed to fork process. errno(%d)", errno);
+ close(pipe_fd_[0]);
+ close(pipe_fd_[1]);
+ return;
+ }
+
+ close(pipe_fd_[0]);
+ queue_.push(std::make_shared<Process>(pid, pipe_fd_[1]));
+ }
+}
+
+int ProcessPool::WaitForRequest(std::unique_ptr<Socket> socket) {
+ auto app_packet = std::make_shared<AppPacket>();
+ int ret = 0;
+ do {
+ char buf[AppPacket::GetHeaderSize()] = { 0, };
+ ret = socket->Read(buf, sizeof(buf));
+ if (ret != 0) {
+ _E("Failed to read from socket. error(%d)", ret);
+ return -1;
+ }
+
+ tizen_base::Parcel parcel(buf, sizeof(buf));
+ app_packet->ReadFromParcel(&parcel);
+
+ std::vector<uint8_t> data;
+ data.resize(app_packet->GetDataSize());
+ ret = socket->Read(data.data(), app_packet->GetDataSize());
+ if (ret != 0) {
+ _E("Failed to read from socket. error(%d)", ret);
+ return -1;
+ }
+
+ parcel.Write(data.data(), data.size());
+ parcel.ResetReader();
+ app_packet->ReadFromParcel(&parcel);
+ } while (ret != 0);
+
+ if (event_listener_ != nullptr)
+ event_listener_->OnRequestReceived(std::move(app_packet));
+
+ return 0;
+}
+
+void ProcessPool::SetTimer() {
+ if (timer_ != 0)
+ return;
+
+ timer_ = g_timeout_add(1000, OnTimeout, this);
+}
+
+gboolean ProcessPool::OnTimeout(gpointer user_data) {
+ auto* process_pool = static_cast<ProcessPool*>(user_data);
+ process_pool->PrepareProcess();
+ process_pool->timer_ = 0;
+ return G_SOURCE_REMOVE;
+}
+
+} // namespace launchpad
+
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an AS IS BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LAUNCHPAD_PROCESS_POOL_PROCESS_POOL_HH_
+#define LAUNCHPAD_PROCESS_POOL_PROCESS_POOL_HH_
+
+#include <glib.h>
+#include <sys/types.h>
+
+#include <queue>
+#include <memory>
+#include <vector>
+
+#include <app_packet.hh>
+#include <socket.hh>
+
+#include "launchpad-process-pool/executor.hh"
+
+namespace launchpad {
+
+class ProcessPool : public Executor::Delegator,
+ public Executor {
+ public:
+ class IEvent {
+ public:
+ virtual ~IEvent() = default;
+ virtual void OnRequestReceived(std::shared_ptr<AppPacket> app_packet) = 0;
+ };
+
+ explicit ProcessPool(int num_processes, IEvent* event_listener);
+ virtual ~ProcessPool();
+
+ bool IsPrepared() const;
+ pid_t Execute(std::shared_ptr<AppPacket> app_packet);
+
+ private:
+ class Process {
+ public:
+ Process(pid_t pid, int fd);
+
+ pid_t GetPid() const;
+ int Send(std::shared_ptr<AppPacket> app_packet);
+ void Kill();
+
+ private:
+ pid_t pid_;
+ std::unique_ptr<Socket> socket_;
+ };
+
+ void OnExecution() override;
+
+ void PrepareProcess();
+ int WaitForRequest(std::unique_ptr<Socket> socket);
+ void SetTimer();
+ static gboolean OnTimeout(gpointer user_data);
+
+ private:
+ int num_processes_;
+ IEvent* event_listener_;
+ int pipe_fd_[2] = { -1, -1 };
+ std::queue<std::shared_ptr<Process>> queue_;
+ guint timer_ = 0;
+};
+
+} // namespace launchpad
+
+#endif // LAUNCHPAD_PROCESS_POOL_PROCESS_POOL_HH_
#include <fstream>
#include <exception.hh>
+#include <procfs.hh>
#include "launchpad-process-pool/log_private.hh"
queue_->Push(std::move(job));
}
-void Worker::SetComm() {
- const std::string path = "/proc/" + std::to_string(gettid()) + "/comm";
- std::ofstream comm_file;
- comm_file.open(path);
- if (!comm_file.is_open()) {
- _E("Failed to open %s", path.c_str());
- return;
- }
-
- comm_file << name_;
- comm_file.close();
-}
-
void Worker::RunThread() {
_W("BEGIN");
- SetComm();
+ Procfs::SetComm(gettid(), name_);
while (true) {
auto job = queue_->WaitAndPop();
job->Do();
void Add(std::shared_ptr<Worker::Job> job);
private:
- void SetComm();
void RunThread();
private:
GIO_DEPS
INIPARSER_DEPS
VCONF_DEPS
+ PARCEL_DEPS
)
TARGET_LINK_LIBRARIES(${TARGET_LAUNCHPAD_COMMON} PUBLIC "-ldl")
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "launchpad-common/app_packet.hh"
+
+#include <utility>
+
+#include <parcel.hh>
+
+namespace launchpad {
+
+AppPacket::AppPacket(int command, int option, std::vector<uint8_t> data)
+ : command_(command),
+ option_(option),
+ data_size_(static_cast<int>(data.size())),
+ data_(std::move(data)) {}
+
+void AppPacket::WriteToParcel(tizen_base::Parcel* parcel) const {
+ parcel->WriteInt32(command_);
+ parcel->WriteInt32(option_);
+ parcel->WriteInt32(data_size_);
+ if (data_size_ > 0)
+ parcel->Write(data_.data(), data_.size());
+}
+
+void AppPacket::ReadFromParcel(tizen_base::Parcel* parcel) {
+ parcel->ReadInt32(&command_);
+ parcel->ReadInt32(&option_);
+ parcel->ReadInt32(&data_size_);
+ if (data_size_ > 0) {
+ data_.resize(data_size_);
+ parcel->Read(data_.data(), data_.size());
+ }
+}
+
+int AppPacket::GetCommand() const {
+ return command_;
+}
+
+int AppPacket::GetOption() const {
+ return option_;
+}
+
+int AppPacket::GetDataSize() const {
+ return data_size_;
+}
+
+const std::vector<uint8_t>& AppPacket::GetData() const {
+ return data_;
+}
+
+} // namespace launchpad
--- /dev/null
+/*
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIB_LAUNCHPAD_COMMON_APP_PACKET_HH_
+#define LIB_LAUNCHPAD_COMMON_APP_PACKET_HH_
+
+#include <cstdint>
+#include <vector>
+
+#include <parcelable.hh>
+
+#undef EXPORT_API
+#define EXPORT_API __attribute__((visibility("default")))
+
+namespace launchpad {
+
+class EXPORT_API AppPacket : public tizen_base::Parcelable {
+ public:
+ AppPacket() = default;
+ AppPacket(int command, int option, std::vector<uint8_t> data);
+ virtual ~AppPacket() = default;
+
+ void WriteToParcel(tizen_base::Parcel* parcel) const override;
+ void ReadFromParcel(tizen_base::Parcel* parcel) override;
+
+ static constexpr size_t GetHeaderSize() {
+ return sizeof(int) + sizeof(int) + sizeof(int);
+ }
+
+ int GetCommand() const;
+ int GetOption() const;
+ int GetDataSize() const;
+ const std::vector<uint8_t>& GetData() const;
+
+ static constexpr int kMaxPacketSize = 131071;
+
+ private:
+ int command_ = 0;
+ int option_ = 0;
+ int data_size_ = 0;
+ std::vector<uint8_t> data_;
+};
+
+} // namespace launchpad
+
+#endif // LIB_LAUNCHPAD_COMMON_APP_PACKET_HH_
Name: liblaunchpad-common
Description: launchpad common library
Version: @VERSION@
-Requires: bundle dlog
+Requires: bundle dlog parcel
Libs: -L${libdir} -llaunchpad-common
Cflags: -I${includedir} -I${includedir}/launchpad-common
return result;
}
+void Procfs::SetComm(pid_t pid, const std::string& comm) {
+ const std::string path = "/proc/" + std::to_string(pid) + "/comm";
+ std::ofstream comm_file;
+ comm_file.open(path);
+ if (!comm_file.is_open()) {
+ _E("Failed to open %s", path.c_str());
+ return;
+ }
+
+ comm_file << comm;
+ comm_file.close();
+}
+
} // namespace launchpad
static void GetMemoryUsage(uint32_t* usage);
static void GetPssMemory(pid_t pid, uint64_t* mem_pss);
static std::string GetAttrCurrent(pid_t pid);
+ static void SetComm(pid_t pid, const std::string& comm);
};
} // namespace launchpad