인터럽트 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

"""여러 인터럽트를 처리하는 두 방식을 시간 단위로 흉내 낸다.

- sequential: 처리 중에는 인터럽트를 막는다. 막혀 있던 요청은 도착 순서대로 처리한다.
- nested: 우선순위가 더 높은 요청만 지금 처리 중인 루틴을 끊는다.
문서의 예(Stallings 그림 1.12): 프린터(우선순위 2) t=10, 통신(5) t=15, 디스크(4) t=20,
각 처리 루틴은 10단위 걸린다.
"""


def simulate(requests, mode, end=60):
    """requests: [(이름, 우선순위, 도착 시각, 처리 시간)]. 시각마다 누가 CPU를 쓰는지 구간으로 돌려준다."""
    remaining = {name: dur for name, _, _, dur in requests}
    prio = {name: p for name, p, _, _ in requests}
    arrive = {name: t for name, _, t, _ in requests}
    stack = []          # 끊긴 루틴들 (중첩 방식)
    pending = []        # 도착했지만 아직 시작 못 한 요청
    current = "user"
    timeline = []
    for t in range(end):
        pending += [n for n, _, a, _ in requests if a == t]
        if mode == "nested":
            # 지금보다 우선순위가 높은 대기 요청이 있으면 지금 것을 스택에 넣고 그쪽으로 간다
            best = max(pending, key=lambda n: prio[n], default=None)
            cur_p = prio.get(current, 0)
            if best is not None and prio[best] > cur_p:
                stack.append(current)
                pending.remove(best)
                current = best
        else:
            if current == "user" and pending:
                pending.sort(key=lambda n: arrive[n])
                stack.append(current)
                current = pending.pop(0)
        timeline.append(current)
        if current != "user":
            remaining[current] -= 1
            if remaining[current] == 0:
                # 루틴이 끝나면 저장해 둔 상태를 꺼내 돌아간다
                current = stack.pop()
    # 같은 이름이 이어지는 구간으로 묶는다
    spans, start = [], 0
    for t in range(1, end + 1):
        if t == end or timeline[t] != timeline[start]:
            spans.append((start, t, timeline[start]))
            start = t
    return spans


REQ = [("printer", 2, 10, 10), ("comm", 5, 15, 10), ("disk", 4, 20, 10)]

if __name__ == "__main__":
    nested = simulate(REQ, "nested", 50)
    print("nested    ", nested)
    assert nested == [(0, 10, "user"), (10, 15, "printer"), (15, 25, "comm"),
                      (25, 35, "disk"), (35, 40, "printer"), (40, 50, "user")]
    seq = simulate(REQ, "sequential", 50)
    print("sequential", seq)
    assert seq == [(0, 10, "user"), (10, 20, "printer"), (20, 30, "comm"),
                   (30, 40, "disk"), (40, 50, "user")]
    # 통신 요청이 처리 시작까지 기다린 시간: 중첩 0, 순차 5
    assert 15 - 15 == 0 and 20 - 15 == 5

    # 카드 C5: 디스크(4) t=5 처리 8, 프린터(2) t=7 처리 4, 통신(5) t=9 처리 3
    c5 = simulate([("disk", 4, 5, 8), ("printer", 2, 7, 4), ("comm", 5, 9, 3)], "nested", 25)
    print("card C5   ", c5)
    assert c5 == [(0, 5, "user"), (5, 9, "disk"), (9, 12, "comm"), (12, 16, "disk"),
                  (16, 20, "printer"), (20, 25, "user")]
    print("ALL CHECKS PASSED")