Skip to content

Option to pass fzn via stdin instead of temp file #317

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 89 additions & 2 deletions include/minizinc/process.hh
Original file line number Diff line number Diff line change
@@ -72,13 +72,87 @@ namespace MiniZinc {

#endif

class InputProvider {

private:
#ifdef _WIN32
HANDLE _h_pipe = nullptr;
#else
int _pipe = 0;
#endif

public:
InputProvider() : stream(this) {}

std::ostream* getStream() {
return &stream;
}

virtual void provide() {}

#ifdef _WIN32
void setHandle(HANDLE h_pipe) {
_h_pipe = h_pipe;
}
#else
void setPipe(int pipe) {
_pipe = pipe;
}
#endif

class PipeStream : public std::ostream {

public:
explicit PipeStream(InputProvider* input) : std::ostream(new PipeBuf(input)) {}

class PipeBuf : public std::streambuf {

public:
explicit PipeBuf(InputProvider* input) : _input(input) {}

protected:
std::streamsize xsputn(const char_type* s, std::streamsize n) override {
return write(s, n);
}

int_type overflow(int_type ch) override {
return write(&ch, 1);
}

private:
InputProvider* _input;

std::streamsize write(const void* s, std::streamsize n) {
#ifdef _WIN32
if (_input->_h_pipe == nullptr) {
return 0;
}
DWORD count;
BOOL success = WriteFile(_input->_h_pipe, reinterpret_cast<const char*>(s), n, &count, nullptr);
return count;
#else
if (_input->_pipe == 0) {
return 0;
}
return ::write(_input->_pipe, s, n);
#endif
}
};
};

protected:
InputProvider::PipeStream stream;

};

template<class S2O>
class Process {
protected:
std::vector<std::string> _fzncmd;
S2O* pS2Out;
int timelimit;
bool sigint;
InputProvider* _input;
#ifndef _WIN32
static void handleInterrupt(int signal) {
if (signal==SIGINT)
@@ -90,8 +164,8 @@ namespace MiniZinc {
static bool hadTerm;
#endif
public:
Process(std::vector<std::string>& fzncmd, S2O* pso, int tl, bool si)
: _fzncmd(fzncmd), pS2Out(pso), timelimit(tl), sigint(si) {
Process(std::vector<std::string>& fzncmd, S2O* pso, int tl, bool si, InputProvider* input)
: _fzncmd(fzncmd), pS2Out(pso), timelimit(tl), sigint(si), _input(input) {
assert( 0!=pS2Out );
}
int run(void) {
@@ -173,6 +247,13 @@ namespace MiniZinc {
// Stop ReadFile from blocking
CloseHandle(g_hChildStd_OUT_Wr);
CloseHandle(g_hChildStd_ERR_Wr);

if (_input != NULL) {
_input->setHandle(g_hChildStd_IN_Wr);
_input->provide();
CloseHandle(g_hChildStd_IN_Wr);
}

// Just close the child's in pipe here
CloseHandle(g_hChildStd_IN_Rd);
bool doneStdout = false;
@@ -208,6 +289,12 @@ namespace MiniZinc {
close(pipes[0][0]);
close(pipes[1][1]);
close(pipes[2][1]);

if (_input != NULL) {
_input->setPipe(pipes[0][1]);
_input->provide();
}

close(pipes[0][1]);

fd_set fdset;
3 changes: 3 additions & 0 deletions include/minizinc/solvers/fzn_solverinstance.hh
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ namespace MiniZinc {
int fzn_time_limit_ms = 0;
int solver_time_limit_ms = 0;
bool fzn_sigint = false;
bool fzn_use_stdin = false;

bool fzn_needs_paths = false;
bool fzn_output_passthrough = false;
@@ -63,6 +64,8 @@ namespace MiniZinc {

void resetSolver(void);

void printModel(Printer p);

protected:
Expression* getSolutionValue(Id* id);
};
84 changes: 58 additions & 26 deletions solvers/fzn/fzn_solverinstance.cpp
Original file line number Diff line number Diff line change
@@ -80,6 +80,7 @@ namespace MiniZinc {
<< " -k, --keep-files\n For compatibility only: to produce .ozn and .fzn, use mzn2fzn\n"
" or <this_exe> --fzn ..., --ozn ...\n"
<< " -r <n>, --seed <n>, --random-seed <n>\n For compatibility only: use solver flags instead.\n"
<< " --use-stdin\n Pass flatzinc to solver via stdin instead of temp file.\n"
;
}

@@ -143,6 +144,8 @@ namespace MiniZinc {
} else if ( cop.getOption( "-f --free-search") ) {
if (_opt.supports_f)
_opt.fzn_flags.push_back("-f");
} else if ( cop.getOption( "--use-stdin") ) {
_opt.fzn_use_stdin = true;
} else {
for (auto& fznf : _opt.fzn_solver_flags) {
if (fznf.t==MZNFZNSolverFlag::FT_ARG && cop.getOption(fznf.n.c_str(), &buffer)) {
@@ -186,6 +189,20 @@ namespace MiniZinc {
}
}

class FZN_Provider : public InputProvider {

protected:
FZNSolverInstance* _inst;
Printer p;

public:
explicit FZN_Provider(FZNSolverInstance* inst) : _inst(inst), p(*getStream(), 0, true) {};

void provide() override {
_inst->printModel(p);
}

};

FZNSolverInstance::FZNSolverInstance(Env& env, std::ostream& log, SolverInstanceBase::Options* options)
: SolverInstanceBase(env, log, options), _fzn(env.flat()), _ozn(env.output()) {}
@@ -244,30 +261,6 @@ namespace MiniZinc {
}
int timelimit = opt.fzn_time_limit_ms;
bool sigint = opt.fzn_sigint;

FileUtils::TmpFile fznFile(".fzn");
std::ofstream os(fznFile.name());
Printer p(os, 0, true);
for (FunctionIterator it = _fzn->begin_functions(); it != _fzn->end_functions(); ++it) {
if(!it->removed()) {
Item& item = *it;
p.print(&item);
}
}
for (VarDeclIterator it = _fzn->begin_vardecls(); it != _fzn->end_vardecls(); ++it) {
if(!it->removed()) {
Item& item = *it;
p.print(&item);
}
}
for (ConstraintIterator it = _fzn->begin_constraints(); it != _fzn->end_constraints(); ++it) {
if(!it->removed()) {
Item& item = *it;
p.print(&item);
}
}
p.print(_fzn->solveItem());
cmd_line.push_back(fznFile.name());

FileUtils::TmpFile* pathsFile = NULL;
if(opt.fzn_needs_paths) {
@@ -280,15 +273,29 @@ namespace MiniZinc {
cmd_line.push_back(pathsFile->name());
}

FZN_Provider* input = nullptr;
FileUtils::TmpFile* fznFile = nullptr;
if (opt.fzn_use_stdin) {
input = new FZN_Provider(this);
} else {
fznFile = new FileUtils::TmpFile(".fzn");
std::ofstream os(fznFile->name());
Printer p(os, 0, true);
printModel(p);
cmd_line.push_back(fznFile->name());
}

if(!opt.fzn_output_passthrough) {
Process<Solns2Out> proc(cmd_line, getSolns2Out(), timelimit, sigint);
Process<Solns2Out> proc(cmd_line, getSolns2Out(), timelimit, sigint, input);
int exitStatus = proc.run();
delete fznFile;
delete pathsFile;
return exitStatus == 0 ? getSolns2Out()->status : SolverInstance::ERROR;
} else {
Solns2Log s2l(getSolns2Out()->getOutput(), _log);
Process<Solns2Log> proc(cmd_line, &s2l, timelimit, sigint);
Process<Solns2Log> proc(cmd_line, &s2l, timelimit, sigint, input);
int exitStatus = proc.run();
delete fznFile;
delete pathsFile;
return exitStatus==0 ? SolverInstance::NONE : SolverInstance::ERROR;
}
@@ -303,4 +310,29 @@ namespace MiniZinc {
assert(false);
return NULL;
}

void FZNSolverInstance::printModel(Printer p) {

for (FunctionIterator it = _fzn->begin_functions(); it != _fzn->end_functions(); ++it) {
if(!it->removed()) {
Item& item = *it;
p.print(&item);
}
}
for (VarDeclIterator it = _fzn->begin_vardecls(); it != _fzn->end_vardecls(); ++it) {
if(!it->removed()) {
Item& item = *it;
p.print(&item);
}
}
for (ConstraintIterator it = _fzn->begin_constraints(); it != _fzn->end_constraints(); ++it) {
if(!it->removed()) {
Item& item = *it;
p.print(&item);
}
}
p.print(_fzn->solveItem());

}

}
2 changes: 1 addition & 1 deletion solvers/mzn/mzn_solverinstance.cpp
Original file line number Diff line number Diff line change
@@ -185,7 +185,7 @@ namespace MiniZinc {
int timelimit = opt.mzn_time_limit_ms;
bool sigint = opt.mzn_sigint;
Solns2Log s2l(getSolns2Out()->getOutput(), _log);
Process<Solns2Log> proc(cmd_line, &s2l, timelimit, sigint);
Process<Solns2Log> proc(cmd_line, &s2l, timelimit, sigint, nullptr);
int exitCode = proc.run();

return exitCode == 0 ? SolverInstance::UNKNOWN : SolverInstance::ERROR;
2 changes: 1 addition & 1 deletion solvers/nl/nl_solverinstance.cpp
Original file line number Diff line number Diff line change
@@ -209,7 +209,7 @@ namespace MiniZinc {
cmd_line.push_back(opt.nl_solver);
cmd_line.push_back(file_nl);
cmd_line.push_back("-AMPL");
Process<NLSolns2Out> proc(cmd_line, &s2o, 0, true);
Process<NLSolns2Out> proc(cmd_line, &s2o, 0, true, nullptr);
exitStatus = proc.run();

if (exitStatus == 0) {