From bc5760eee9c9f858a54fab1bae82e72cad4df71a Mon Sep 17 00:00:00 2001 From: unrealdragon Date: Wed, 21 Oct 2026 21:59:19 +0530 Subject: [PATCH] Fixes for flaws found by gemini 3.8 on 21/10/26. --- appinfo.cpp | 34 ++++++++------ executionmanager.cpp | 103 +++++++++++++++++++++++-------------------- executionmanager.h | 7 ++- main.cpp | 47 +++++++++++--------- 4 files changed, 106 insertions(+), 85 deletions(-) diff --git a/appinfo.cpp b/appinfo.cpp index 58a1cf3..286a928 100644 --- a/appinfo.cpp +++ b/appinfo.cpp @@ -126,9 +126,10 @@ std::string AppInfo::getAfterString() const for(auto &[key,value]: after) { ss << key << "[" << value << "], "; } - std::string after = ss.str(); - after = after.substr(0,after.size() - 2); - return after; + std::string after_str = ss.str(); + if(after_str.size() >= 2) + after_str = after_str.substr(0, after_str.size() - 2); + return after_str; } std::string AppInfo::getAfterKeys() const @@ -137,9 +138,10 @@ std::string AppInfo::getAfterKeys() const for(auto &[key,value]: after) { ss << key << ", "; } - std::string after = ss.str(); - after = after.substr(0,after.size() - 2); - return after; + std::string after_str = ss.str(); + if(after_str.size() >= 2) + after_str = after_str.substr(0, after_str.size() - 2); + return after_str; } std::string AppInfo::getStringRunMode() const @@ -343,6 +345,7 @@ bool AppInfo::checkValidity() void AppInfo::updateNextTimeToRun() { + next_time_to_run_parse_failed = false; bool got_run_every = false; bool got_run_at = false; std::chrono::system_clock::time_point every_time_point, at_time_point; @@ -513,9 +516,10 @@ std::optional AppInfo::parseAtTimeFormat( } auto local_next = std::chrono::local_days{ymd} + time_of_day; if (local_next <= local_now) { - std::chrono::year_month_day ymd_next = (current_ymd.year() / current_ymd.month() / target_day) + std::chrono::months(1); + auto next_ym = current_ymd.year() / current_ymd.month() + std::chrono::months(1); + std::chrono::year_month_day ymd_next = next_ym / target_day; if (!ymd_next.ok()) { - ymd_next = ymd_next.year() / ymd_next.month() / std::chrono::last; + ymd_next = next_ym / std::chrono::last; } local_next = std::chrono::local_days{ymd_next} + time_of_day; } @@ -781,9 +785,10 @@ std::string AppInfo::AppData::getAfterString() const for(auto &[key,value]: after) { ss << key << "[" << value << "], "; } - std::string after = ss.str(); - after = after.substr(0,after.size() - 2); - return after; + std::string after_str = ss.str(); + if(after_str.size() >= 2) + after_str = after_str.substr(0, after_str.size() - 2); + return after_str; } std::string AppInfo::AppData::getAfterKeys() const @@ -792,7 +797,8 @@ std::string AppInfo::AppData::getAfterKeys() const for(auto &[key,value]: after) { ss << key << ", "; } - std::string after = ss.str(); - after = after.substr(0,after.size() - 2); - return after; + std::string after_str = ss.str(); + if(after_str.size() >= 2) + after_str = after_str.substr(0, after_str.size() - 2); + return after_str; } diff --git a/executionmanager.cpp b/executionmanager.cpp index 1b7af67..ab3f672 100644 --- a/executionmanager.cpp +++ b/executionmanager.cpp @@ -58,45 +58,56 @@ void ExecutionManager::printActiveJobs() void ExecutionManager::run() { finished = false; - initial_list_size = job_list.size(); seperateScheduledTasks(); log_file << "Main Thread: Job list: " << job_list.size() << " Scheduled list: " << scheduled_list.size() << std::endl; last_poll_time = std::chrono::system_clock::now(); while(true) { + // Clean up completed worker threads + for(auto it = thread_list.begin(); it != thread_list.end(); ) { + if((*it)->done.load()) { + if((*it)->thread.joinable()) + (*it)->thread.join(); + it = thread_list.erase(it); + } else { + ++it; + } + } + pollScheduledTasks(); - for(int i=active_thread_count.load(); i < threads ;i++) { + + // Dispatch jobs from job_list to worker threads + while(active_thread_count.load() < threads) { + std::shared_ptr ptr; int val = 0; + size_t remaining_size = 0; { std::lock_guard lock_get(this->job_list_mutex); - val = index_count++; if(job_list.empty()) { - log_file << "Main Thread: Job list is empty, waiting for finish." << std::endl; - break; - } - if(val > (initial_list_size - 1)) { break; } + ptr = job_list.front(); + job_list.erase(job_list.begin()); + remaining_size = job_list.size(); + val = index_count++; } + active_thread_count.fetch_add(1); - std::shared_ptr thread = std::make_shared([this, val](){ - std::shared_ptr ptr; - std::osyncstream os(log_file.rdbuf()); - size_t list_size = 0; - { - std::lock_guard lock_get(this->job_list_mutex); - if(job_list.size() == 0) { - os << std::vformat("Thread[{}] called while the job list was empty: ", std::make_format_args(val)) - << std::endl; - return; + auto worker = std::make_shared(); + worker->thread = std::thread([this, val, ptr, remaining_size, worker](){ + struct ThreadGuard { + std::atomic& count; + std::atomic& done; + ~ThreadGuard() { + count.fetch_sub(1); + done.store(true); } - ptr = this->job_list[0]; - job_list.erase(job_list.begin()); - list_size = job_list.size(); - } + } guard{active_thread_count, worker->done}; + + std::osyncstream os(log_file.rdbuf()); std::string module_name = ptr->getModuleName(); os << std::vformat("Thread[{}] running: ", std::make_format_args(val)) - << module_name << " current list size: " << list_size << std::endl; + << module_name << " current list size: " << remaining_size << std::endl; int old = state_change.load(); while(!ptr->isParentDependencyCleared(os)) { @@ -220,25 +231,26 @@ void ExecutionManager::run() state_change.notify_all(); os << std::vformat("Thread[{}] finished: ", std::make_format_args(val)) << module_name << std::endl; - active_thread_count.fetch_sub(1); }); - thread_list.push_back(thread); + thread_list.push_back(worker); } + + // Check if all work is done (no active jobs, no pending jobs, no scheduled tasks) { std::lock_guard lock_get(this->job_list_mutex); - if(job_list.empty()) { - log_file << "Main Thread: Job list is empty, waiting for finish." << std::endl; + if(job_list.empty() && scheduled_list.empty() && active_thread_count.load() == 0) { + log_file << "Main Thread: All jobs finished." << std::endl; break; } } - if (active_thread_count.load() >= threads) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); } - for(auto i: thread_list) { - if(i->joinable()) - i->join(); + + for(auto &worker: thread_list) { + if(worker->thread.joinable()) + worker->thread.join(); } + thread_list.clear(); finished = true; } @@ -249,16 +261,16 @@ bool ExecutionManager::checkScheduledTaskIsReady(std::chrono::system_clock::time void ExecutionManager::seperateScheduledTasks() { - for(auto task = job_list.begin(); task != job_list.end(); task++) { + std::lock_guard lock_get(this->job_list_mutex); + for(auto task = job_list.begin(); task != job_list.end(); ) { // If task is not running on start and task is scheduled. if(!((*task)->getRunOnStart()) && ((*task)->getScheduled())) { log_file << "Main Thread: Job[" + (*task)->getModuleName() + "] is scheduled for execution." << std::endl; - scheduled_list.push_back(std::move(*task)); + scheduled_list.push_back(*task); task = job_list.erase(task); - if(task == job_list.end()) - break; - task--; + } else { + ++task; } } } @@ -266,23 +278,20 @@ void ExecutionManager::seperateScheduledTasks() void ExecutionManager::pollScheduledTasks() { auto current_poll_time = std::chrono::system_clock::now(); - if(std::chrono::duration_cast(current_poll_time - last_poll_time).count() <= 1) + if(std::chrono::duration_cast(current_poll_time - last_poll_time).count() < polling_interval) return; last_poll_time = current_poll_time; { std::lock_guard lock_get(this->job_list_mutex); - log_file << "Main Thread: Polling before: Job list: " << job_list.size() << - " Scheduled list: " << scheduled_list.size() << std::endl; - for(auto task = scheduled_list.begin(); task != scheduled_list.end(); task++) { + for(auto task = scheduled_list.begin(); task != scheduled_list.end(); ) { if(checkScheduledTaskIsReady((*task)->getNextTimeToRun())) { - job_list.push_back(std::move(*task)); + log_file << "Main Thread: Job[" + (*task)->getModuleName() + + "] scheduled time arrived, adding to execution queue." << std::endl; + job_list.push_back(*task); task = scheduled_list.erase(task); - if(task == scheduled_list.end()) - break; - task--; + } else { + ++task; } } - log_file << "Main Thread: Polling after: Job list: " << job_list.size() << - " Scheduled list: " << scheduled_list.size() << std::endl; } } diff --git a/executionmanager.h b/executionmanager.h index 2aee581..c90b905 100644 --- a/executionmanager.h +++ b/executionmanager.h @@ -10,16 +10,19 @@ class ExecutionManager { + struct WorkerThread { + std::thread thread; + std::atomic done{false}; + }; bool finished = false; int threads; int index_count = 0; - int initial_list_size = 0; long long int log_file_max_size = 0; std::string log_dir; std::mutex job_list_mutex; std::atomic state_change = 0; std::atomic active_thread_count = 0; - std::vector> thread_list; + std::vector> thread_list; std::vector> job_list; std::vector> scheduled_list; std::chrono::system_clock::time_point last_poll_time; diff --git a/main.cpp b/main.cpp index 0dd99d8..8caf0c4 100644 --- a/main.cpp +++ b/main.cpp @@ -109,7 +109,7 @@ void printDefaults() { std::cout << "StartUpRunner Default Values: " << std::endl; std::cout << "Json file : " << options.json_file_path << std::endl; std::cout << "Logs directory : " << options.log_directory << std::endl; - std::cout << "Log file max size: " << options.threads << std::endl; + std::cout << "Log file max size: " << options.log_file_max_size << std::endl; std::cout << "Threads : " << options.threads << std::endl; } @@ -189,7 +189,7 @@ Example startup_apps.json: { "name": "LS 2", "app_name": "/bin/ls", - "app_arpgs": ["-lh"], + "app_args": ["-lh"], "run_mode": "script", "create_log": true, "after": [{"module": "LS 0", "state": "exited"}], @@ -246,7 +246,7 @@ void parseArgs(int c, char *v[]) { int i = contains(arg_list, "--json", "-j"); if(i >= 0) { int j = i + 1; - if(j > arg_list.size()) { + if(j >= arg_list.size()) { printHelp(); exit(-1); } @@ -257,7 +257,7 @@ void parseArgs(int c, char *v[]) { i = contains(arg_list, "--log_dir", "-ld"); if(i >= 0) { int j = i + 1; - if(j > arg_list.size()) { + if(j >= arg_list.size()) { printHelp(); exit(-1); } @@ -268,7 +268,7 @@ void parseArgs(int c, char *v[]) { i = contains(arg_list, "--threads", "-t"); if(i >= 0) { int j = i + 1; - if(j > arg_list.size()) { + if(j >= arg_list.size()) { printHelp(); exit(-1); } @@ -278,29 +278,24 @@ void parseArgs(int c, char *v[]) { } i = contains(arg_list, "--test", "-T"); if(i >= 0) { - int j = i + 1; - if(j > arg_list.size()) { - printHelp(); - exit(-1); - } options.check_for_errors = true; + int j = i + 1; + if(j < arg_list.size() && arg_list[j].rfind("-", 0) != 0) { + options.json_file_path = arg_list[j]; + } std::cout << "Testing json file." << std::endl; contains_any_known_arg = true; } i = contains(arg_list, "--help", "-h"); if(i >= 0) { int j = i + 1; - if(j > arg_list.size()) { - printHelp(); - exit(-1); - } - if(arg_list[j] == "json") { + if(j < arg_list.size() && arg_list[j] == "json") { printJsonHelp(); exit(0); } else { printHelp(); - exit(-1); + exit(0); } contains_any_known_arg = true; } @@ -339,11 +334,14 @@ std::vector string_split(std::string s, const std::string& delimite size_t pos = 0; std::string token; while ((pos = s.find(delimiter)) != std::string::npos) { - token = s.substr(0, pos); - tokens.push_back(token); + token = string_strip(s.substr(0, pos)); + if(!token.empty()) + tokens.push_back(token); s.erase(0, pos + delimiter.length()); } - tokens.push_back(string_strip(s)); + std::string last = string_strip(s); + if(!last.empty()) + tokens.push_back(last); return tokens; } @@ -436,7 +434,7 @@ std::optional checkAppList(std::deque ©_list, std::ve } } if(!found) { - out << "Depenedency not found" << std::endl; + out << "Dependency not found" << std::endl; } } after_list.push_back(std::move(app_info)); @@ -463,19 +461,21 @@ std::optional checkAppList(std::deque ©_list, std::ve } } } + bool all_valid = true; for(auto &i: after_list) { bool run_validity = i->checkValidity(); i->printInfo(out); for(auto &[j, state]: i->getParents()) { if(state == AppInfo::State::exited && j->getRunMode() == AppInfo::RunMode::daemon) { out << "[WARN]: " << i->getModuleName() << - " is waiting for a daemon: " << j->getModuleName() << " to start exeuction." << + " is waiting for a daemon: " << j->getModuleName() << " to start execution." << std::endl; } } if(!run_validity) { out << "[ERR]: " << i->getModuleName() << " incorrect format for scheduled run. Please see help page." << std::endl; + all_valid = false; } else if((i->getScheduled()) && (!i->getRunOnStart())) { const auto* zone = std::chrono::current_zone(); auto local_now = zone->to_local(i->getNextTimeToRun()); @@ -491,9 +491,12 @@ std::optional checkAppList(std::deque ©_list, std::ve } else { out << "Module: " << i->getModuleName() << - " is to be launched on init." << i->getScheduled() << i->getRunOnStart() << run_validity << std::endl; + " is to be launched on init." << std::endl; } } + if(!all_valid) { + return std::nullopt; + } return 0; }