This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Sass
|
||||
class Compiler
|
||||
# The {Dispatcher} class.
|
||||
#
|
||||
# It dispatches messages between multiple instances of {Session} and a single {Connection} to the compiler.
|
||||
class Dispatcher
|
||||
def initialize(idle_timeout: 0)
|
||||
@id = 1
|
||||
@observers = {}.compare_by_identity
|
||||
@mutex = Mutex.new
|
||||
@connection = Connection.new
|
||||
@connection.listen(self)
|
||||
ForkTracker.add(self)
|
||||
|
||||
return unless idle_timeout.positive?
|
||||
|
||||
@last_accessed_time = current_time
|
||||
Thread.new do
|
||||
Thread.current.name = "sass-embedded-connection-reaper-#{@connection.id}"
|
||||
duration = idle_timeout
|
||||
loop do
|
||||
sleep(duration.negative? ? idle_timeout : duration)
|
||||
break if @mutex.synchronize do
|
||||
raise Errno::EBUSY if _closed?
|
||||
|
||||
duration = idle_timeout - (current_time - @last_accessed_time)
|
||||
duration.negative? && _idle? && _close
|
||||
end
|
||||
end
|
||||
close
|
||||
rescue Errno::EBUSY
|
||||
# do nothing
|
||||
end
|
||||
end
|
||||
|
||||
def subscribe(observer)
|
||||
@mutex.synchronize do
|
||||
raise Errno::EBUSY if _closed?
|
||||
|
||||
id = @id
|
||||
@id = id.next
|
||||
@observers[id] = observer
|
||||
id
|
||||
end
|
||||
end
|
||||
|
||||
def unsubscribe(id)
|
||||
@mutex.synchronize do
|
||||
@observers.delete(id)
|
||||
|
||||
return unless @observers.empty?
|
||||
|
||||
if _closed?
|
||||
Thread.new do
|
||||
close
|
||||
end
|
||||
else
|
||||
_idle
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
@mutex.synchronize do
|
||||
_close
|
||||
end
|
||||
@connection.close
|
||||
ForkTracker.delete(self)
|
||||
end
|
||||
|
||||
def closed?
|
||||
@connection.closed?
|
||||
end
|
||||
|
||||
def error(error)
|
||||
observers = @mutex.synchronize do
|
||||
_close
|
||||
@observers.values
|
||||
end
|
||||
|
||||
if observers.empty?
|
||||
close
|
||||
else
|
||||
observers.each do |observer|
|
||||
observer.error(error)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def receive_proto(id, proto)
|
||||
case id
|
||||
when 1...0xffffffff
|
||||
@mutex.synchronize { @observers[id] }&.receive_proto(proto)
|
||||
when 0
|
||||
outbound_message = EmbeddedProtocol::OutboundMessage.decode(proto)
|
||||
oneof = outbound_message.message
|
||||
message = outbound_message.public_send(oneof)
|
||||
@mutex.synchronize { @observers[message.id] }&.public_send(oneof, message)
|
||||
when 0xffffffff
|
||||
outbound_message = EmbeddedProtocol::OutboundMessage.decode(proto)
|
||||
oneof = outbound_message.message
|
||||
message = outbound_message.public_send(oneof)
|
||||
raise Errno::EPROTO, message.message
|
||||
else
|
||||
raise Errno::EPROTO
|
||||
end
|
||||
end
|
||||
|
||||
def send_proto(...)
|
||||
@connection.write(...)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def current_time
|
||||
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||
end
|
||||
|
||||
def _close
|
||||
@id = 0xffffffff
|
||||
end
|
||||
|
||||
def _closed?
|
||||
@id == 0xffffffff
|
||||
end
|
||||
|
||||
def _idle
|
||||
@last_accessed_time = current_time if defined?(@last_accessed_time)
|
||||
|
||||
@id = 1
|
||||
end
|
||||
|
||||
def _idle?
|
||||
@id == 1
|
||||
end
|
||||
end
|
||||
|
||||
private_constant :Dispatcher
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user