@@ -8,29 +8,22 @@ use self::{
88 compiler:: { BuildOutcome , Compiler } ,
99 exec_test:: { exec, ExecRequest } ,
1010} ;
11- use judging_apis:: Status ;
11+ use anyhow:: Context ;
12+ use judging_apis:: { valuer_proto:: ValuerResponse , Status } ;
1213use serde:: { Deserialize , Serialize } ;
1314use std:: {
1415 borrow:: Cow ,
1516 future:: Future ,
1617 path:: { Path , PathBuf } ,
1718 pin:: Pin ,
1819} ;
19- use tracing:: debug;
20-
20+ use tracing:: { debug, error, instrument} ;
2121/// Allows sending requests to invoker
2222// TODO upstream BoxedEngine to jjs-commons
2323pub type InvokerClient = rpc:: Client < rpc:: BoxEngine > ;
2424
25- pub ( crate ) struct JudgeContext {
26- /// Can be used to send requests to invoker
27- pub ( crate ) invoker : InvokerClient ,
28- /// Channel that should be used for sending updates
29- pub ( crate ) events_tx : async_channel:: Sender < Event > ,
30- }
31-
3225/// Note: this is not `judging_apis::invoke::Command`, it is higher-level.
33- #[ derive( Debug ) ]
26+ #[ derive( Debug , Default ) ]
3427pub ( crate ) struct Command {
3528 pub argv : Vec < String > ,
3629 pub env : Vec < String > ,
@@ -100,3 +93,182 @@ pub(crate) enum JudgeOutcome {
10093 /// Maybe, several protocols were emitted, but results are neither precise nor complete
10194 Fault ,
10295}
96+
97+ pub ( crate ) struct JudgeContext {
98+ /// Can be used to send requests to invoker
99+ pub ( crate ) invoker : InvokerClient ,
100+ /// Channel that should be used for sending updates
101+ pub ( crate ) events_tx : async_channel:: Sender < Event > ,
102+ }
103+
104+ #[ instrument( skip( cx, judge_req) , fields( id = %judge_req. judge_request_id) ) ]
105+ pub fn do_judge ( mut cx : JudgeContext , judge_req : LoweredJudgeRequest ) {
106+ debug ! ( "Got LoweredJudgeRequest: {:?}" , & judge_req) ;
107+ tokio:: task:: spawn ( async move {
108+ let outcome = match cx. judge ( & judge_req) . await {
109+ Ok ( o) => o,
110+ Err ( err) => {
111+ error ! ( "Invoke failed: {:#}" , err) ;
112+ cx. create_fake_protocols (
113+ & judge_req,
114+ & judging_apis:: Status {
115+ kind : judging_apis:: StatusKind :: InternalError ,
116+ code : judging_apis:: status_codes:: JUDGE_FAULT . to_string ( ) ,
117+ } ,
118+ )
119+ . await
120+ . ok ( ) ;
121+ JudgeOutcome :: Fault
122+ }
123+ } ;
124+ debug ! ( "JudgeOutcome: {:?}" , & outcome) ;
125+ cx. events_tx . send ( Event :: JudgeDone ( outcome) ) . await ;
126+ } ) ;
127+ }
128+
129+ impl JudgeContext {
130+ async fn judge ( & mut self , req : & LoweredJudgeRequest ) -> anyhow:: Result < JudgeOutcome > {
131+ let compiler = Compiler { req } ;
132+
133+ if !req. run_source . exists ( ) {
134+ anyhow:: bail!( "Run source file not exists" ) ;
135+ }
136+
137+ if !req. out_dir . exists ( ) {
138+ anyhow:: bail!( "Run output dir not exists" ) ;
139+ }
140+
141+ let compiler_response = compiler. compile ( ) ;
142+
143+ let outcome;
144+
145+ match compiler_response {
146+ Err ( err) => return Err ( err) . context ( "compilation error" ) ,
147+ Ok ( BuildOutcome :: Error ( st) ) => {
148+ self . create_fake_protocols ( req, & st) . await ?;
149+ outcome = JudgeOutcome :: CompileError ( st) ;
150+ }
151+ Ok ( BuildOutcome :: Success ) => {
152+ self . run_tests ( req) . await . context ( "failed to run tests" ) ?;
153+
154+ outcome = JudgeOutcome :: TestingDone ;
155+ }
156+ } ;
157+ Ok ( outcome)
158+ }
159+
160+ /// Used when we are unable to produce protocols, i.e. on compilation errors
161+ /// and judge faults.
162+ async fn create_fake_protocols (
163+ & mut self ,
164+ req : & LoweredJudgeRequest ,
165+ status : & judging_apis:: Status ,
166+ ) -> anyhow:: Result < ( ) > {
167+ for kind in judging_apis:: judge_log:: JudgeLogKind :: list ( ) {
168+ let pseudo_valuer_proto = judging_apis:: valuer_proto:: JudgeLog {
169+ kind,
170+ tests : vec ! [ ] ,
171+ subtasks : vec ! [ ] ,
172+ score : 0 ,
173+ is_full : false ,
174+ } ;
175+ let mut protocol = self . process_judge_log ( & pseudo_valuer_proto, req, & [ ] ) ?;
176+ protocol. status = status. clone ( ) ;
177+ self . put_protocol ( req, protocol) . await ?;
178+ }
179+ Ok ( ( ) )
180+ }
181+
182+ async fn put_outcome (
183+ & mut self ,
184+ score : u32 ,
185+ status : judging_apis:: Status ,
186+ kind : judging_apis:: judge_log:: JudgeLogKind ,
187+ ) {
188+ let header = judging_apis:: JudgeOutcomeHeader {
189+ score : Some ( score) ,
190+ status,
191+ kind,
192+ } ;
193+ self . events_tx . send ( Event :: OutcomeHeader ( header) ) . await . ok ( ) ;
194+ }
195+
196+ async fn put_protocol (
197+ & mut self ,
198+ req : & LoweredJudgeRequest ,
199+ protocol : judging_apis:: judge_log:: JudgeLog ,
200+ ) -> anyhow:: Result < ( ) > {
201+ let protocol_file_name = format ! ( "protocol-{}.json" , protocol. kind. as_str( ) ) ;
202+ let protocol_path = req. out_dir . join ( protocol_file_name) ;
203+ debug ! ( "Writing protocol to {}" , protocol_path. display( ) ) ;
204+ let protocol_file = std:: fs:: File :: create ( & protocol_path) ?;
205+ let protocol_file = std:: io:: BufWriter :: new ( protocol_file) ;
206+ serde_json:: to_writer ( protocol_file, & protocol)
207+ . context ( "failed to write judge log to file" ) ?;
208+ self . put_outcome ( protocol. score , protocol. status , protocol. kind )
209+ . await ;
210+ Ok ( ( ) )
211+ }
212+
213+ async fn run_tests ( & mut self , req : & LoweredJudgeRequest ) -> anyhow:: Result < ( ) > {
214+ let mut test_results = vec ! [ ] ;
215+
216+ let mut valuer = valuer:: Valuer :: new ( req) . context ( "failed to init valuer" ) ?;
217+ valuer
218+ . write_problem_data ( req)
219+ . await
220+ . context ( "failed to send problem data" ) ?;
221+ loop {
222+ match valuer. poll ( ) . await ? {
223+ ValuerResponse :: Test { test_id : tid, live } => {
224+ if live {
225+ self . events_tx . send ( Event :: LiveTest ( tid. get ( ) ) ) . await ;
226+ }
227+ let tid_u32: u32 = tid. into ( ) ;
228+ let test = & req. problem . tests [ ( tid_u32 - 1u32 ) as usize ] ;
229+ let exec_request = ExecRequest {
230+ test,
231+ test_id : tid. into ( ) ,
232+ } ;
233+
234+ let judge_response = exec_test:: exec ( & req, exec_request, self )
235+ . with_context ( || format ! ( "failed to judge solution on test {}" , tid) ) ?;
236+ test_results. push ( ( tid, judge_response. clone ( ) ) ) ;
237+ valuer
238+ . notify_test_done ( judging_apis:: valuer_proto:: TestDoneNotification {
239+ test_id : tid,
240+ test_status : judge_response. status ,
241+ } )
242+ . await
243+ . with_context ( || {
244+ format ! ( "failed to notify valuer that test {} is done" , tid)
245+ } ) ?;
246+ }
247+ ValuerResponse :: Finish => {
248+ break ;
249+ }
250+ ValuerResponse :: LiveScore { score } => {
251+ self . events_tx . send ( Event :: LiveScore ( score) ) . await ;
252+ }
253+ ValuerResponse :: JudgeLog ( judge_log) => {
254+ let converted_judge_log = self
255+ . process_judge_log ( & judge_log, req, & test_results)
256+ . context ( "failed to convert valuer judge log to invoker judge log" ) ?;
257+ self . put_protocol ( req, converted_judge_log)
258+ . await
259+ . context ( "failed to save protocol" ) ?;
260+ }
261+ }
262+ }
263+
264+ Ok ( ( ) )
265+ }
266+
267+ /// Creates `InputSource` that can be further sent to invoker
268+ async fn intern ( & self , data : & [ u8 ] ) -> anyhow:: Result < judging_apis:: invoke:: InputSource > {
269+ // TODO: optimize
270+ Ok ( judging_apis:: invoke:: InputSource :: Inline {
271+ data : data. to_vec ( ) ,
272+ } )
273+ }
274+ }
0 commit comments