SimPy PreemptiveResource Explained: Interrupting for Priority
PreemptiveResource is the aggressive sibling of PriorityResource. It doesn't just jump the queue—it kicks out whoever's currently using the resource.
PriorityResource vs PreemptiveResource
PriorityResource: "I'll wait, but put me first in line."
PreemptiveResource: "Move. Now."
import simpy
# Priority: high priority waits for current user to finish
priority_resource = simpy.PriorityResource(env, capacity=1)
# Preemptive: high priority interrupts current user
preemptive_resource = simpy.PreemptiveResource(env, capacity=1)
Basic Example
import simpy
def task(env, name, resource, priority, duration):
with resource.request(priority=priority) as req:
yield req
try:
print(f"{name} starts at {env.now}")
yield env.timeout(duration)
print(f"{name} finishes at {env.now}")
except simpy.Interrupt as interrupt:
print(f"{name} preempted at {env.now}")
env = simpy.Environment()
cpu = simpy.PreemptiveResource(env, capacity=1)
env.process(task(env, "LowPriority", cpu, priority=10, duration=10))
env.process(task(env, "HighPriority", cpu, priority=1, duration=3))
env.run()
Output:
LowPriority starts at 0
HighPriority starts at 0
LowPriority preempted at 0
HighPriority finishes at 3
The high-priority task immediately interrupts the low-priority one.
Handling Preemption
When preempted, simpy.Interrupt is raised. You must handle it:
def job(env, resource, priority):
with resource.request(priority=priority) as req:
yield req
try:
yield env.timeout(10)
except simpy.Interrupt:
# What happens when preempted?
print("I got kicked out!")
# Maybe try again, maybe give up
Without the try/except, preemption crashes your process.
Resuming After Preemption
Common pattern: resume where you left off:
def resilient_job(env, resource, priority, total_work):
remaining = total_work
while remaining > 0:
with resource.request(priority=priority) as req:
yield req
start = env.now
try:
yield env.timeout(remaining)
remaining = 0 # Completed
except simpy.Interrupt:
remaining -= (env.now - start) # Work done
print(f"Preempted, {remaining:.1f} remaining")
print(f"Completed at {env.now}")
The job keeps trying until all work is done, even if repeatedly preempted.
Real-World Examples
Machine Repair
def regular_job(env, machine, duration):
with machine.request(priority=2) as req:
yield req
try:
yield env.timeout(duration)
print(f"Job completed at {env.now}")
except simpy.Interrupt:
print(f"Job interrupted for repair at {env.now}")
def repair(env, machine):
with machine.request(priority=1) as req: # Higher priority
yield req
print(f"Repair starts at {env.now}")
yield env.timeout(5)
print(f"Repair ends at {env.now}")
def breakdown(env, machine):
yield env.timeout(3) # Break down at time 3
env.process(repair(env, machine))
env = simpy.Environment()
machine = simpy.PreemptiveResource(env, capacity=1)
env.process(regular_job(env, machine, 10))
env.process(breakdown(env, machine))
env.run()
Emergency Override
NORMAL = 10
EMERGENCY = 1
def use_resource(env, name, resource, priority):
with resource.request(priority=priority) as req:
yield req
try:
print(f"{name} using resource at {env.now}")
yield env.timeout(20)
print(f"{name} finished at {env.now}")
except simpy.Interrupt:
print(f"{name} yielded to emergency at {env.now}")
Preemption Order
Like PriorityResource, lower priority number = more important:
# Priority 1 preempts priority 5
# Priority 5 preempts priority 10
# Equal priorities: later arrival cannot preempt
Equal priorities don't preempt each other—that would cause infinite loops.
Preventing Preemption
Some requests shouldn't be preempted:
with resource.request(priority=5, preempt=False) as req:
yield req
# This won't be interrupted by higher priority
yield env.timeout(10)
preempt=False means "I respect priority order in the queue, but once I'm running, don't interrupt me."
Statistics with Preemption
Track preemptions:
stats = {'completed': 0, 'preempted': 0}
def job(env, resource, priority):
with resource.request(priority=priority) as req:
yield req
try:
yield env.timeout(10)
stats['completed'] += 1
except simpy.Interrupt:
stats['preempted'] += 1
# After simulation
total = stats['completed'] + stats['preempted']
print(f"Preemption rate: {stats['preempted']/total*100:.1f}%")
When to Use PreemptiveResource
Use when: - Emergency handling (machine breakdown repair) - Real-time systems with hard deadlines - Operating system CPU scheduling - Critical process must start immediately
Don't use when: - Work can't be resumed (preemption wastes it) - Fairness matters - Starvation of low priority is unacceptable - The real system doesn't actually preempt
The Danger of Preemption
Preemption can cause: - Wasted work (if preempted jobs restart from scratch) - Starvation (low priority never finishes) - Complexity (handling interrupts everywhere)
Use judiciously. Often PriorityResource is enough.
Comparison Table
| Feature | Resource | PriorityResource | PreemptiveResource |
|---|---|---|---|
| Queue order | FIFO | Priority | Priority |
| Interrupts | No | No | Yes |
| Requires interrupt handling | No | No | Yes |
| Fair | Yes | No | No |
Example: Operating System Scheduler
def process(env, name, cpu, priority, burst_time):
remaining = burst_time
while remaining > 0:
with cpu.request(priority=priority) as req:
yield req
start = env.now
try:
yield env.timeout(remaining)
remaining = 0
print(f"{name} finished at {env.now}")
except simpy.Interrupt:
remaining -= (env.now - start)
print(f"{name} preempted, {remaining:.1f} remaining")
env = simpy.Environment()
cpu = simpy.PreemptiveResource(env, capacity=1)
# Low priority background task
env.process(process(env, "Background", cpu, priority=10, burst_time=20))
# High priority arrives later
def spawn_high_priority(env, cpu):
yield env.timeout(5)
env.process(process(env, "HighPriority", cpu, priority=1, burst_time=5))
env.process(spawn_high_priority(env, cpu))
env.run()
Summary
PreemptiveResource: - Interrupts lower-priority current users - Requires try/except for interrupt handling - Useful for emergencies and real-time systems - Can cause starvation and wasted work
Preemption is powerful. Use it when the situation demands it.
Next Steps
Discover the Power of Simulation
Want to become a go-to expert in simulation with Python? The Complete Simulation Bootcamp will show you how simulation can transform your career and your projects.
Explore the Bootcamp