Skip to content

Commit 5ec8e45

Browse files
authored
Fix TL when output is large (#149)
* Fix TL when program generates large output * Add tests * Fix tests * Change lim package * Fix ovl package * Fix lim package * Fix lim package again * Change wait in lim2.cpp * Change limits in lim package * Change wait time in lim2 * Change wait time in lim2
1 parent df1cbf2 commit 5ec8e45

File tree

13 files changed

+133
-47
lines changed

13 files changed

+133
-47
lines changed

src/sinol_make/commands/run/__init__.py

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -403,16 +403,18 @@ def check_output(self, name, input_file, output_file_path, output, answer_file_p
403403
output_file.write("\n".join(output) + "\n")
404404
return self.check_output_checker(name, input_file, output_file_path, answer_file_path)
405405

406-
def execute_oiejq(self, command, name, result_file_path, input_file_path, output_file_path, answer_file_path,
406+
def execute_oiejq(self, name, timetool_path, executable, result_file_path, input_file_path, output_file_path, answer_file_path,
407407
time_limit, memory_limit, hard_time_limit):
408+
command = f'"{timetool_path}" "{executable}"'
408409
env = os.environ.copy()
409410
env["MEM_LIMIT"] = f'{memory_limit}K'
410411
env["MEASURE_MEM"] = "1"
411412

412413
timeout = False
413-
with open(input_file_path, "r") as input_file:
414-
process = subprocess.Popen(command, shell=True, stdin=input_file, stdout=subprocess.PIPE,
415-
stderr=subprocess.PIPE, env=env, preexec_fn=os.setsid)
414+
with open(input_file_path, "r") as input_file, open(output_file_path, "w") as output_file, \
415+
open(result_file_path, "w") as result_file:
416+
process = subprocess.Popen(command, shell=True, stdin=input_file, stdout=output_file,
417+
stderr=result_file, env=env, preexec_fn=os.setsid)
416418

417419
def sigint_handler(signum, frame):
418420
try:
@@ -423,7 +425,7 @@ def sigint_handler(signum, frame):
423425
signal.signal(signal.SIGINT, sigint_handler)
424426

425427
try:
426-
output, lines = process.communicate(timeout=hard_time_limit)
428+
process.wait(timeout=time_limit)
427429
except subprocess.TimeoutExpired:
428430
timeout = True
429431
try:
@@ -432,11 +434,15 @@ def sigint_handler(signum, frame):
432434
pass
433435
process.communicate()
434436

437+
with open(result_file_path, "r") as result_file:
438+
lines = result_file.read()
439+
with open(output_file_path, "r") as output_file:
440+
output = output_file.read()
435441
result = ExecutionResult()
436442

437443
if not timeout:
438-
lines = lines.decode('utf-8').splitlines()
439-
output = output.decode('utf-8').splitlines()
444+
lines = lines.splitlines()
445+
output = output.splitlines()
440446

441447
for line in lines:
442448
line = line.strip()
@@ -476,14 +482,20 @@ def sigint_handler(signum, frame):
476482
return result
477483

478484

479-
def execute_time(self, command, name, result_file_path, input_file_path, output_file_path, answer_file_path,
485+
def execute_time(self, name, executable, result_file_path, input_file_path, output_file_path, answer_file_path,
480486
time_limit, memory_limit, hard_time_limit):
481-
482-
executable = package_util.get_executable(name)
487+
if sys.platform == 'darwin':
488+
time_name = 'gtime'
489+
elif sys.platform == 'linux':
490+
time_name = 'time'
491+
elif sys.platform == 'win32' or sys.platform == 'cygwin':
492+
raise Exception("Measuring time with GNU time on Windows is not supported.")
493+
494+
command = [f'{time_name}', '-f', '%U\\n%M\\n%x', '-o', result_file_path, executable]
483495
timeout = False
484496
mem_limit_exceeded = False
485-
with open(input_file_path, "r") as input_file:
486-
process = subprocess.Popen(command, stdin=input_file, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
497+
with open(input_file_path, "r") as input_file, open(output_file_path, "w") as output_file:
498+
process = subprocess.Popen(command, stdin=input_file, stdout=output_file, stderr=subprocess.DEVNULL,
487499
preexec_fn=os.setsid)
488500

489501
def sigint_handler(signum, frame):
@@ -520,12 +532,13 @@ def sigint_handler(signum, frame):
520532
pass
521533
timeout = True
522534
break
523-
output, _ = process.communicate()
524535

536+
with open(output_file_path, "r") as output_file:
537+
output = output_file.read()
525538
result = ExecutionResult()
526539
program_exit_code = None
527540
if not timeout:
528-
output = output.decode("utf-8").splitlines()
541+
output = output.splitlines()
529542
with open(result_file_path, "r") as result_file:
530543
lines = result_file.readlines()
531544
if len(lines) == 3:
@@ -589,22 +602,10 @@ def run_solution(self, data_for_execution: ExecutionData):
589602
hard_time_limit_in_s = math.ceil(2 * time_limit / 1000.0)
590603

591604
if self.timetool_name == 'oiejq':
592-
command = f'"{timetool_path}" "{executable}"'
593-
594-
return self.execute_oiejq(command, name, result_file, test, output_file, self.get_output_file(test),
605+
return self.execute_oiejq(name, timetool_path, executable, result_file, test, output_file, self.get_output_file(test),
595606
time_limit, memory_limit, hard_time_limit_in_s)
596607
elif self.timetool_name == 'time':
597-
if sys.platform == 'darwin':
598-
timeout_name = 'gtimeout'
599-
time_name = 'gtime'
600-
elif sys.platform == 'linux':
601-
timeout_name = 'timeout'
602-
time_name = 'time'
603-
elif sys.platform == 'win32' or sys.platform == 'cygwin':
604-
raise Exception("Measuring time with GNU time on Windows is not supported.")
605-
606-
command = [f'{time_name}', '-f', '%U\\n%M\\n%x', '-o', result_file, executable]
607-
return self.execute_time(command, name, result_file, test, output_file, self.get_output_file(test),
608+
return self.execute_time(name, executable, result_file, test, output_file, self.get_output_file(test),
608609
time_limit, memory_limit, hard_time_limit_in_s)
609610

610611
def run_solutions(self, compiled_commands, names, solutions):
@@ -1179,11 +1180,11 @@ def run(self, args):
11791180
self.check_errors(all_results)
11801181
try:
11811182
validation_results = self.validate_expected_scores(results)
1182-
except:
1183+
except Exception:
11831184
self.config = util.try_fix_config(self.config)
11841185
try:
11851186
validation_results = self.validate_expected_scores(results)
1186-
except:
1187+
except Exception:
11871188
util.exit_with_error("Validating expected scores failed. "
11881189
"This probably means that `sinol_expected_scores` is broken. "
11891190
"Delete it and run `sinol-make run --apply-suggestions` again.")

tests/commands/run/test_integration.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
@pytest.mark.parametrize("create_package", [get_simple_package_path(), get_verify_status_package_path(),
1515
get_checker_package_path(), get_library_package_path(),
1616
get_library_string_args_package_path(), get_limits_package_path(),
17-
get_override_limits_package_path(), get_icpc_package_path()],
17+
get_override_limits_package_path(), get_icpc_package_path(),
18+
get_large_output_package_path()],
1819
indirect=True)
1920
def test_simple(create_package, time_tool):
2021
"""
@@ -70,7 +71,8 @@ def test_wrong_solution(create_package, time_tool):
7071
@pytest.mark.parametrize("create_package", [get_simple_package_path(), get_verify_status_package_path(),
7172
get_checker_package_path(), get_library_package_path(),
7273
get_library_string_args_package_path(), get_limits_package_path(),
73-
get_override_limits_package_path(), get_icpc_package_path()],
74+
get_override_limits_package_path(), get_icpc_package_path(),
75+
get_large_output_package_path()],
7476
indirect=True)
7577
def test_no_expected_scores(capsys, create_package, time_tool):
7678
"""
@@ -106,7 +108,8 @@ def test_no_expected_scores(capsys, create_package, time_tool):
106108
@pytest.mark.parametrize("create_package", [get_simple_package_path(), get_verify_status_package_path(),
107109
get_checker_package_path(), get_library_package_path(),
108110
get_library_string_args_package_path(), get_limits_package_path(),
109-
get_override_limits_package_path(), get_icpc_package_path()],
111+
get_override_limits_package_path(), get_icpc_package_path(),
112+
get_large_output_package_path()],
110113
indirect=True)
111114
def test_apply_suggestions(create_package, time_tool):
112115
"""
@@ -452,8 +455,8 @@ def test_mem_limit_kill(create_package, time_tool):
452455
end_time = time.time()
453456

454457
assert e.value.code == 1
455-
assert end_time - start_time < 5 # The solution runs for 20 seconds, but it immediately exceeds memory limit,
456-
# so it should be killed.
458+
assert end_time - start_time < 10 # The solution runs for 20 seconds, but it immediately exceeds memory limit,
459+
# so it should be killed.
457460

458461

459462
@pytest.mark.parametrize("create_package", [get_undocumented_options_package_path()], indirect=True)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
title: Package with large outputs
2+
sinol_task_id: lou
3+
memory_limit: 16000
4+
time_limit: 2000
5+
sinol_expected_scores:
6+
lou.cpp:
7+
expected:
8+
0: {points: 0, status: OK}
9+
points: 0
10+
lou1.cpp:
11+
expected:
12+
0: {points: 0, status: WA}
13+
points: 0
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1

tests/packages/large_output/out/.gitkeep

Whitespace-only changes.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <bits/stdc++.h>
2+
3+
using namespace std;
4+
5+
int main() {
6+
int bytes_to_write = (1 << 16) + 1;
7+
for (int i = 0; i < bytes_to_write; i++) {
8+
cout << "x";
9+
}
10+
return 0;
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <bits/stdc++.h>
2+
3+
using namespace std;
4+
5+
int main() {
6+
int bytes_to_write = (1 << 20);
7+
for (int i = 0; i < bytes_to_write; i++) {
8+
cout << "x";
9+
}
10+
return 0;
11+
}

tests/packages/lim/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ memory_limits:
44
2: 60000
55
time_limit: 1000
66
time_limits:
7-
1: 3000
7+
1: 5000
88
scores:
99
1: 50
1010
2: 50

tests/packages/lim/prog/lim2.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,27 @@
22
#include <chrono>
33

44
using namespace std;
5-
using namespace std::chrono_literals;
5+
using namespace std::chrono;
6+
7+
int wait(int milisecs) {
8+
auto start = high_resolution_clock::now();
9+
int i = 0;
10+
while (duration_cast<milliseconds>(high_resolution_clock::now() - start).count() < milisecs)
11+
i++;
12+
return i;
13+
}
614

715
int main() {
816
int a, b;
917
cin >> a >> b;
1018

1119
if (a == 2 && b == 1) {
12-
this_thread::sleep_for(6s);
20+
int i = wait(7000);
21+
a += i - i;
1322
}
1423
else {
15-
this_thread::sleep_for(2s);
24+
int i = wait(2000);
25+
a += i - i;
1626
}
1727

1828
cout << a + b << endl;

tests/packages/ovl/prog/ovl.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,21 @@
22
#include <chrono>
33

44
using namespace std;
5-
using namespace std::chrono_literals;
5+
using namespace std::chrono;
6+
7+
int wait(int secs) {
8+
auto start = high_resolution_clock::now();
9+
int i = 0;
10+
while (duration_cast<seconds>(high_resolution_clock::now() - start).count() < secs)
11+
i++;
12+
return i;
13+
}
614

715
int main() {
8-
this_thread::sleep_for(2s);
16+
int i = wait(2);
917

1018
int a, b;
1119
cin >> a >> b;
12-
cout << a + b;
20+
b += i;
21+
cout << a + b - i;
1322
}

0 commit comments

Comments
 (0)