diff --git a/lib/sass/compiler.rb b/lib/sass/compiler.rb index ee2020f0..928821ab 100644 --- a/lib/sass/compiler.rb +++ b/lib/sass/compiler.rb @@ -26,7 +26,7 @@ module Sass # sass.close class Compiler def initialize - @dispatcher = ResilientDispatcher.new + @dispatcher = ResilientDispatcher.new(Dispatcher) end # Compiles the Sass file at +path+ to CSS. diff --git a/lib/sass/compiler/dispatcher.rb b/lib/sass/compiler/dispatcher.rb index 510bb7fe..231de651 100644 --- a/lib/sass/compiler/dispatcher.rb +++ b/lib/sass/compiler/dispatcher.rb @@ -35,7 +35,7 @@ def unsubscribe(id) close end else - @id = 1 + idle end end end @@ -90,6 +90,12 @@ def send_proto(...) @connection.write(...) end + private + + def idle + @id = 1 + end + # The {Channel} between {Dispatcher} and {Host}. class Channel attr_reader :id diff --git a/lib/sass/compiler/resilient_dispatcher.rb b/lib/sass/compiler/resilient_dispatcher.rb index c1c53be5..abc382ff 100644 --- a/lib/sass/compiler/resilient_dispatcher.rb +++ b/lib/sass/compiler/resilient_dispatcher.rb @@ -6,8 +6,9 @@ class Compiler # # It recovers from failures and continues to function. class ResilientDispatcher - def initialize - @dispatcher = Dispatcher.new + def initialize(dispatcher_class) + @dispatcher_class = dispatcher_class + @dispatcher = @dispatcher_class.new @mutex = Mutex.new end @@ -29,7 +30,7 @@ def connect(...) @mutex.synchronize do @dispatcher.connect(...) rescue Errno::EBUSY - @dispatcher = Dispatcher.new + @dispatcher = @dispatcher_class.new @dispatcher.connect(...) end end diff --git a/lib/sass/embedded.rb b/lib/sass/embedded.rb index 53668316..e8e39553 100644 --- a/lib/sass/embedded.rb +++ b/lib/sass/embedded.rb @@ -54,7 +54,43 @@ def compiler @mutex.synchronize do return @compiler if @compiler - compiler = Compiler.new + compiler = Class.new(Compiler) do + def initialize + @dispatcher = self.class.const_get(:ResilientDispatcher).new(Class.new(self.class.const_get(:Dispatcher)) do + def initialize + super + + idle_timeout = 10 + @last_accessed_time = current_time + + Thread.new do + duration = idle_timeout + loop do + sleep(duration.negative? ? idle_timeout : duration) + evicted = @mutex.synchronize do + duration = idle_timeout - (current_time - @last_accessed_time) + @id = 0xffffffff if @observers.empty? && duration.negative? + end + break if evicted + end + close + end + end + + private + + def idle + super + + @last_accessed_time = current_time + end + + def current_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + end) + end + end.new Process.singleton_class.prepend(Module.new do define_method :_fork do