1 
2 /*
3  * File BFNTMainLoop.cpp.
4  *
5  * This file is part of the source code of the software program
6  * Vampire. It is protected by applicable
7  * copyright laws.
8  *
9  * This source code is distributed under the licence found here
10  * https://vprover.github.io/license.html
11  * and in the source directory
12  *
13  * In summary, you are allowed to use Vampire for non-commercial
14  * purposes but not allowed to distribute, modify, copy, create derivatives,
15  * or use in competitions.
16  * For other uses of Vampire please contact developers for a different
17  * licence, which we will make an effort to provide.
18  */
19 /**
20  * @file BFNTMainLoop.cpp
21  * Implements class BFNTMainLoop.
22  */
23 
24 #include <cerrno>
25 #include <csignal>
26 
27 #include "Lib/Portability.hpp"
28 
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 
32 #include "Lib/Environment.hpp"
33 #include "Lib/Int.hpp"
34 #include "Lib/Metaiterators.hpp"
35 #include "Lib/ScopedPtr.hpp"
36 #include "Lib/System.hpp"
37 #include "Lib/Timer.hpp"
38 
39 #include "Lib/Sys/Multiprocessing.hpp"
40 
41 #include "Kernel/Clause.hpp"
42 #include "Kernel/Problem.hpp"
43 #include "Kernel/Unit.hpp"
44 
45 #include "Shell/Property.hpp"
46 #include "Shell/TPTPPrinter.hpp"
47 #include "Shell/UIHelper.hpp"
48 
49 #include "Statistics.hpp"
50 
51 #include "BFNTMainLoop.hpp"
52 
53 #define BFNT_CHILD_RESULT_SAT 0
54 #define BFNT_CHILD_RESULT_UNSAT 6
55 #define BFNT_CHILD_RESULT_UNKNOWN 7
56 
57 namespace Shell
58 {
59 
BFNTMainLoop(Problem & prb,const Options & opt)60 BFNTMainLoop::BFNTMainLoop(Problem& prb, const Options& opt)
61 : MainLoop(prb, opt),
62   _childOpts(opt),
63   _bfnt(prb.getProperty())
64 {
65   CALL("BFNTMainLoop::BFNTMainLoop");
66 
67   //this is important, otherwise we'd start creating new and new processes
68   // -- the child process would also run BFNT therefore also attemps to run
69   //BFNT-running children.
70   _childOpts.setBfnt(false);
71 }
72 
73 
init()74 void BFNTMainLoop::init()
75 {
76   CALL("BFNTMainLoop::init");
77 
78   _hasSorts = _prb.getProperty()->hasNonDefaultSorts();
79   if(_hasSorts) {
80     return;
81   }
82 
83   //Putting problem units into the BFNT convertor here may result into
84   //one clause appearing in multiple Problem objects. In _prb and then in
85   //child problems created by the spawed processes. Normally we wouldn't
86   //want this to happen, but in the _prb object we do not use the clauses
87   //any more after this point, and the child problems are isolated from each
88   //other in different processes.
89   _bfnt.apply(_prb.units());
90 }
91 
92 /**
93  * Run the child process that does proving on the flattenned problem
94  *
95  * Result statuses of the process:
96  * BFNT_CHILD_RESULT_SAT
97  * BFNT_CHILD_RESULT_UNSAT
98  * BFNT_CHILD_RESULT_UNKNOWN
99  */
runChild(size_t modelSize)100 void BFNTMainLoop::runChild(size_t modelSize)
101 {
102   CALL("BFNTMainLoop::runChild");
103 
104   ScopedPtr<Problem> childPrb(_bfnt.createProblem(modelSize));
105 
106   ScopedPtr<MainLoop> childMainLoop(MainLoop::createFromOptions(*childPrb, _childOpts));
107   MainLoopResult innerRes = childMainLoop->run();
108   innerRes.updateStatistics();
109 
110   if(env.options->mode()!=Options::Mode::SPIDER) {
111     if(innerRes.terminationReason==Statistics::SATISFIABLE || innerRes.terminationReason==Statistics::TIME_LIMIT) {
112       env.beginOutput();
113       env.statistics->print(env.out());
114       env.endOutput();
115     }
116   }
117 
118   if(env.options->mode()!=Options::Mode::SPIDER && innerRes.terminationReason==Statistics::SATISFIABLE) {
119     env.beginOutput();
120     UIHelper::outputSatisfiableResult(env.out());
121     env.endOutput();
122     UIHelper::satisfiableStatusWasAlreadyOutput = true;
123   }
124 
125   switch(innerRes.terminationReason) {
126   case Statistics::SATISFIABLE:
127     exit(BFNT_CHILD_RESULT_SAT);
128   case Statistics::REFUTATION:
129     exit(BFNT_CHILD_RESULT_UNSAT);
130   default:
131     exit(BFNT_CHILD_RESULT_UNKNOWN);
132   }
133   exit(BFNT_CHILD_RESULT_UNKNOWN);
134 }
135 
136 
spawnChild(size_t modelSize)137 MainLoopResult BFNTMainLoop::spawnChild(size_t modelSize)
138 {
139   CALL("BFNTMainLoop::spawnChild");
140 
141   pid_t childPid=Multiprocessing::instance()->fork();
142 
143   if(!childPid) {
144     runChild(modelSize);
145     ASSERTION_VIOLATION;
146   }
147 
148 
149   System::ignoreSIGINT();
150 
151   int status;
152   errno=0;
153   pid_t res=waitpid(childPid, &status, 0);
154   if(res==-1) {
155     SYSTEM_FAIL("Error in waiting for forked process.",errno);
156   }
157 
158   System::heedSIGINT();
159 
160   Timer::syncClock();
161 
162   if(res!=childPid) {
163     INVALID_OPERATION("Invalid waitpid return value: "+Int::toString(res)+"  pid of forked Vampire: "+Int::toString(childPid));
164   }
165 
166   ASS(!WIFSTOPPED(status));
167 
168   if( (WIFSIGNALED(status) && WTERMSIG(status)==SIGINT) ||
169       (WIFEXITED(status) && WEXITSTATUS(status)==VAMP_RESULT_STATUS_SIGINT) )  {
170     //if the forked Vampire was terminated by SIGINT (Ctrl+C), we also terminate
171     //(3 is the return value for this case; see documentation for the
172     //@b vampireReturnValue global variable)
173 
174     raise(SIGINT);
175   }
176 
177   if(WIFEXITED(status)) {
178     switch(WEXITSTATUS(status)) {
179     case BFNT_CHILD_RESULT_SAT:
180       UIHelper::satisfiableStatusWasAlreadyOutput = true;
181       return MainLoopResult(Statistics::SATISFIABLE);
182     case BFNT_CHILD_RESULT_UNSAT:
183       return MainLoopResult(Statistics::REFUTATION);
184 
185     case VAMP_RESULT_STATUS_OTHER_SIGNAL:
186     case VAMP_RESULT_STATUS_UNHANDLED_EXCEPTION:
187       INVALID_OPERATION("error in the child process");
188 
189     case BFNT_CHILD_RESULT_UNKNOWN:
190     default: //under default will fall timeout
191       return MainLoopResult(Statistics::UNKNOWN);
192     }
193   }
194   else {
195     return MainLoopResult(Statistics::UNKNOWN);
196   }
197 }
198 
199 
runImpl()200 MainLoopResult BFNTMainLoop::runImpl()
201 {
202   CALL("BFNTMainLoop::runImpl");
203 
204   if(_hasSorts) {
205     return MainLoopResult(Statistics::UNKNOWN);
206   }
207 
208 
209   env.timer->makeChildrenIncluded();
210 
211   size_t modelSize = 1;
212   for(;;) {
213     Timer::syncClock();
214     if(env.timeLimitReached()) { return MainLoopResult(Statistics::TIME_LIMIT); }
215     env.statistics->maxBFNTModelSize = modelSize;
216     MainLoopResult childResult = spawnChild(modelSize);
217 
218     if(childResult.terminationReason == Statistics::SATISFIABLE) {
219       return childResult;
220     }
221 
222     modelSize++;
223   }
224 }
225 
226 }
227