Skip to content

Commit 3d07501

Browse files
authored
feat: Interpreter as Watcher for UNIX (#250)
* feat: Runner wrap for UNIX * refactor(judge): Use MiB as parameter name * feat: Execute as watcher * chore: Fix tests * refactor: Rename to Interpreter as Watcher * test: Fix file conflicts * fix: Minor fixes * refactor: Split into multiple arguments
1 parent ca50c29 commit 3d07501

24 files changed

Lines changed: 357 additions & 168 deletions

src/advancedcompilersettingsdialog.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ AdvancedCompilerSettingsDialog::AdvancedCompilerSettingsDialog(QWidget *parent)
4343
&AdvancedCompilerSettingsDialog::memoryLimitRatioChanged);
4444
connect(ui->disableMemoryLimit, &QCheckBox::stateChanged, this,
4545
&AdvancedCompilerSettingsDialog::disableMemoryLimitCheckChanged);
46+
connect(ui->interpreterAsWatcher, &QCheckBox::stateChanged, this,
47+
&AdvancedCompilerSettingsDialog::interpreterAsWatcherCheckChanged);
4648
connect(ui->configurationSelect, qOverload<int>(&QComboBox::currentIndexChanged), this,
4749
&AdvancedCompilerSettingsDialog::configurationIndexChanged);
4850
connect(ui->configurationSelect, &QComboBox::editTextChanged, this,
@@ -70,6 +72,7 @@ void AdvancedCompilerSettingsDialog::resetEditCompiler(Compiler *compiler) {
7072
ui->timeLimitRatio->setValue(editCompiler->getTimeLimitRatio());
7173
ui->memoryLimitRatio->setValue(editCompiler->getMemoryLimitRatio());
7274
ui->disableMemoryLimit->setChecked(editCompiler->getDisableMemoryLimitCheck());
75+
ui->interpreterAsWatcher->setChecked(editCompiler->getInterpreterAsWatcher());
7376
ui->memoryLimitRatio->setEnabled(! editCompiler->getDisableMemoryLimitCheck());
7477
QStringList configurationNames = editCompiler->getConfigurationNames();
7578
ui->configurationSelect->setEnabled(false);
@@ -145,12 +148,14 @@ void AdvancedCompilerSettingsDialog::compilerTypeChanged() {
145148
ui->interpreterSelectButton->setEnabled(false);
146149
ui->interpreterArgumentsLabel->setEnabled(false);
147150
ui->interpreterArguments->setEnabled(false);
151+
ui->interpreterAsWatcher->setEnabled(false);
148152
} else {
149153
ui->interpreterLabel->setEnabled(true);
150154
ui->interpreterLocation->setEnabled(true);
151155
ui->interpreterSelectButton->setEnabled(true);
152156
ui->interpreterArgumentsLabel->setEnabled(true);
153157
ui->interpreterArguments->setEnabled(true);
158+
ui->interpreterAsWatcher->setEnabled(true);
154159
}
155160

156161
if (editCompiler->getCompilerType() == Compiler::InterpretiveWithByteCode) {
@@ -233,6 +238,11 @@ void AdvancedCompilerSettingsDialog::disableMemoryLimitCheckChanged() {
233238
ui->memoryLimitRatio->setEnabled(! check);
234239
}
235240

241+
void AdvancedCompilerSettingsDialog::interpreterAsWatcherCheckChanged() {
242+
bool check = ui->interpreterAsWatcher->isChecked();
243+
editCompiler->setInterpreterAsWatcher(check);
244+
}
245+
236246
void AdvancedCompilerSettingsDialog::configurationIndexChanged() {
237247
if (! ui->configurationSelect->isEnabled())
238248
return;

src/advancedcompilersettingsdialog.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class AdvancedCompilerSettingsDialog : public QDialog {
4343
void timeLimitRatioChanged();
4444
void memoryLimitRatioChanged();
4545
void disableMemoryLimitCheckChanged();
46+
void interpreterAsWatcherCheckChanged();
4647
void configurationIndexChanged();
4748
void configurationTextChanged();
4849
void deleteConfiguration();

src/base/compiler.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Compiler::Compiler(QObject *parent) : QObject(parent) {
1616
timeLimitRatio = 1;
1717
memoryLimitRatio = 1;
1818
disableMemoryLimitCheck = false;
19+
interpreterAsWatcher = false;
1920
}
2021

2122
auto Compiler::getCompilerType() const -> Compiler::CompilerType { return compilerType; }
@@ -44,6 +45,8 @@ auto Compiler::getMemoryLimitRatio() const -> double { return memoryLimitRatio;
4445

4546
auto Compiler::getDisableMemoryLimitCheck() const -> bool { return disableMemoryLimitCheck; }
4647

48+
auto Compiler::getInterpreterAsWatcher() const -> bool { return interpreterAsWatcher; }
49+
4750
void Compiler::setCompilerType(Compiler::CompilerType type) { compilerType = type; }
4851

4952
void Compiler::setCompilerName(const QString &name) { compilerName = name; }
@@ -68,6 +71,8 @@ void Compiler::setMemoryLimitRatio(double ratio) { memoryLimitRatio = ratio; }
6871

6972
void Compiler::setDisableMemoryLimitCheck(bool check) { disableMemoryLimitCheck = check; }
7073

74+
void Compiler::setInterpreterAsWatcher(bool use) { interpreterAsWatcher = use; }
75+
7176
void Compiler::addConfiguration(const QString &name, const QString &arguments1, const QString &arguments2) {
7277
configurationNames.append(name);
7378
compilerArguments.append(arguments1);
@@ -114,6 +119,7 @@ void Compiler::copyFrom(Compiler *other) {
114119
timeLimitRatio = other->getTimeLimitRatio();
115120
memoryLimitRatio = other->getMemoryLimitRatio();
116121
disableMemoryLimitCheck = other->getDisableMemoryLimitCheck();
122+
interpreterAsWatcher = other->getInterpreterAsWatcher();
117123
}
118124

119125
int Compiler::read(const QJsonObject &json) {
@@ -144,6 +150,7 @@ int Compiler::read(const QJsonObject &json) {
144150
READ_JSON(json, timeLimitRatio);
145151
READ_JSON(json, memoryLimitRatio);
146152
READ_JSON(json, disableMemoryLimitCheck);
153+
READ_JSON(json, interpreterAsWatcher);
147154
return 0;
148155
}
149156

@@ -164,4 +171,5 @@ void Compiler::write(QJsonObject &json) const {
164171
WRITE_JSON(json, timeLimitRatio); // double
165172
WRITE_JSON(json, memoryLimitRatio); // double
166173
WRITE_JSON(json, disableMemoryLimitCheck); // bool
174+
WRITE_JSON(json, interpreterAsWatcher); // bool
167175
}

src/base/compiler.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class Compiler : public QObject {
3434
double getTimeLimitRatio() const;
3535
double getMemoryLimitRatio() const;
3636
bool getDisableMemoryLimitCheck() const;
37+
bool getInterpreterAsWatcher() const;
3738

3839
void setCompilerType(CompilerType);
3940
void setCompilerName(const QString &);
@@ -45,6 +46,7 @@ class Compiler : public QObject {
4546
void setTimeLimitRatio(double);
4647
void setMemoryLimitRatio(double);
4748
void setDisableMemoryLimitCheck(bool);
49+
void setInterpreterAsWatcher(bool);
4850

4951
void addConfiguration(const QString &, const QString &, const QString &);
5052
void setConfigName(int, const QString &);
@@ -71,4 +73,5 @@ class Compiler : public QObject {
7173
double timeLimitRatio;
7274
double memoryLimitRatio;
7375
bool disableMemoryLimitCheck;
76+
bool interpreterAsWatcher;
7477
};

src/base/settings.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ void Settings::saveSettings() {
483483
settings.setValue("TimeLimitRatio", compilerList[i]->getTimeLimitRatio());
484484
settings.setValue("MemoryLimitRatio", compilerList[i]->getMemoryLimitRatio());
485485
settings.setValue("DisableMemoryLimitCheck", compilerList[i]->getDisableMemoryLimitCheck());
486+
settings.setValue("InterpreterAsWatcher", compilerList[i]->getInterpreterAsWatcher());
486487
QStringList configurationNames = compilerList[i]->getConfigurationNames();
487488
QStringList compilerArguments = compilerList[i]->getCompilerArguments();
488489
QStringList interpreterArguments = compilerList[i]->getInterpreterArguments();
@@ -609,6 +610,7 @@ void Settings::loadSettings() {
609610
compiler->setTimeLimitRatio(settings.value("TimeLimitRatio").toDouble());
610611
compiler->setMemoryLimitRatio(settings.value("MemoryLimitRatio").toDouble());
611612
compiler->setDisableMemoryLimitCheck(settings.value("DisableMemoryLimitCheck").toBool());
613+
compiler->setInterpreterAsWatcher(settings.value("InterpreterAsWatcher").toBool());
612614
int configurationCount = settings.beginReadArray("Configuration");
613615

614616
for (int j = 0; j < configurationCount; j++) {

src/core/judgingthread.cpp

Lines changed: 102 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include "judgingthread.h"
11+
#include "LemonType.hpp"
1112
#include "base/LemonLog.hpp"
1213
#include "base/settings.h"
1314
#include "core/judgesharedvariables.h"
@@ -86,8 +87,14 @@ void JudgingThread::setFullScore(int score) { fullScore = score; }
8687

8788
void JudgingThread::setTimeLimit(int limit) { timeLimit = limit; }
8889

90+
void JudgingThread::setRawTimeLimit(int limit) { rawTimeLimit = limit; }
91+
8992
void JudgingThread::setMemoryLimit(int limit) { memoryLimit = limit; }
9093

94+
void JudgingThread::setRawMemoryLimit(int limit) { rawMemoryLimit = limit; }
95+
96+
void JudgingThread::setInterpreterAsWatcher(bool use) { interpreterAsWatcher = use; }
97+
9198
auto JudgingThread::getTimeUsed() const -> int { return timeUsed; }
9299

93100
auto JudgingThread::getMemoryUsed() const -> int { return memoryUsed; }
@@ -888,7 +895,13 @@ void JudgingThread::runProgram() {
888895
#ifdef Q_OS_LINUX
889896
// TODO: rewrite with cgroup
890897
QFile watcher(workingDirectory + QUuid::createUuid().toString(QUuid::Id128));
891-
QFile::copy(":/watcher/watcher_unix", watcher.fileName());
898+
899+
if (interpreterAsWatcher) {
900+
QFile::copy(executableFile, watcher.fileName());
901+
} else {
902+
QFile::copy(":/watcher/watcher_unix", watcher.fileName());
903+
}
904+
892905
watcher.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
893906
auto *runner = new QProcess(this);
894907
QStringList argumentsList;
@@ -925,7 +938,8 @@ void JudgingThread::runProgram() {
925938

926939
argumentsList << watcher.fileName();
927940

928-
argumentsList << QString("\"%1\" %2").arg(executableFile, arguments);
941+
argumentsList << executableFile;
942+
argumentsList << arguments;
929943

930944
if (task->getStandardInputCheck()) {
931945
argumentsList << QFileInfo(inputFile).absoluteFilePath();
@@ -942,6 +956,20 @@ void JudgingThread::runProgram() {
942956
argumentsList << "_tmperr";
943957
argumentsList << QString("%1").arg(timeLimit + extraTime);
944958
argumentsList << QString("%1").arg(memoryLimit);
959+
argumentsList << QString("%1").arg(rawTimeLimit);
960+
argumentsList << QString("%1").arg(rawMemoryLimit);
961+
962+
if (task->getStandardInputCheck()) {
963+
argumentsList << "";
964+
} else {
965+
argumentsList << task->getInputFileName();
966+
}
967+
968+
if (task->getStandardOutputCheck()) {
969+
argumentsList << "";
970+
} else {
971+
argumentsList << task->getOutputFileName();
972+
}
945973

946974
qDebug() << argumentsList;
947975

@@ -952,11 +980,19 @@ void JudgingThread::runProgram() {
952980
#else
953981

954982
QFile watcher(workingDirectory + QUuid::createUuid().toString(QUuid::Id128));
955-
QFile::copy(":/watcher/watcher_unix", watcher.fileName());
983+
984+
if (interpreterAsWatcher) {
985+
QFile::copy(executableFile, watcher.fileName());
986+
} else {
987+
QFile::copy(":/watcher/watcher_unix", watcher.fileName());
988+
}
989+
956990
watcher.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner);
957991
auto *runner = new QProcess(this);
958992
QStringList argumentsList;
959-
argumentsList << QString("\"%1\" %2").arg(executableFile, arguments);
993+
994+
argumentsList << executableFile;
995+
argumentsList << arguments;
960996

961997
if (task->getStandardInputCheck()) {
962998
argumentsList << QFileInfo(inputFile).absoluteFilePath();
@@ -973,6 +1009,22 @@ void JudgingThread::runProgram() {
9731009
argumentsList << "_tmperr";
9741010
argumentsList << QString("%1").arg(timeLimit + extraTime);
9751011
argumentsList << QString("%1").arg(memoryLimit);
1012+
argumentsList << QString("%1").arg(rawTimeLimit);
1013+
argumentsList << QString("%1").arg(rawMemoryLimit);
1014+
1015+
if (task->getStandardInputCheck()) {
1016+
argumentsList << "";
1017+
} else {
1018+
argumentsList << task->getInputFileName();
1019+
}
1020+
1021+
if (task->getStandardOutputCheck()) {
1022+
argumentsList << "";
1023+
} else {
1024+
argumentsList << task->getOutputFileName();
1025+
}
1026+
1027+
qDebug() << argumentsList;
9761028

9771029
runner->setProcessEnvironment(environment);
9781030
runner->setWorkingDirectory(workingDirectory);
@@ -984,6 +1036,7 @@ void JudgingThread::runProgram() {
9841036
delete runner;
9851037
score = 0;
9861038
result = CannotStartProgram;
1039+
message = "Start runner failed";
9871040
return;
9881041
}
9891042

@@ -1021,65 +1074,61 @@ void JudgingThread::runProgram() {
10211074
runner->waitForFinished(-1);
10221075
delete runner;
10231076
score = 0;
1024-
result = TimeLimitExceeded;
10251077
timeUsed = memoryUsed = -1;
1026-
return;
1027-
}
1028-
1029-
int code = runner->exitCode();
1030-
1031-
if (code == 1) {
1032-
delete runner;
1033-
score = 0;
1078+
// Watcher usually needs to handle the situation of program timeout and kill it. Therefore, it is
1079+
// abnormal for watcher to timeout itself, and report FAIL instead of TLE.
10341080
result = CannotStartProgram;
1035-
timeUsed = memoryUsed = -1;
1081+
message = "Watcher time limit exceeded";
10361082
return;
10371083
}
10381084

1039-
if (code == 2) {
1040-
delete runner;
1041-
score = 0;
1042-
result = RunTimeError;
1043-
QFile file(workingDirectory + "_tmperr");
1044-
1045-
if (file.open(QFile::ReadOnly)) {
1046-
QTextStream stream(&file);
1047-
message = stream.readAll();
1048-
file.close();
1049-
}
1050-
1051-
timeUsed = memoryUsed = -1;
1052-
return;
1085+
{
1086+
QString out = QString::fromLocal8Bit(runner->readAllStandardOutput().constData());
1087+
QTextStream stream(&out, QIODevice::ReadOnly);
1088+
stream >> timeUsed >> memoryUsed;
10531089
}
10541090

1055-
QString out = QString::fromLocal8Bit(runner->readAllStandardOutput().constData());
1056-
QTextStream stream(&out, QIODevice::ReadOnly);
1057-
stream >> timeUsed >> memoryUsed;
1091+
message = QString::fromLocal8Bit(runner->readAllStandardError().constData());
10581092

1059-
if (memoryUsed <= 0)
1060-
memoryLimit = -1;
1061-
1062-
if (code == 3) {
1063-
delete runner;
1064-
score = 0;
1065-
result = TimeLimitExceeded;
1066-
timeUsed = -1;
1067-
return;
1068-
}
1093+
enum : int {
1094+
RS_AC = 0,
1095+
RS_FAIL = 1,
1096+
RS_RE = 2,
1097+
RS_TLE = 3,
1098+
RS_MLE = 4,
1099+
};
10691100

1070-
if (code == 4) {
1071-
delete runner;
1072-
score = 0;
1073-
result = MemoryLimitExceeded;
1074-
memoryUsed = -1;
1075-
return;
1076-
}
1101+
int code = runner->exitCode();
10771102

1078-
if (memoryUsed > memoryLimit * 1024LL * 1024) {
1079-
delete runner;
1080-
score = 0;
1081-
result = MemoryLimitExceeded;
1082-
return;
1103+
switch (code) {
1104+
case RS_RE:
1105+
result = RunTimeError;
1106+
score = 0;
1107+
[[fallthrough]];
1108+
case RS_AC: {
1109+
QFile file(workingDirectory + "_tmperr");
1110+
if (file.open(QFile::ReadOnly)) {
1111+
message += file.readAll().right(1024);
1112+
file.close();
1113+
}
1114+
} break;
1115+
case RS_FAIL:
1116+
result = CannotStartProgram;
1117+
score = 0;
1118+
break;
1119+
case RS_TLE:
1120+
result = TimeLimitExceeded;
1121+
score = 0;
1122+
break;
1123+
case RS_MLE:
1124+
result = MemoryLimitExceeded;
1125+
score = 0;
1126+
break;
1127+
default:
1128+
result = CannotStartProgram;
1129+
score = 0;
1130+
message = QString("Watcher reported an invalid result: %1").arg(code);
1131+
break;
10831132
}
10841133

10851134
delete runner;

src/core/judgingthread.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ class JudgingThread : public QThread {
3434
void setTask(Task *);
3535
void setFullScore(int);
3636
void setTimeLimit(int);
37+
void setRawTimeLimit(int);
3738
void setMemoryLimit(int);
39+
void setRawMemoryLimit(int);
40+
void setInterpreterAsWatcher(bool);
3841
int getTimeUsed() const;
3942
int getMemoryUsed() const;
4043
int getScore() const;
@@ -63,14 +66,17 @@ class JudgingThread : public QThread {
6366
int specialJudgeTimeLimit{};
6467
int fullScore{};
6568
int timeLimit{};
69+
int rawTimeLimit{};
6670
int memoryLimit{};
71+
int rawMemoryLimit{};
6772
int timeUsed;
6873
int memoryUsed;
6974
int score{};
7075
int judgedTimes;
7176
ResultState result;
7277
QString message;
7378
bool stopJudging;
79+
bool interpreterAsWatcher{};
7480
void compareLineByLine(const QString &);
7581
void compareIgnoreSpaces(const QString &);
7682
void compareWithDiff(const QString &);

0 commit comments

Comments
 (0)