Skip to content

Communication (aka run-twice) Support Implementation - #42

Open
Student-FastDev wants to merge 7 commits into
varqox:mainfrom
Student-FastDev:communication
Open

Student-FastDev wants to merge 7 commits into
varqox:mainfrom
Student-FastDev:communication

Conversation

@Student-FastDev

Copy link
Copy Markdown

This adds communication problems for #40. A submission can run several times within one test, with a problem manager deciding what each execution receives. Executions can also run concurrently. Contestants use ordinary stdin/stdout.

For an encoder/decoder task, the manager runs the encoder, checks its output against the channel restrictions, and passes the permitted encoding to a fresh decoder. It compares the decoded answer with the private input and returns the test result. The same runner supports interactive phases and participants that keep running across several turns.

02-two-runs

Package settings

Communication mode is configured in the ordinary Simfile:

communication: true
manager: manager/manager.cc
instances: 32
min_instances: 2
max_concurrent: 8
memory_limit: 128
Setting Meaning Default / limits
communication Selects the communication runner Absent or false retains the existing mode
manager Manager source inside manager/ Required
instances Maximum executions reserved or started per test 2; from 1 through 32
min_instances Minimum completed executions for acceptance Equal to instances when omitted
max_concurrent Maximum launched, unfinished executions at once 1; at most 8 and at most instances

For a two-run task, the only communication-specific settings needed are communication: true and the manager path. The remaining limits default to two executions, both required, with one running at a time. A communication package cannot specify a legacy checker or contain checker sources under check/ or checker/.

These settings bound the whole package. The manager chooses the actual count and schedule for each test, using its private input or the task's rules. The example permits two sequential runs on one test, eight simultaneous participants on another, and 32 runs in waves of four on another. The manager must check that each test's required work was completed.

03-schedules

Processes

01-processes

JudgeWorker compiles the contestant and manager through the existing language suites. PreparedProgram owns the executable or script and its seccomp filter. Each execution uses the language-specific runner and a fresh sandbox supervisor. Compiled submissions are reused across executions without recompilation.

The manager receives private input as read-only /in. Its stdout carries framed commands to the host session runner, its stdin receives events, and its stderr carries the final checker result. Contestant stdout is always data. Contestants receive only the input selected by the manager, and have no direct pipes to each other.

The host owns the pipes and temporary spools. It polls manager commands, contestant output, startup, and termination independently. Bounded spooling lets the runner drain a writer while another participant waits to write.

Batch execution reserves an ID, accepts all input, then starts on commit. The stdin is a read-only file ending at EOF. The runner drains output privately and releases it to the manager after successful termination. Ordinary buffered solutions therefore work without interactive flushes.

Streaming execution starts immediately. The manager can send input during execution and close it when EOF is required. Output arrives incrementally while the contestant runs. Batch and streaming executions can share a session. If a streaming execution exits early, queued input that can no longer be delivered is discarded but still counts against the limits.

IDs begin at 1, increase consecutively, and cannot be reused. A batch reservation counts towards instances before commit; it occupies a concurrency slot only when committed and launched. The manager must wait for Ended before reusing a concurrency slot. OutputClosed reports EOF on stdout. Ended requires successful termination and complete output drainage. A contestant failure aborts through the runner's verdict path.

Each participant has separate namespaces and a cgroup, with the existing sandbox's capability dropping, no_new_privs, descriptor closure, read-only runtime mounts, and language-specific seccomp filter. Managers and contestants have a one-process/thread limit. Fresh execution adds opt-in startup events, nonblocking supervisor requests, signal-mask reset, and parent-owned cgroup cleanup. Error and cancellation paths kill and reap supervisors and remove their cgroups.

Writing a manager

SIP supplies templates/communication_manager.cc as a self-contained C++ source, with a lowercase-message encoder/decoder example. It requires no additional link library. run_batch() runs one batch execution at a time and either returns a string or delivers output chunks to a callback. start_stream(), send(), close_input(), and receive() provide streaming operation.

The helper handles framing. Problem authors implement channel validation, query budgets, turn rules, and answer checking. A ten-bit channel requires the manager to validate and, where needed, canonicalize those ten bits before forwarding them. SIM's transport limits do not enforce task-specific channel restrictions. Concurrent managers must dispatch interleaved events by instance ID, handle fragmented output, and wait for all successful Ended events. The stock run_batch() helper expects events for its own run. Overlapping it with another execution requires adapting that event loop.

Manager wire interface

The manager runs with --sim-communication /in. The 1 in Hello identifies the wire format; it is not a Simfile setting. Each frame starts with three unsigned 32-bit little-endian fields: type, instance ID, and payload length. The 12-byte header and payload may span reads or writes. Payloads allow arbitrary bytes, including NUL bytes, up to 65,536 bytes.

Command Type ID / payload
Hello 1 ID 0; integer 1
PrepareBatch 2 Next unused ID; empty
BatchInput 3 Reserved batch ID; bytes
CommitBatch 4 Reserved batch ID; empty
Start 5 Next unused ID; empty
Input 6 Streaming ID; bytes
CloseInput 7 Streaming ID; empty
Finish 8 ID 0; one byte, 1 for accepted or 0 for wrong
Event Type Payload
Hello 101 Three integers: version, execution limit, concurrency limit
Started 102 Empty; sandbox setup finished, before program execution
Output 103 Output bytes
OutputClosed 104 Empty; EOF on stdout
Ended 105 Empty; successful termination and drained output

Hello must come first. Instance events have nonzero IDs. Bytes retain their order within an instance. Managers must flush commands and reserve stdout for frames.

Verdicts, limits, and scoring

Acceptance requires every reserved or started execution to finish successfully, with the count between min_instances and instances. Uncommitted reservations and unfinished runs prevent acceptance. The manager sends Finish with payload byte 1, writes the existing checker result format to stderr, and exits normally:

OK
100
Optional public comment

The percentage permits partial credit. For early rejection, the manager sends Finish with payload byte 0 and uses WRONG as the first line of the checker result on stderr. SIM cancels outstanding runs. Finish and stderr must agree, and no commands may follow Finish. Comments pass through the bounded checker-output parser and must not disclose private input.

A manager cannot accept a contestant that exceeded a limit or exited abnormally. Invalid frames or lifecycle operations, manager crashes, and missing or contradictory results produce checker errors. Cancellation after early rejection is recorded separately. Each test still produces one result using existing group scoring and submission status logic. Standard time and memory fields report the maximum individual usage.

Resource Default bound
Manager CPU / memory in JudgeWorker 10 seconds / 256 MiB
Input / output per contestant 64 MiB / 64 MiB
Cumulative session spool data 256 MiB
Session memory admission 2 GiB
Relay memory reservation 16 MiB
Startup allowance per sandbox start 5 seconds

Contestants each receive the test's CPU and memory limits. Memory admission adds maximum concurrency times contestant memory, manager memory, the relay reservation, and an accounting page per sandbox. Conversion checks the standard budget before calibration; the worker checks its actual manager limit. The 2 GiB budget is an admission check. Each process has its own cgroup enforcing its individual limit. Spool accounting is cumulative, including framed or forwarded copies. Manager control input is also bounded. Operators must provision temporary storage for their worker pool.

Batch runs keep their individual wall deadlines. Streaming runs may wait for the manager or other participants within this session allowance:

instances * contestant wall allowance
+ manager wall allowance
+ (instances + 1) * startup allowance

The calculation uses the configured maximum count, even on smaller tests. Individual CPU limits still apply. Session expiry with an occupied concurrency slot produces TLE; otherwise it produces a checker error.

Calibration requires a complete, full-score model result on every test. Authors can use sip test --keep-limits to inspect partial scores without changing limits.

SIP workflow

sip communication init example "Example problem"
cd example
# Add a model under prog/, private inputs under in/, and recipes in Sipfile.
sip gen
sip test
sip test model --keep-limits
sip pack

sip communication init writes the settings and manager template, refusing to overwrite an existing Simfile or manager/manager.cc. sip gen and sip regen generate private inputs without .out files. sip genout is rejected for communication packages. sip test runs communication sessions and normally calibrates the model. --keep-limits disables calibration, works before or after solution patterns, and leaves the Simfile unchanged. sip pack validates before using the existing ZIP workflow.

The aliases sip communication test, sip communication gen, sip communication regen, and sip communication pack require communication mode.

Existing installations

The C++ ExecutionFormat enum has Legacy = 0 and Communication = 1. Legacy covers existing batch and interactive tasks. The upgrader adds the numeric problems.execution_format column with DEFAULT 0 and ALGORITHM=INSTANT, so existing problems need no per-row update or package rewrite.

The upgrader accepts the current baseline and the preceding schema missing the submission full_status index, applying that existing index change when needed.

Existing batch and interactive judge implementations keep their execution paths. Existing language-suite calls retain their runner functions and request options.

Closes #40.

Copilot AI balanced review requested due to automatic review settings October 18, 2026 12:55

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@Student-FastDev

Copy link
Copy Markdown
Author

I've attached an example package for Licytacje (LIC) from the XXXIII Polish Olympiad in Informatics, Finals, Day 1.
The problem runs two copies of the same submission concurrently, one for each player. Each copy receives different data, and they communicate by exchanging bids through the manager. I adapted the library interface to use stdin and stdout.

licytacje.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Communication (aka run-twice) Support Request

2 participants