diff --git a/lib/fluent/plugin_helper/event_loop.rb b/lib/fluent/plugin_helper/event_loop.rb
index 52e70e648d..1ff9946b9a 100644
--- a/lib/fluent/plugin_helper/event_loop.rb
+++ b/lib/fluent/plugin_helper/event_loop.rb
@@ -43,6 +43,15 @@ def event_loop_attach(watcher)
         end
       end
 
+      def event_loop_detach(watcher)
+        if watcher.attached?
+          watcher.detach
+        end
+        @_event_loop_mutex.synchronize do
+          @_event_loop_attached_watchers.delete(watcher)
+        end
+      end
+
       def event_loop_wait_until_start
         sleep(0.1) until event_loop_running?
       end
diff --git a/lib/fluent/plugin_helper/timer.rb b/lib/fluent/plugin_helper/timer.rb
index 02fac1ced8..d3047f82dc 100644
--- a/lib/fluent/plugin_helper/timer.rb
+++ b/lib/fluent/plugin_helper/timer.rb
@@ -15,6 +15,7 @@
 #
 
 require 'fluent/plugin_helper/event_loop'
+require 'set'
 
 module Fluent
   module PluginHelper
@@ -33,7 +34,8 @@ def timer_execute(title, interval, repeat: true, &block)
         raise ArgumentError, "BUG: title must be a symbol" unless title.is_a? Symbol
         raise ArgumentError, "BUG: block not specified for callback" unless block_given?
         checker = ->(){ @_timer_running }
-        timer = TimerWatcher.new(title, interval, repeat, log, checker, &block)
+        detacher = ->(watcher){ event_loop_detach(watcher) }
+        timer = TimerWatcher.new(title, interval, repeat, log, checker, detacher, &block)
         @_timers << title
         event_loop_attach(timer)
         timer
@@ -45,7 +47,7 @@ def timer_running?
 
       def initialize
         super
-        @_timers = []
+        @_timers ||= Set.new
       end
 
       def start
@@ -60,16 +62,17 @@ def stop
 
       def terminate
         super
-        @_timers = []
+        @_timers = nil
       end
 
       class TimerWatcher < Coolio::TimerWatcher
-        def initialize(title, interval, repeat, log, checker, &callback)
+        def initialize(title, interval, repeat, log, checker, detacher, &callback)
           @title = title
           @callback = callback
           @repeat = repeat
           @log = log
           @checker = checker
+          @detacher = detacher
           super(interval, repeat)
         end
 
@@ -81,9 +84,7 @@ def on_timer
           detach
           @log.error "Timer detached.", title: @title
         ensure
-          if attached?
-            detach unless @repeat
-          end
+          @detacher.call(self) unless @repeat
         end
       end
     end