From 5d4aa1ac1fc3e6ad3e86bfa0fd9c61f96bc69f98 Mon Sep 17 00:00:00 2001
From: pjs <pjs@alum.mit.edu>
Date: Wed, 11 Jul 2018 07:41:17 -0700
Subject: [PATCH] Cancellation functioning w/ test cases

---
 demos/CancelQ.rb | 23 +++++++++++++++++++++++
 lib/simplekit.rb | 24 ++++++++++++++++++++----
 2 files changed, 43 insertions(+), 4 deletions(-)
 create mode 100644 demos/CancelQ.rb

diff --git a/demos/CancelQ.rb b/demos/CancelQ.rb
new file mode 100644
index 0000000..d9022f7
--- /dev/null
+++ b/demos/CancelQ.rb
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+
+require_relative '../lib/simplekit'
+
+class MyModel
+  include SimpleKit
+
+  def init
+    @x = 1
+    schedule(:increment, rand(2), n: 1, char: 'z'.ord)
+    cancel(:increment, char: 'q'.ord)
+  end
+
+  def increment(n:, char:)
+    @x += n
+    schedule(:increment, 2.0 * rand(2), n: @x, char: char - 1, priority: 3)
+    printf "%f, %f, %c\n", model_time, @x, char
+    cancel_all :increment if model_time > 10
+  end
+end
+
+srand(42)
+MyModel.new.run
diff --git a/lib/simplekit.rb b/lib/simplekit.rb
index 0826328..cd63429 100644
--- a/lib/simplekit.rb
+++ b/lib/simplekit.rb
@@ -1,3 +1,4 @@
+require 'set'
 require_relative 'priority_queue'
 
 # The +SimpleKit+ module provides basic event scheduling capabilities.
@@ -50,7 +51,7 @@ module SimpleKit
     #   - +event+ -> the event to be scheduled.
     #   - +delay+ -> the amount of time which should elapse before
     #     the event executes.
-    #   - +args+ -> an optional list of arguments to pass to the event
+    #   - +args+ -> zero or more named arguments to pass to the event
     #     at invocation time.
     def schedule(event, delay, **args)
       raise 'Model scheduled event with negative delay.' if delay < 0
@@ -58,7 +59,8 @@ module SimpleKit
     end
 
     def cancel(event, **args)
-      @cancel_set[event] = args
+      @cancel_set[event] ||= Set.new
+      @cancel_set[event].add(args.empty? ? nil : args)
     end
 
     def cancel_all(event)
@@ -83,8 +85,22 @@ module SimpleKit
       while (current_event = @event_list.pop)
         e = current_event.event
         if @cancel_set.key? e
-          @cancel_set.delete e # if @cancel_set[e].empty?
-          next
+          if @cancel_set[e] === nil
+            @cancel_set[e].delete nil
+            @cancel_set.delete e if @cancel_set[e].empty?
+            next
+          else
+            arg_match = false
+            for h in @cancel_set[e] do
+              if current_event.args >= h
+                arg_match = true
+                @cancel_set[e].delete h
+                @cancel_set.delete e if @cancel_set[e].empty?
+                break
+              end
+            end
+            next if arg_match
+          end
         end
         @model_time = current_event.time
         if current_event.args.empty?
-- 
GitLab