Falcon
Loading...
Searching...
No Matches
runinfo.hpp
1// ---------------------------------------------------------------------
2// This file is part of falcon-core.
3//
4// Copyright (C) 2015, 2016, 2017 Neuro-Electronics Research Flanders
5//
6// Falcon-server is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10//
11// Falcon-server is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License
17// along with falcon-core. If not, see <http://www.gnu.org/licenses/>.
18// ---------------------------------------------------------------------
19
20#pragma once
21#include <sys/stat.h>
22#include <sys/types.h>
23#include <unistd.h>
24
25#include <atomic>
26#include <chrono>
27#include <condition_variable>
28#include <cstdio>
29#include <ctime>
30#include <map>
31#include <mutex>
32#include <string>
33
34#include "context.hpp"
35#include "logging/log.hpp"
36#include "utilities/time.hpp"
37
38// forward declaration
39namespace graph {
40class ProcessorGraph;
41}
42
43class RunContext : public StorageContext {
44 public:
45 friend class graph::ProcessorGraph;
46 friend class IProcessor;
47
48 public:
49 RunContext(GlobalContext& context, std::atomic<bool>& terminate_signal,
50 std::string run_group_id, std::string run_id, std::string template_id,
51 bool test_flag)
52 : StorageContext(context),
53 global_context_(context),
54 start_time_(Clock::now()),
55 stop_time_(start_time_),
56 terminate_signal_(terminate_signal),
57 template_id_(template_id),
58 default_test_flag_(test_flag) {
59 struct stat info;
60
61 // use default run group id if none given
62 run_group_id_ = run_group_id.empty() ? "default" : run_group_id;
63
64 // add run group storage site
65 add_storage_context("rungroup", storage_context("runroot") + run_group_id_);
66
67 if (!template_id.empty()) {
68 add_storage_context("templatebase", storage_context("rungroup") + "/" + template_id);
69 if ((stat(storage_context("templatebase").c_str(), &info) < 0) ||
70 !(info.st_mode & S_IFDIR)) {
71 throw std::runtime_error(
72 "Run source folder does not exist or is not accessible. (" +
73 storage_context("templatebase") + ")");
74 }
75 }
76
77 // create run group folder
78 if (mkdir(storage_context("rungroup").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) !=
79 0) {
80 if (errno != EEXIST) {
81 throw std::runtime_error("Cannot create run environment " +
82 storage_context("rungroup"));
83 }
84 }
85
86 // generate default destination
87 if (run_id.empty()) {
88 char buffer[20];
89
90 time_t rawtime;
91 struct tm* timeinfo;
92
93 time(&rawtime);
94 timeinfo = localtime(&rawtime);
95
96 strftime(buffer, 20, "%Y%m%d_%H%M%S", timeinfo);
97
98 run_id = buffer;
99 }
100
101 add_storage_context("runbase", storage_context("rungroup") + "/" + run_id);
102 run_id_ = run_id;
103
104 // create run destination folder
105 if (mkdir(storage_context("runbase").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
106 if (errno == EEXIST) {
107 throw std::runtime_error("Run base folder already exists. (" +
108 storage_context("runbase") + ")");
109 } else {
110 throw std::runtime_error("Cannot create run base folder " +
111 storage_context("runbase"));
112 }
113 }
114
115 // create symbolic link rungroup/_last_run pointing to run base folder
116 std::string symlinkname = storage_context("runroot") + "/_last_run";
117 // remove old symlink (if present)
118 std::remove(symlinkname.c_str());
119
120 // create new symlink
121 auto runbase_path = storage_context("runbase");
122
123 try {
124 // 3. Get absolute paths to ensure calculation is accurate
125 fs::path target_abs = fs::absolute(runbase_path);
126 fs::path link_path_abs = fs::absolute(symlinkname);
127
128 // 4. Calculate relative path from the symlink's PARENT directory to the target
129 // Example: if link is in /root/_last_run and target is /root/runs/run1
130 // The relative path should be "runs/run1"
131 fs::path relative_target = fs::relative(target_abs, link_path_abs.parent_path());
132
133 // 5. Create the relative symlink
134 fs::create_directory_symlink(relative_target, link_path_abs);
135
136 } catch (const fs::filesystem_error& e) {
137 LOG(WARNING) << "Could not create symbolic link for last run: " << e.what();
138 }
139
140 // create symbolic link runroot/_last_run_group pointing to run group
141 // folder
142 symlinkname = storage_context("runroot") + "_last_run_group";
143 // remove old symlink (if present)
144 std::remove(symlinkname.c_str());
145 // create new symlink
146 auto rungroup_path = storage_context("rungroup");
147
148 try {
149 // 3. Get absolute paths to ensure calculation is accurate
150 fs::path target_abs = fs::absolute(rungroup_path);
151 fs::path link_path_abs = fs::absolute(symlinkname);
152
153 // 4. Calculate relative path from the symlink's PARENT directory to the target
154 // Example: if link is in /root/_last_run_group and target is /root/runs/group1
155 // The relative path should be "runs/group1"
156 fs::path relative_target = fs::relative(target_abs, link_path_abs.parent_path());
157
158 // 5. Create the relative symlink
159 fs::create_directory_symlink(relative_target, link_path_abs);
160
161 } catch (const fs::filesystem_error& e) {
162 LOG(WARNING) << "Could not create symbolic link for last run group: " << e.what();
163 }
164
165 add_storage_context("lastrunbase", storage_context("rungroup") + "/_last_run");
166 add_storage_context("lastrungroup", storage_context("runroot") + "/_last_run_group");
167
168 set_default_context("runbase");
169 }
170
171 GlobalContext& global() { return global_context_; }
172
173 bool terminated() const { return terminate_signal_.load(); }
174
175 void Terminate() {
176 if (!terminate_signal_.exchange(true)) {
177 stop_time_ = Clock::now();
178 }
179 }
180 void TerminateWithError(std::string processor_name, std::string step,
181 std::string error_message) {
182 if (!terminate_signal_.exchange(true)) {
183 stop_time_ = Clock::now();
184 error_message_ =
185 "Processor `" + processor_name + "` failed in `" + step + "`: " + error_message;
186 }
187 }
188
189 TimePoint start_time() const { return start_time_; }
190 TimePoint stop_time() const {
191 if (terminated()) {
192 return stop_time_;
193 } else {
194 return Clock::now();
195 }
196 }
197 int minutes() const {
198 return (
199 std::chrono::duration_cast<std::chrono::minutes>(stop_time() - start_time()).count());
200 }
201 int seconds() const {
202 return (
203 std::chrono::duration_cast<std::chrono::seconds>(stop_time() - start_time()).count());
204 }
205
206 std::string error_message() {
207 if (terminated()) {
208 return error_message_;
209 } else {
210 return "";
211 }
212 }
213 bool error() {
214 if (terminated()) {
215 return error_message_.size() > 0;
216 } else {
217 return false;
218 }
219 }
220
221 std::string run_group_id() const { return run_group_id_; }
222 std::string run_id() const { return run_id_; }
223 std::string template_id() const { return template_id_; }
224
225 bool test() const { return default_test_flag_.load(); }
226
227 protected:
228 std::mutex mutex;
229 std::condition_variable go_condition;
230 bool go_signal = false;
231
232 private:
233 GlobalContext& global_context_;
234
235 protected:
236 TimePoint start_time_;
237 TimePoint stop_time_;
238 std::atomic<bool>& terminate_signal_;
239 std::string error_message_;
240
241 private:
242 std::string run_group_id_;
243 std::string run_id_;
244 std::string template_id_;
245 std::atomic<bool> default_test_flag_;
246};
247
248class ProcessingContext : public StorageContext {
249 public:
250 ProcessingContext(RunContext& context, std::string processor_name, bool test)
251 : StorageContext(context),
252 run_context_(context),
253 processor_name_(processor_name),
254 test_flag_(test) {
255 add_storage_context("run", storage_context("runbase") + "/" + processor_name);
256 if (mkdir(storage_context("run").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0) {
257 if (errno != EEXIST) {
258 throw std::runtime_error("Cannot create processor run environment " +
259 storage_context("run"));
260 }
261 }
262
263 if (storage_map().count("templatebase") == 1) {
264 add_storage_context("template", storage_context("templatebase") + "/" + processor_name);
265 }
266
267 // add processor specific test storage site
268 if (test) {
269 add_storage_context("test", storage_context("run") + "/test");
270 if (mkdir(storage_context("test").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) !=
271 0) {
272 if (errno != EEXIST) {
273 throw std::runtime_error("Cannot create processor test environment " +
274 storage_context("test"));
275 }
276 }
277
278 if (storage_map().count("template") == 1) {
279 add_storage_context("templatetest", storage_context("template") + "/test");
280 }
281 }
282
283 add_storage_context("lastrun", storage_context("lastrunbase") + "/" + processor_name);
284 add_storage_context("lasttest", storage_context("lastrun") + "/test");
285 set_default_context("run");
286 }
287
288 RunContext& run() { return run_context_; }
289
290 bool test() const { return test_flag_.load(); }
291
292 bool terminated() const { return run_context_.terminated(); }
293 void Terminate() { run_context_.Terminate(); }
294 void TerminateWithError(std::string step, std::string error_message) {
295 run_context_.TerminateWithError(processor_name_, step, error_message);
296 }
297
298 private:
299 RunContext& run_context_;
300 std::string processor_name_;
301 std::atomic<bool> test_flag_;
302 std::map<std::string, std::string> storage_map_;
303};
Definition context.hpp:54
Definition runinfo.hpp:43
Definition processorgraph.hpp:62