LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
tempdir.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2012-2026, QORE Lab, Imperial College London
3
* All rights reserved.
4
*/
5
#ifndef LINE_UTIL_TEMPDIR_H
6
#define LINE_UTIL_TEMPDIR_H
7
8
/**
9
* @file
10
* @ingroup line_util
11
* A scratch directory for the subprocess wrappers, the port's `lineTempName`.
12
*
13
* The wrappers hand an external binary a FILE, not a stream, so they need a
14
* private directory to put it in and to collect the tool's output from. The
15
* name comes from mkdtemp rather than from a counter or a PID: two solves of the
16
* same model running side by side (a parity sweep does exactly that) would
17
* otherwise write the same `model.jmva` and read each other's results.
18
*
19
* The directory is REMOVED IN THE DESTRUCTOR, including on the exception path,
20
* because the wrappers throw on a tool failure and a leaked scratch directory
21
* per failed solve accumulates silently. `keep()` suppresses the removal for
22
* the case where the caller wants to inspect what was sent.
23
*/
24
25
#include <cctype>
26
#include <cerrno>
27
#include <cstdlib>
28
#include <string>
29
#include <vector>
30
31
#include <dirent.h>
32
#include <sys/stat.h>
33
#include <unistd.h>
34
35
#include "
line/util/error.h
"
36
37
namespace
line
{
38
namespace
util
{
39
40
namespace
detail {
41
42
/** The string without its surrounding whitespace. */
43
inline
std::string trim_ws(
const
std::string& s) {
44
std::size_t b = 0, e = s.size();
45
while
(b < e && std::isspace(
static_cast<
unsigned
char
>
(s[b]))) ++b;
46
while
(e > b && std::isspace(
static_cast<
unsigned
char
>
(s[e - 1]))) --e;
47
return
s.substr(b, e - b);
48
}
49
50
/**
51
* Create a directory and every missing parent, as MATLAB's `mkdir` does.
52
*
53
* "It is already there" is success: two solves staging side by side race on the
54
* shared workspace directory, and the loser of that race has what it asked for.
55
*/
56
inline
void
mkdir_p(
const
std::string& dir) {
57
for
(std::size_t at = dir.find(
'/'
, 1); ; at = dir.find(
'/'
, at + 1)) {
58
const
std::string part = (at == std::string::npos) ? dir : dir.substr(0, at);
59
if
(::mkdir(part.c_str(), 0700) != 0 && errno != EEXIST)
60
throw
InputError(
"cannot create the staging directory '"
+ part +
"'"
);
61
if
(at == std::string::npos)
break
;
62
}
63
}
64
65
}
// namespace detail
66
67
/**
68
* Create a private scratch directory named after its caller.
69
*
70
* LINE_WORKSPACE_ROOT RELOCATES EVERY STAGED MODEL, exactly as it does in
71
* `lineTempName.m`, `SysUtils.lineTempName` and the native-Python wrappers.
72
* run-tests.sh sets it when it wraps lqns, lqsim or qnsolver in a container: the
73
* shim bind-mounts that root and nothing else, so a model left under TMPDIR is
74
* invisible to the binary, which then writes no result and is reported as having
75
* rejected the model. No wrapper here needs to know a container is involved.
76
*
77
* `mountable` is the second argument of `lineTempName`: a snap-confined Docker
78
* daemon bind-mounts neither the system temp dir nor a dot-directory, so a
79
* caller that hands the directory to a container asks for a HOME-rooted one.
80
*
81
* @param prefix a short tag naming the caller, as `lineTempName('qns')` does
82
* @param mountable root under $HOME/.line rather than TMPDIR
83
*/
84
inline
std::string
make_temp_dir
(
const
std::string& prefix,
bool
mountable =
false
) {
85
std::string root;
86
const
char
*
ws
= std::getenv(
"LINE_WORKSPACE_ROOT"
);
87
if
(
ws
!=
nullptr
) root = detail::trim_ws(std::string(
ws
));
88
if
(root.empty() && mountable) {
89
const
char
* home = std::getenv(
"HOME"
);
90
if
(home !=
nullptr
&& *home) root = std::string(home) +
"/.line"
;
91
}
92
if
(!root.empty()) {
93
root +=
"/line_workspace/"
+ prefix;
94
detail::mkdir_p(root);
95
}
else
{
96
const
char
* base = std::getenv(
"TMPDIR"
);
97
root = (base && *base ? std::string(base) : std::string(
"/tmp"
));
98
}
99
const
std::string tmpl = root +
"/line_"
+ prefix +
"_XXXXXX"
;
100
std::vector<char> buf(tmpl.begin(), tmpl.end());
101
buf.push_back(
'\0'
);
102
if
(::mkdtemp(&buf[0]) ==
nullptr
)
103
throw
InputError
(
"cannot create a temporary directory under '"
+ tmpl +
"'"
);
104
return
std::string(&buf[0]);
105
}
106
107
class
TempDir
{
108
public
:
109
/**
110
* @param prefix a short tag naming the caller, as `lineTempName('qns')` does
111
* @param mountable root it where a confined Docker daemon can bind-mount it
112
*/
113
explicit
TempDir
(
const
std::string& prefix,
bool
mountable =
false
)
114
: path_(
make_temp_dir
(prefix, mountable)) {}
115
116
~TempDir
() {
117
if
(!keep_ && !path_.empty()) remove_tree(path_);
118
}
119
120
TempDir
(
const
TempDir
&) =
delete
;
121
TempDir
&
operator=
(
const
TempDir
&) =
delete
;
122
123
/** The directory itself, with no trailing separator. */
124
const
std::string&
path
()
const
{
return
path_; }
125
126
/** A file inside it. */
127
std::string
file
(
const
std::string& name)
const
{
return
path_ +
"/"
+ name; }
128
129
/** Leave the directory in place, for a caller that wants to inspect it. */
130
void
keep
() { keep_ =
true
; }
131
132
private
:
133
static
void
remove_tree(
const
std::string& dir) {
134
DIR* d = ::opendir(dir.c_str());
135
if
(d !=
nullptr
) {
136
struct
dirent* e;
137
while
((e = ::readdir(d)) !=
nullptr
) {
138
const
std::string n(e->d_name);
139
if
(n ==
"."
|| n ==
".."
)
continue
;
140
const
std::string child = dir +
"/"
+ n;
141
struct
stat st;
142
if
(::lstat(child.c_str(), &st) == 0 && S_ISDIR(st.st_mode))
143
remove_tree(child);
144
else
145
::unlink(child.c_str());
146
}
147
::closedir(d);
148
}
149
::rmdir(dir.c_str());
150
}
151
152
std::string path_;
153
bool
keep_ =
false
;
154
};
155
156
}
// namespace util
157
}
// namespace line
158
159
#endif
// LINE_UTIL_TEMPDIR_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::util::TempDir::~TempDir
~TempDir()
Definition
tempdir.h:116
line::util::TempDir::TempDir
TempDir(const std::string &prefix, bool mountable=false)
Definition
tempdir.h:113
line::util::TempDir::file
std::string file(const std::string &name) const
A file inside it.
Definition
tempdir.h:127
line::util::TempDir::TempDir
TempDir(const TempDir &)=delete
line::util::TempDir::operator=
TempDir & operator=(const TempDir &)=delete
line::util::TempDir::keep
void keep()
Leave the directory in place, for a caller that wants to inspect it.
Definition
tempdir.h:130
line::util::TempDir::path
const std::string & path() const
The directory itself, with no trailing separator.
Definition
tempdir.h:124
error.h
The exception types the port throws.
line::util
Definition
line_console.h:45
line::util::make_temp_dir
std::string make_temp_dir(const std::string &prefix, bool mountable=false)
Create a private scratch directory named after its caller.
Definition
tempdir.h:84
line::ws
Definition
websocket.h:51
line
Definition
aoi_dist2ph.h:52
include
line
util
tempdir.h
Generated by
1.18.0