Calculate Completion Time for a Single-Server Reactor Queue
Hudson River TradingSoftware Engineer · Technical ScreenLast updated
Simulate a single-threaded reactor queue with a bounded buffer to compute the completion timestamp or drop status for each incoming task.
Problem Statement
In a low-latency trading system, an event reactor processes incoming market data packets on a single dedicated core. Packets arrive sequentially and must be processed without preemption.
You are given an integer buffer_size representing the maximum number of tasks that can wait in the incoming queue at any given moment (excluding any task currently being executed by the server). You are also given a list of tasks, where each task is represented as a pair (arrival_time, duration):
arrival_time: The timestamp at which the task arrives at the reactor.duration: The amount of processing time the task requires on the server.
Rules of the reactor:
- The server can only process one task at a time. Once started, a task runs to completion without interruption.
- When a task arrives:
- Any tasks that have already completed by or at
arrival_timeare removed from the system. - If the server is idle, the task begins processing immediately.
- If the server is busy, the task enters the waiting buffer if the number of currently waiting tasks is strictly less than
buffer_size. - If the waiting buffer is full (i.e., currently holds
buffer_sizewaiting tasks), the incoming task is dropped immediately.
- Any tasks that have already completed by or at
- Tasks in the buffer are served in First-Come, First-Served (FCFS) order. Tasks arriving at the same timestamp are evaluated in the order they appear in the input list.
Return a list of integers where the i-th element is the timestamp at which task i finishes execution, or -1 if task i was dropped.
Function Signature
def calculate_completion_times(buffer_size: int, tasks: list[tuple[int, int]]) -> list[int]:
pass
Input & Output Format
- Input:
buffer_size: An integer(0 <= buffer_size <= 10^5).tasks: A list of tuples(arrival_time, duration)where0 <= arrival_time <= 10^9and1 <= duration <= 10^9. Tasks are ordered such thatarrival_time[i] <= arrival_time[i+1].
- Output: A list of integers of length
len(tasks)containing the completion time of each task or-1.
Examples
Example 1
Input: buffer_size = 1, tasks = [(1, 5), (2, 4), (3, 3), (7, 2)]
Output: [6, 10, -1, 12]
Explanation:
- Task 0 arrives at
t = 1, starts immediately, and finishes att = 1 + 5 = 6. - Task 1 arrives at
t = 2. The server is busy until6. The buffer is empty, so Task 1 enters the buffer. (Buffer size: 1/1) - Task 2 arrives at
t = 3. Server is busy until6, and buffer already has 1 task. Task 2 is dropped (-1). - At
t = 6, Task 0 finishes. Task 1 starts immediately and finishes att = 6 + 4 = 10. Buffer is now empty. - Task 3 arrives at
t = 7. Server is busy until10. Buffer is empty, so Task 3 enters buffer. Att = 10, Task 3 starts and finishes att = 10 + 2 = 12.
Example 2
Input: buffer_size = 0, tasks = [(0, 3), (2, 2), (3, 1)]
Output: [3, -1, 4]
Explanation:
- Task 0 arrives at
0and finishes at3. - Task 1 arrives at
2, server is busy, buffer capacity is 0, dropped (-1). - Task 2 arrives at
3. Task 0 finishes exactly at3, so the server becomes idle. Task 2 starts immediately and finishes at3 + 1 = 4.
Edge Cases
buffer_size = 0: Tasks must find the server idle upon arrival, or they are dropped.- Empty
taskslist: Return[]. - Multiple tasks arriving at the exact same timestamp.
- Large gaps between task arrivals where the server remains idle.
Hints
Hint 1
Maintain a queue of completion times of tasks currently accepted (both the currently executing task and those waiting in the buffer).
Hint 2
Before processing an incoming task at arrival_time, evict all completed tasks whose finish times are less than or equal to arrival_time.
Hint 3
If the queue size after eviction exceeds buffer_size (accounting for the currently running task), the new task must be dropped; otherwise, its start time is max(arrival_time, finish time of previous task).
Follow-up questions
- How would you adapt the solution if tasks have priority levels and higher-priority tasks can preempt lower-priority tasks?
- If tasks have hard deadlines where a task in the queue is dropped if its processing cannot start before its deadline, how would the data structure change?
- How would you scale this to a multi-threaded reactor with M identical worker cores processing tasks from a shared bounded queue?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.