resuming a timeout after a trap

58 views
Skip to first unread message

Erik Brakkee

unread,
Feb 9, 2019, 2:32:47 PM2/9/19
to simmer-devel
Hi, 

There is a nice example on in the Bank Tutorial II (https://r-simmer.org/articles/simmer-04-bank-2.html) where a signal is handled while doing a timeout. The intention is to continue after handling the trap using the same timeout as before. The solution used is to record the start time and timeout value in attributes and use these to compute the time left for a new timeout() call at the end of handling the trap. 

However, I am looking at a problem where the bank closes and before closing it sends a signal. The aim of the trap is to increase the priority of the arrival and then to continue with the timeout. However, in my case the resource is also preemptible so when subtracting the current time from the original end time (start_time + delay), end time might be too low since the arrival could have been preempted earlier. 

Example: 
  • arrive at 17:00, timeout is 2 hours
  • preemption by other arrival from 17:30-17:45
  • bank closes at 18:00 -> signal is sent. 
  • trap computes remaining delay as  17:00 + 2 hours - 18:00 = 1 hour. This is incorrect since only 45 minutes were spent so the remaining time is 1:45. 
Is there a way to get the current activity time in a trajectory? This way a similar computation can be done using 
activity times and I can write an interuptible timeout method

Cheers
  Erik 

Erik Brakkee

unread,
Feb 9, 2019, 2:40:55 PM2/9/19
to simmer-devel
Below is some code. I want to achieve the behavior that when the depot closes, arrivals will still continue. However, I want them to be preemptible during opening hours. This is why I increase the priority as part of the trap. 

Also, I think I might have found an issue (see the line marked "POSSIBLE BUG" below). When I send the trap and immediately after that seize the resource (remove the timeout on that line), I get times that are out of order. 

7: openDepot0: Opening counter capacity 1
17: customer0: Entering
17: customer0: being served
18: closeDepot0: closing counter
18: closeDepot0: closed counter
31: openDepot1: Opening counter capacity 1
13: customer0: Received notification that bank is closing
17: customer0: leaving
42: closeDepot1: closing counter

Code: 

library(simmer)
library(simmer.bricks)

# define resources and capacities only once. 
resources = c("counter")
capacities = c(1)

env = simmer("bank")
for (i in seq_along(resources)) { 
  env = env %>% 
    add_resource(resources[[i]], preemptive = TRUE, capacity = 0) 
}

bank_is_closing = trajectory("bank_closing") %>%
  log_("Received notification that bank is closing") %>%
  set_prioritization(c(100, 100, FALSE)) %>%
  timeout(4) # need to compute remaining timeout here. 

customer =
  trajectory("customer") %>%
  log_("Entering") %>%
  seize("counter", 1) %>%
  set_attribute("end_time", function() now(env) + 5) %>%
  trap("closing", handler = bank_is_closing) %>%
  log_("being served") %>%
  timeout(5) %>%
  release("counter") %>%
  log_("leaving") 


openResources = function(resources, capacities) { 
  traj = trajectory("open resources")
  for (i in seq_along(resources)) {
    resource = resources[[i]]
    capacity = capacities[[i]]
    traj = traj %>% 
      log_(paste("Opening", resource, "capacity", capacity)) %>%
      set_capacity(resource, capacity)
  }
  traj
}

closeResources = function(resources) { 
  # closing resources in parallel so that closing will start for 
  # all resources at the same time. 
  closeResourceTrajectory = function(resource) { 
    trajectory(paste("close", resource)) %>%
      send(signals = "closing") %>%
      timeout(0.0001) %>%  ###### POSSIBLE BUG ?
      log_(paste("closing", resource)) %>%
      seize(resource, function() get_capacity(env, resource)) %>%
      log_(paste("closed", resource)) %>%
      set_capacity(resource, 0) %>%
      release_all(resource)
  }
  
  args = lapply(resources, function(resource) { 
    closeResourceTrajectory(resource)
  })
  trajectory("close resource") %>%
    do_parallel(args, .env = env, wait = TRUE)
}
  
bank = env %>%
  add_generator("customer", customer, at(17), mon=2, priority = 10) %>%
  add_generator("openDepot", openResources(resources, capacities),
                from_to(7, 100, function() 24)) %>%
  add_generator("closeDepot", closeResources(resources),
                from_to(18, 100, function() 24), priority = 30)
  #add_generator("priority_customer", customer, at(17.75), mon = 2, 
  #             priority = 11, preemptible = 20) 
  #add_generator("closing", closeResources(resources), 
  #              from_to(18, 100, function() 24), priority = 5) %>%
  #add_generator("closingForcefully", closeResources(resources), 
  #              from_to(21, 100, function() 24), priority = 30) %>%
  #add_generator("opening", openResources(resources, capacities), 
  #              from_to(7, 100, function() 24), priority = 5)
 


bank %>% run(until = 100)


Op zaterdag 9 februari 2019 20:32:47 UTC+1 schreef Erik Brakkee:

Erik Brakkee

unread,
Feb 9, 2019, 2:55:55 PM2/9/19
to simmer-devel
NOTE: remaining time is 1:15 of course, not 1:45


Op zaterdag 9 februari 2019 20:32:47 UTC+1 schreef Erik Brakkee:
Hi, 

Erik Brakkee

unread,
Feb 9, 2019, 3:26:46 PM2/9/19
to simmer-devel
I can think of at least on workaround, which would be to split the timeout into a number of equal parts and keep track of the remaining time. E.g. for a timeout of 5 I could use:

set_attribute("remaining_time", 4.5) %>%
timeout(1) %>%
set_attribute("remaining_time", 3.5) %>%
timeout(1) %>%
set_attribute("remaining_time", 2.5) %>%
timeout(1) %>%
set_attribute("remaining_time", 1.5) %>%
timeout(1) %>%
set_attribute("remaining_time", 0.5) %>%
timeout(1)

Then the remaining_time attribute can be used in the trap. DIsadvantage is that for a few arrivals there could be some inaccuracy in the timeout when the bank closes. However, the above code compensates that by estimating that on average the interruption will be in the middle of a time interval. 



Op zaterdag 9 februari 2019 20:32:47 UTC+1 schreef Erik Brakkee:
Hi, 

Erik Brakkee

unread,
Feb 9, 2019, 3:50:02 PM2/9/19
to simmer-devel
There was an error in my thinking earlier. There is no need for the attribute. It can all be encapsulated in a few functions: 


bank_is_closing = function(timeoutValue) { 
  trajectory("bank_closing") %>%
    log_("Received notification that bank is closing") %>%
    set_prioritization(c(100, 100, FALSE)) %>%
    timeout(timeoutValue)
}

interruptibleTimeout = function(trajectory, timeoutValue) {
  n = 100
  trajectory = trajectory %>%
     trap("closing", handler = bank_is_closing(timeoutValue/n/2))
  for (i in 1:n) { 
    trajectory = trajectory %>%
      timeout(timeoutValue/n)
  }
  trajectory %>% untrap("closing")
}

Then use interruptibleTimeout instead of timeout. 

Cheers
  Erik


Op zaterdag 9 februari 2019 20:32:47 UTC+1 schreef Erik Brakkee:
Hi, 

Iñaki Ucar

unread,
Feb 9, 2019, 6:48:23 PM2/9/19
to simmer-devel
There's no way currently, but it may be a nice addition. I'll look into it.

Iñaki

Iñaki Ucar

unread,
Feb 9, 2019, 6:50:01 PM2/9/19
to simmer-devel
On Sat, 9 Feb 2019 at 20:40, Erik Brakkee <erik.b...@gmail.com> wrote:
>
> Below is some code. I want to achieve the behavior that when the depot closes, arrivals will still continue. However, I want them to be preemptible during opening hours. This is why I increase the priority as part of the trap.
>
> Also, I think I might have found an issue (see the line marked "POSSIBLE BUG" below). When I send the trap and immediately after that seize the resource (remove the timeout on that line), I get times that are out of order.

I get an ordered output when I execute your code.

Iñaki

Iñaki Ucar

unread,
Feb 9, 2019, 6:51:57 PM2/9/19
to simmer-devel
On Sat, 9 Feb 2019 at 21:50, Erik Brakkee <erik.b...@gmail.com> wrote:
>
> There was an error in my thinking earlier. There is no need for the attribute. It can all be encapsulated in a few functions:
>
>
> bank_is_closing = function(timeoutValue) {
> trajectory("bank_closing") %>%
> log_("Received notification that bank is closing") %>%
> set_prioritization(c(100, 100, FALSE)) %>%
> timeout(timeoutValue)
> }
>
> interruptibleTimeout = function(trajectory, timeoutValue) {
> n = 100
> trajectory = trajectory %>%
> trap("closing", handler = bank_is_closing(timeoutValue/n/2))
> for (i in 1:n) {
> trajectory = trajectory %>%
> timeout(timeoutValue/n)
> }
> trajectory %>% untrap("closing")
> }
>
>
> Then use interruptibleTimeout instead of timeout.

Yes, this is a possible workaround.

Iñaki

Erik Brakkee

unread,
Feb 10, 2019, 5:10:27 AM2/10/19
to simmer...@googlegroups.com
This is weird. I am executing the code below with the timeout commented out. The problem still exists if I update all packages.

closeResources = function(resources) { 
  # closing resources in parallel so that closing will start for 
  # all resources at the same time. 
  closeResourceTrajectory = function(resource) { 
    trajectory(paste("close", resource)) %>%
      send(signals = "closing") %>%
      #timeout(0.0001) %>%  ###### POSSIBLE BUG ?
      log_(paste("closing", resource)) %>%
      seize(resource, function() get_capacity(env, resource)) %>%
      log_(paste("closed", resource)) %>%
      set_capacity(resource, 0) %>%
      release_all(resource)
  }
  
  args = lapply(resources, function(resource) { 
    closeResourceTrajectory(resource)
  })
  trajectory("close resource") %>%
    do_parallel(args, .env = env, wait = TRUE)
}


This my R environment when running this simulation: 
cat('R version', paste0(R.version), '\n')
pkgs = (.packages())
for (pkg in pkgs) { 
  cat('packages ', pkg, ' ', paste0(packageVersion(pkg)), '\n')
}

R version: x86_64-redhat-linux-gnu x86_64 linux-gnu x86_64, linux-gnu  3 5.1 2018 07 02 74947 R R version 3.5.1 (2018-07-02) Feather Spray 
packages  simmer.bricks   0.2.1 
packages  simmer   4.2.1 
packages  stats   3.5.1 
packages  graphics   3.5.1 
packages  grDevices   3.5.1 
packages  utils   3.5.1 
packages  datasets   3.5.1 
packages  methods   3.5.1 
packages  base   3.5.1 

  

Erik Brakkee

unread,
Feb 10, 2019, 4:27:32 PM2/10/19
to simmer-devel
I now have another issue which relates to the interruptible timeout method I posted earlier:


is_closing = function(attributeName, n) { 
  trajectory("bank_closing") %>%
    log_("Received closing notification") %>%
    set_prioritization(c(PRIO_OVERTIME, PRIO_OVERTIME, FALSE)) %>%
    timeout(function() { get_attribute(env, attributeName)/n/2 }) %>%
    log_("Finished handling trap")
}

 interruptible_timeout_from_schedule = function(trajectory, attributeName) { 
  n = 10
  trajectory = trajectory %>%
     trap("closing", handler = is_closing(attributeName, n))
  for (i in 1:n) { 
    args = list(trajectory, paste("timeout step", i))
    trajectory = do.call("dtlog", args)
    trajectory = trajectory %>%
      timeout(function() { get_attribute(env, attributeName)/n })
  }
  trajectory %>% 
    log_("resetting priority") %>%
    set_prioritization(function() {
      prio = get_attribute(env, "original_priority")
      c(prio, prio, FALSE)
    }) %>%
    untrap("closing")
}

Now, upon receiving the signal, I see that after processing the signal it resumes at the wrong timeout. Specifically, I see this in the logs: 

1.47705e+09: parcel140: 1477052932.5 Fri 2016-10-21 12:28:52 timeout step 1
1.47706e+09: parcel140: 1477055020.5 Fri 2016-10-21 13:03:40 timeout step 2
1.47706e+09: parcel140: 1477057108.5 Fri 2016-10-21 13:38:28 timeout step 3
1.47706e+09: parcel140: 1477059196.5 Fri 2016-10-21 14:13:16 timeout step 4
1.47706e+09: parcel140: 1477061284.5 Fri 2016-10-21 14:48:04 timeout step 5
1.47706e+09: parcel140: 1477063372.5 Fri 2016-10-21 15:22:52 timeout step 6
1.47707e+09: parcel140: 1477065460.5 Fri 2016-10-21 15:57:40 timeout step 7
1.47707e+09: parcel140: 1477067548.5 Fri 2016-10-21 16:32:28 timeout step 8
1.47707e+09: parcel140: 1477069636.5 Fri 2016-10-21 17:07:16 timeout step 9
1.47707e+09: parcel140: 1477071724.5 Fri 2016-10-21 17:42:04 timeout step 10
1.47707e+09: parcel140: 1477072800 Fri 2016-10-21 18:00:00 Received closing notification
1.47707e+09: parcel140: 1477073844 Fri 2016-10-21 18:17:24 Finished handling trap
1.47707e+09: parcel140: 1477073844 Fri 2016-10-21 18:17:24 timeout step 8
1.47708e+09: parcel140: 1477075932 Fri 2016-10-21 18:52:12 timeout step 9
1.47708e+09: parcel140: 1477078020 Fri 2016-10-21 19:27:00 timeout step 10
1.47708e+09: parcel140: 1477080108 Fri 2016-10-21 20:01:48 resetting priority

Is this intended to happen? Shouldn't it always skip the timeout that was interrupted?

Cheers
  Erik

Erik Brakkee

unread,
Feb 10, 2019, 4:36:53 PM2/10/19
to simmer-devel
And, as usual, I am responding to my own mail. There is a workaround for this problem which is to register the trap before each timeout and untrap() it afterwards:

interruptible_timeout_from_schedule = function(trajectory, attributeName) { 
  n = 10
  for (i in 1:n) { 
    args = list(trajectory, paste("timeout step", i))
    trajectory = do.call("log_", args)
    trajectory = trajectory %>%
      trap("closing", handler = is_closing(attributeName, n)) %>%
      timeout(function() { get_attribute(env, attributeName)/n }) %>%
      untrap("closing")
  }
  trajectory %>% 
    dtlog("resetting priority") %>%
    set_prioritization(function() {
      prio = get_attribute(env, "original_priority")
      c(prio, prio, FALSE)
    }) 
}
Cheers
  Erik


Op zaterdag 9 februari 2019 20:32:47 UTC+1 schreef Erik Brakkee:
Hi, 

Iñaki Ucar

unread,
Feb 11, 2019, 4:39:08 AM2/11/19
to simmer-devel
I didn't notice that I should have to comment the timeout before
running the code to reproduce the issue. Definitely, that shouldn't
happen. I'll investigate. Thanks for the report!

Iñaki
> --
> You received this message because you are subscribed to the Google Groups "simmer-devel" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to simmer-devel...@googlegroups.com.
> To post to this group, send email to simmer...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/simmer-devel/CALTmpijWvFD47scqYcufoRi6Zs%2BKUma-FqX9daZoH8qCt_Dtcg%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.



--
Iñaki Úcar

Iñaki Ucar

unread,
Feb 11, 2019, 4:42:32 AM2/11/19
to simmer-devel
On Sun, 10 Feb 2019 at 22:27, Erik Brakkee <erik.b...@gmail.com> wrote:
>>
>> I now have another issue which relates to the interruptible timeout method I posted earlier:

Sorry, but I'm not able to track so many self-replies. Could you post
the entire code for this particular issue?

Iñaki

Iñaki Ucar

unread,
Feb 11, 2019, 12:12:43 PM2/11/19
to simmer-devel
This is fixed in the master branch. See
https://github.com/r-simmer/simmer/issues/187

Iñaki
--
Iñaki Úcar

Erik Brakkee

unread,
Feb 11, 2019, 4:49:33 PM2/11/19
to simmer...@googlegroups.com
Hi  Iñaki 

Thanks for fixing it so quickly.

Cheers
  Erik

Iñaki Úcar

unread,
Apr 17, 2022, 3:41:40 PM4/17/22
to simmer-devel
FYI, the current master branch on GitHub has a get_activity_time() function.

Iñaki

Reply all
Reply to author
Forward
0 new messages