PreemptiveResource and mixed preemption is broken

Created by: sscherfke

#!python

import simpy


def user(name, env, res, prio, preempt):
    print('%s requesting at %d' % (name, env.now))
    with res.request(priority=prio, preempt=preempt) as req:
        try:
            # print(name, req)
            yield req
            print('%s got resource at %d' % (name, env.now))
            yield env.timeout(3)
        except simpy.Interrupt:
           print('%s got preempted at %d' % (name, env.now))
    print('%s done at %d' % (name, env.now))


env = simpy.Environment()
res = simpy.PreemptiveResource(env, capacity=1)
A = env.process(user('A', env, res, prio=0, preempt=True))
env.run(until=1)  # Give A a head start

B = env.process(user('B', env, res, prio=-2, preempt=False))
C = env.process(user('C', env, res, prio=-1, preempt=True))
env.run()

Got:

A requesting at 0
A got resource at 0
B requesting at 1
C requesting at 1
A done at 3
B got resource at 3
B done at 6
C got resource at 6
C done at 9

Expected a):

A requesting at 0
A got resource at 0
B requesting at 1
C requesting at 1
A got preempted at 1
A done at 1
C got resource at 1
C done at 4
B got resource at 4
B done at 7

or b):

A requesting at 0
A got resource at 0
B requesting at 1
C requesting at 1
A got preempted at 1
A done at 1
B got resource at 1
B done at 4
C got resource at 4
C done at 7

This doesn’t work because:

  • B requests the resource, the requests gets queued
  • _trigger_put() is called, B’s request doesn’t now want to preempt. Nothing happens.
  • C requests the resource, the requests ges queued behind B’s request
  • _trigger_put() is called
  • B’s request is examined, doesn not want to preempt. _trigger_put() returns because the request didn’t get triggered.
  • C’s request has no chance to preempt.

That’s why neither the expected output a) nor b) can be achieved.

Now, if we woudn’t break from the loop in _trigger_get() and also examine C’s request: C’s request would indeet preempt A’s request and remove it from the users. However, since we already skipped B’s request, C’s request would succeed although it is not as important as B’s. A way to fix this would be to iterate the put_queue twice (ugly). All this isn’t possible anyway because we break from the loop as soon as a request is not triggered.

So IMHO the only sensible way to fix PreemptiveResource would be to prohibit mixed preemption (set preempt always to True and remove the preempt arg from PriorityRequest.