Generated on for Gecode by doxygen 1.15.0
blackbox-exec.cpp
Go to the documentation of this file.
1/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2/*
3 * Contributing authors:
4 * Mikael Zayenz Lagerkvist <lagerkvist@gecode.dev>
5 *
6 * Copyright:
7 * Jip J. Dekker, 2026
8 *
9 * This file is part of Gecode, the generic constraint
10 * development environment:
11 * http://www.gecode.dev
12 *
13 * Permission is hereby granted, free of charge, to any person obtaining
14 * a copy of this software and associated documentation files (the
15 * "Software"), to deal in the Software without restriction, including
16 * without limitation the rights to use, copy, modify, merge, publish,
17 * distribute, sublicense, and/or sell copies of the Software, and to
18 * permit persons to whom the Software is furnished to do so, subject to
19 * the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be
22 * included in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 *
32 */
33
34#include <cerrno>
35#include <cstdint>
36#include <cstdlib>
37#include <fstream>
38#include <iostream>
39#include <limits>
40#include <set>
41#include <string>
42
43#ifdef _WIN32
44#include <windows.h>
45#else
46#include <fcntl.h>
47#include <sys/types.h>
48#include <unistd.h>
49#endif
50
51namespace {
52
53 std::string
54 process_id(void) {
55#ifdef _WIN32
56 return std::to_string(static_cast<int64_t>(GetCurrentProcessId()));
57#else
58 return std::to_string(static_cast<int64_t>(getpid()));
59#endif
60 }
61
62 void
63 record(const std::string& log, const std::string& event,
64 const std::string& id) {
65 if (log.empty()) {
66 return;
67 }
68 const std::string line = event + " " + id + "\n";
69#ifdef _WIN32
70 HANDLE file = CreateFileA(log.c_str(), FILE_APPEND_DATA,
71 FILE_SHARE_READ | FILE_SHARE_WRITE |
72 FILE_SHARE_DELETE,
73 nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
74 nullptr);
75 if (file == INVALID_HANDLE_VALUE) {
76 return;
77 }
78 DWORD written;
79 (void)WriteFile(file, line.data(), static_cast<DWORD>(line.size()),
80 &written, nullptr);
81 (void)CloseHandle(file);
82#else
83 int fd;
84 do {
85 fd = open(log.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0600);
86 } while ((fd == -1) && (errno == EINTR));
87 if (fd == -1) {
88 return;
89 }
90 ssize_t n;
91 do {
92 n = write(fd, line.data(), line.size());
93 } while ((n == -1) && (errno == EINTR));
94 (void)close(fd);
95#endif
96 }
97
98 bool
99 has_two_starts(const std::string& log) {
100 std::ifstream in(log.c_str());
101 std::set<std::string> ids;
102 std::string event;
103 std::string id;
104 while (in >> event >> id) {
105 if (event == "start") {
106 ids.insert(id);
107 }
108 }
109 return ids.size() >= 2;
110 }
111
112 void
113 wait_for_peer(const std::string& log, const std::string& id) {
114 for (unsigned int i = 0; i < 500; ++i) {
115 if (has_two_starts(log)) {
116 return;
117 }
118#ifdef _WIN32
119 Sleep(10);
120#else
121 usleep(10000);
122#endif
123 }
124 record(log, "timeout", id);
125 }
126
127 bool
128 first_integer(const std::string& request, int64_t& value) {
129 const std::string::size_type end = request.find(';');
130 if ((end == std::string::npos) || (end == 0)) {
131 return false;
132 }
133 const std::string integer = request.substr(0, end);
134 char* last = nullptr;
135 errno = 0;
136 const long long parsed = std::strtoll(integer.c_str(), &last, 10);
137 if ((last == integer.c_str()) || (*last != '\0') || (errno == ERANGE) ||
138 (parsed < (std::numeric_limits<int64_t>::min)()) ||
139 (parsed > (std::numeric_limits<int64_t>::max)())) {
140 return false;
141 }
142 value = static_cast<int64_t>(parsed);
143 return true;
144 }
145
146 void
147 write_response(const std::string& value) {
148 std::cout.write(value.data(), static_cast<std::streamsize>(value.size()));
149 std::cout << std::endl;
150 std::cout.flush();
151 }
152
153 bool
154 dependent_bounds(const std::string& request) {
155 long long xmin, xmax, ymin, ymax;
156 if (std::sscanf(request.c_str(), "%lld,%lld,%lld,%lld;",
157 &xmin, &xmax, &ymin, &ymax) != 4) {
158 return false;
159 }
160 (void) ymin;
161 (void) ymax;
162 write_response(std::to_string(xmin) + "," + std::to_string(xmax) + "," +
163 std::to_string(5 * xmin) + "," +
164 std::to_string(5 * xmax) + ";");
165 return true;
166 }
167
168 const std::string*
169 fixed_response(const std::string& mode) {
170 static const std::string value7("7;");
171 static const std::string bounds2("5,5;");
172 static const std::string bounds4("5,5,5,5;");
173 static const std::string mixed("5,5;5,5");
174 static const std::string nan(";nan");
175 static const std::string nul(";\0x", 3);
176 static const std::string malformed("x;");
177 if (mode == "value7") {
178 return &value7;
179 } else if (mode == "bounds2") {
180 return &bounds2;
181 } else if (mode == "bounds4") {
182 return &bounds4;
183 } else if (mode == "mixed") {
184 return &mixed;
185 } else if (mode == "nan") {
186 return &nan;
187 } else if (mode == "nul") {
188 return &nul;
189 } else if (mode == "malformed") {
190 return &malformed;
191 }
192 return nullptr;
193 }
194
195 void
196 fault(int64_t kind) {
197 if (kind == 1) {
198 write_response("invalid");
199 } else if (kind == 2) {
200 return;
201 } else if (kind == 3) {
202 write_response(std::string(";\0x", 3));
203 } else if (kind == 5) {
204 write_response(std::to_string((std::numeric_limits<int64_t>::max)()) +
205 ";");
206 } else {
207 write_response(std::string(1024U * 1024U + 1U, 'x'));
208 }
209 }
210
211}
212
213int
214main(int argc, char* argv[]) {
215 const std::string mode = (argc > 1) ? argv[1] : "normal";
216 const std::string log = (argc > 2) ? argv[2] : "";
217 unsigned int round = 0;
218#ifndef _WIN32
219 bool descendant_started = false;
220#endif
221 std::string request;
222 while (std::getline(std::cin, request)) {
223 ++round;
224 int64_t value = 0;
225 const bool has_value = first_integer(request, value);
226 if (mode == "fault") {
227 fault(has_value ? value : 1);
228 return 0;
229 }
230 if (mode == "descendant") {
231#ifndef _WIN32
232 if (!descendant_started) {
233 int ready[2];
234 if (pipe(ready) != 0) {
235 return 1;
236 }
237 const pid_t descendant = fork();
238 if (descendant == 0) {
239 (void)close(ready[0]);
240 record(log, "descendant", process_id());
241 const char started = '1';
242 (void)write(ready[1], &started, 1);
243 (void)close(ready[1]);
244 for (;;) {
245 pause();
246 }
247 }
248 (void)close(ready[1]);
249 if (descendant <= 0) {
250 (void)close(ready[0]);
251 return 1;
252 }
253 char started = '\0';
254 ssize_t n;
255 do {
256 n = read(ready[0], &started, 1);
257 } while ((n == -1) && (errno == EINTR));
258 (void)close(ready[0]);
259 if ((n != 1) || (started != '1')) {
260 return 1;
261 }
262 descendant_started = true;
263 }
264#endif
265 write_response("1;");
266 continue;
267 }
268
269 if (mode == "dependent_bounds") {
270 if (!dependent_bounds(request)) {
271 return 1;
272 }
273 continue;
274 }
275
276 if (const std::string* value = fixed_response(mode)) {
277 write_response(*value);
278 continue;
279 }
280
281 if (!log.empty() && has_value) {
282 const std::string id = process_id();
283 record(log, "start", id);
284 wait_for_peer(log, id);
285 record(log, "ready", id);
286 }
287 write_response(std::to_string(has_value ? value :
288 static_cast<int64_t>(round)) + ";");
289 }
290 return 0;
291}
292
293// STATISTICS: test-flatzinc
int main(int argc, char *argv[])
void log(Home home, FloatVar x0, FloatVar x1)
Post propagator for .
Gecode::IntArgs i({1, 2, 3, 4})