# Buggy code
def begin_grading(
    self, submission: Submission, report=logger.info, blocking=False
) -> None:
    # Ensure only one submission is running at a time; this lock is released at the end of submission grading.
    # This is necessary because `begin_grading` is "re-entrant"; after e.g. grading-end is sent, the network
    # thread may receive a new submission before the grading thread and worker from the *previous* submission
    # have finished tearing down. Trashing global state (e.g. `self.current_submission`) before then would be
    # an error.
    self._grading_lock.acquire()
    assert self.current_submission is None

    self.current_submission = submission
    ipc_ready_signal = threading.Event()
    grading_thread = threading.Thread(
        target=self._grading_thread_main,
        args=(ipc_ready_signal, submission, report),
        daemon=True,
    )
    grading_thread.start()

    ipc_ready_signal.wait()
    if blocking:
        grading_thread.join()


# Traceback (AttributeError)
INFO 2020-07-15 17:02:08,535 56 judge Start grading boi2019p3/2205852 in CPP11...
INFO 2020-07-15 17:02:08,540 56 packet Accept submission: 2205852: executor: CPP11, code: boi2019p3
INFO 2020-07-15 17:02:09,336 56 judge Batch #1
INFO 2020-07-15 17:02:09,346 56 judge   Test case  1 AC [0.002s (0.003s) | 5668kb]
INFO 2020-07-15 17:02:09,356 56 judge   Test case  2 AC [0.003s (0.004s) | 5348kb]
INFO 2020-07-15 17:02:09,357 56 judge Batch #2
INFO 2020-07-15 17:02:09,376 56 judge   Test case  3 AC [0.011s (0.012s) | 5672kb]
INFO 2020-07-15 17:02:09,395 56 judge   Test case  4 WA [0.011s (0.012s) | 5104kb]
INFO 2020-07-15 17:02:09,395 56 judge   Test case  5 --
INFO 2020-07-15 17:02:09,395 56 judge   Test case  6 --
INFO 2020-07-15 17:02:09,396 56 judge Batch #3
INFO 2020-07-15 17:02:09,409 56 judge   Test case  7 WA [0.006s (0.008s) | 5860kb]
INFO 2020-07-15 17:02:09,409 56 judge   Test case  8 --
INFO 2020-07-15 17:02:09,410 56 judge   Test case  9 --
INFO 2020-07-15 17:02:09,410 56 judge   Test case 10 --
INFO 2020-07-15 17:02:09,425 56 judge   Test case 11 --
INFO 2020-07-15 17:02:09,425 56 judge   Test case 12 --
INFO 2020-07-15 17:02:09,426 56 judge   Test case 13 --
INFO 2020-07-15 17:02:09,426 56 judge   Test case 14 --
INFO 2020-07-15 17:02:09,426 56 judge   Test case 15 --
INFO 2020-07-15 17:02:09,426 56 judge   Test case 16 --
INFO 2020-07-15 17:02:09,426 56 judge Batch #4
WARNING 2020-07-15 17:02:14,456 19004 tracer Shocker activated and killed 19036
WARNING 2020-07-15 17:02:14,456 19004 tracer Request the killing of process: 19036
INFO 2020-07-15 17:02:14,458 56 judge   Test case 17 TLE [5.000s (5.001s) | 12368kb] {RTE, WA}
INFO 2020-07-15 17:02:14,458 56 judge   Test case 18 --
INFO 2020-07-15 17:02:14,461 56 judge   Test case 19 --
INFO 2020-07-15 17:02:14,462 56 judge   Test case 20 --
INFO 2020-07-15 17:02:14,465 56 judge   Test case 21 --
INFO 2020-07-15 17:02:14,466 56 judge   Test case 22 --
INFO 2020-07-15 17:02:14,471 56 judge Batch #5
INFO 2020-07-15 17:02:14,478 56 packet Received abortion request for 2205852
WARNING 2020-07-15 17:02:14,479 19004 tracer Skipping the killing of process because it already exited: 19036
ERROR 2020-07-15 17:02:19,481 56 judge Worker is still alive, sending SIGKILL!
ERROR 2020-07-15 17:02:19,484 56 judge Failed to read IPC message from worker!
ERROR 2020-07-15 17:02:19,497 56 judge Traceback (most recent call last):
File "/judge/dmoj/judge.py", line 152, in _grading_thread_main
for ipc_type, data in self.current_judge_worker.communicate():
File "/judge/dmoj/judge.py", line 306, in communicate
ipc_type, data = self.worker_process_conn.recv()
File "/usr/lib/python3.8/multiprocessing/connection.py", line 250, in recv
buf = self._recv_bytes()
File "/usr/lib/python3.8/multiprocessing/connection.py", line 414, in _recv_bytes
buf = self._recv(4)
File "/usr/lib/python3.8/multiprocessing/connection.py", line 383, in _recv
raise EOFError
EOFError

ERROR 2020-07-15 17:02:19,628 56 packet Exception while reading packet from site, will not attempt to reconnect! Quitting judge.
Traceback (most recent call last):
File "/judge/dmoj/packet.py", line 156, in _read_forever
self._receive_packet(self._read_single())
File "/judge/dmoj/packet.py", line 276, in _receive_packet
log.info('Received abortion request for %s', self.judge.current_submission.id)
AttributeError: 'NoneType' object has no attribute 'id'

# Fixed code
def begin_grading(
    self, submission: Submission, report=logger.info, blocking=False
) -> None:
    # Ensure only one submission is running at a time; this lock is released at the end of submission grading.
    # This is necessary because `begin_grading` is "re-entrant"; after e.g. grading-end is sent, the network
    # thread may receive a new submission before the grading thread and worker from the *previous* submission
    # have finished tearing down. Trashing global state (e.g. `self.current_judge_worker`) before then would be
    # an error.
    self._grading_lock.acquire()
    with self._current_submission_lock:
        assert self.current_judge_worker is None

        report(
            ansi_style(
                "Start grading #ansi[%s](yellow)/#ansi[%s](green|bold) in %s..."
                % (submission.problem_id, submission.id, submission.language)
            )
        )

        self.current_judge_worker = JudgeWorker(submission)

        ipc_ready_signal = threading.Event()
        grading_thread = threading.Thread(
            target=self._grading_thread_main,
            args=(ipc_ready_signal, report),
            daemon=True,
        )
        grading_thread.start()

    ipc_ready_signal.wait()

    if blocking:
        grading_thread.join()