|
1 | 1 | <?php declare(strict_types=1); |
| 2 | + |
| 3 | +require_once __DIR__ . '/../vendor/autoload.php'; |
2 | 4 | /** |
3 | 5 | * Requests a batch of judge tasks the domserver, executes them and reports |
4 | 6 | * the results back to the domserver. |
|
13 | 15 | require(ETCDIR . '/judgehost-config.php'); |
14 | 16 | require(LIBDIR . '/lib.misc.php'); |
15 | 17 |
|
| 18 | +use GuzzleHttp\Client; |
| 19 | +use GuzzleHttp\Handler\CurlMultiHandler; |
| 20 | +use GuzzleHttp\HandlerStack; |
| 21 | + |
16 | 22 | $endpoints = []; |
17 | 23 | $domjudge_config = []; |
18 | 24 |
|
@@ -537,7 +543,7 @@ function fetch_executable_internal( |
537 | 543 | return [$execrunpath, null, null]; |
538 | 544 | } |
539 | 545 |
|
540 | | -$options = getopt("dv:n:hVe:j:t:", ["diskspace-error"]); |
| 546 | +$options = getopt("dv:n:hV", ["diskspace-error"]); |
541 | 547 | // We can't fully trust the output of getopt, it has outstanding bugs: |
542 | 548 | // https://bugs.php.net/search.php?cmd=display&search_for=getopt&x=0&y=0 |
543 | 549 | if ($options===false) { |
@@ -658,39 +664,7 @@ function fetch_executable_internal( |
658 | 664 |
|
659 | 665 | read_credentials(); |
660 | 666 |
|
661 | | -if (!empty($options['e'])) { |
662 | | - $endpointID = $options['e']; |
663 | | - $endpoint = $endpoints[$endpointID]; |
664 | | - $endpoints[$endpointID]['ch'] = setup_curl_handle($endpoint['user'], $endpoint['pass']); |
665 | | - $new_judging_run = (array) dj_json_decode(base64_decode(file_get_contents($options['j']))); |
666 | | - $judgeTaskId = $options['t']; |
667 | | - |
668 | | - $success = false; |
669 | | - for ($i = 0; $i < 5; $i++) { |
670 | | - if ($i > 0) { |
671 | | - $sleep_ms = 100 + random_int(200, ($i+1)*1000); |
672 | | - dj_sleep(0.001 * $sleep_ms); |
673 | | - } |
674 | | - $response = request( |
675 | | - sprintf('judgehosts/add-judging-run/%s/%s', $new_judging_run['hostname'], |
676 | | - urlencode((string)$judgeTaskId)), |
677 | | - 'POST', |
678 | | - $new_judging_run, |
679 | | - false |
680 | | - ); |
681 | | - if ($response !== null) { |
682 | | - logmsg(LOG_DEBUG, "Adding judging run result for jt$judgeTaskId successful."); |
683 | | - $success = true; |
684 | | - break; |
685 | | - } |
686 | | - logmsg(LOG_WARNING, "Failed to report jt$judgeTaskId in attempt #" . ($i + 1) . "."); |
687 | | - } |
688 | | - if (!$success) { |
689 | | - error("Final attempt of uploading jt$judgeTaskId was unsuccessful, giving up."); |
690 | | - } |
691 | | - unlink($options['j']); |
692 | | - exit(0); |
693 | | -} |
| 667 | + |
694 | 668 |
|
695 | 669 | // Set umask to allow group and other access, as this is needed for the |
696 | 670 | // unprivileged user. |
@@ -729,7 +703,14 @@ function fetch_executable_internal( |
729 | 703 | $endpointIDs = array_keys($endpoints); |
730 | 704 | $currentEndpoint = 0; |
731 | 705 | $lastWorkdir = null; |
| 706 | + |
| 707 | +// Set up Guzzle client used for async progress reporting. |
| 708 | +$guzzleHandler = new CurlMultiHandler(); |
| 709 | +$handlerStack = HandlerStack::create($guzzleHandler); |
| 710 | +$guzzleClient = new Client(['handler' => $handlerStack, 'timeout' => 30]); |
| 711 | + |
732 | 712 | while (true) { |
| 713 | + $guzzleHandler->tick(); // process async requests in case there are any |
733 | 714 | // If all endpoints are waiting, sleep for a bit. |
734 | 715 | $dosleep = true; |
735 | 716 | foreach ($endpoints as $id => $endpoint) { |
@@ -1026,6 +1007,7 @@ function fetch_executable_internal( |
1026 | 1007 | } |
1027 | 1008 | break; |
1028 | 1009 | } |
| 1010 | + $guzzleHandler->tick(); // process async requests in case there are any |
1029 | 1011 | } |
1030 | 1012 |
|
1031 | 1013 | file_put_contents($success_file, $expected_uuid_pid); |
@@ -1349,7 +1331,7 @@ function compile( |
1349 | 1331 |
|
1350 | 1332 | function judge(array $judgeTask): bool |
1351 | 1333 | { |
1352 | | - global $EXITCODES, $myhost, $options, $workdirpath, $exitsignalled, $gracefulexitsignalled, $endpointID; |
| 1334 | + global $EXITCODES, $myhost, $options, $workdirpath, $exitsignalled, $gracefulexitsignalled, $endpointID, $guzzleClient, $endpoints; |
1353 | 1335 | $startTime = microtime(true); |
1354 | 1336 |
|
1355 | 1337 | $compile_config = dj_json_decode($judgeTask['compile_config']); |
@@ -1593,17 +1575,26 @@ function judge(array $judgeTask): bool |
1593 | 1575 |
|
1594 | 1576 | $ret = true; |
1595 | 1577 | if ($result === 'correct') { |
1596 | | - // Post result back asynchronously. PHP is lacking multi-threading, so |
1597 | | - // we just call ourselves again. |
1598 | | - $tmpfile = tempnam(TMPDIR, 'judging_run_'); |
1599 | | - file_put_contents($tmpfile, base64_encode(dj_json_encode($new_judging_run))); |
1600 | | - $judgedaemon = BINDIR . '/judgedaemon'; |
1601 | | - $cmd = $judgedaemon |
1602 | | - . ' -e ' . $endpointID |
1603 | | - . ' -t ' . $judgeTask['judgetaskid'] |
1604 | | - . ' -j ' . $tmpfile |
1605 | | - . ' >> /dev/null & '; |
1606 | | - shell_exec($cmd); |
| 1578 | + // Post result back asynchronously. |
| 1579 | + $url = sprintf('%s/judgehosts/add-judging-run/%s/%s', $endpoints[$endpointID]['url'], urlencode($myhost), urlencode((string)$judgeTask['judgetaskid'])); |
| 1580 | + $auth = [$endpoints[$endpointID]['user'], $endpoints[$endpointID]['pass']]; |
| 1581 | + |
| 1582 | + $multipart_data = []; |
| 1583 | + foreach ($new_judging_run as $key => $value) { |
| 1584 | + $multipart_data[] = [ |
| 1585 | + 'name' => $key, |
| 1586 | + 'contents' => $value |
| 1587 | + ]; |
| 1588 | + } |
| 1589 | + |
| 1590 | + $promise = $guzzleClient->postAsync($url, [ |
| 1591 | + 'auth' => $auth, |
| 1592 | + 'multipart' => $multipart_data |
| 1593 | + ]); |
| 1594 | + |
| 1595 | + $promise->then(null, function ($reason) { |
| 1596 | + logmsg(LOG_WARNING, "Async submission failed: " . $reason->getMessage()); |
| 1597 | + }); |
1607 | 1598 | } else { |
1608 | 1599 | // This run was incorrect, only continue with the remaining judge tasks |
1609 | 1600 | // if we are told to do so. |
|
0 commit comments