diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/kgio.rb | 10 | ||||
-rw-r--r-- | lib/kgio/autopush.rb | 68 | ||||
-rw-r--r-- | lib/kgio/autopush/acceptor.rb | 42 | ||||
-rw-r--r-- | lib/kgio/autopush/sock_rw.rb | 68 |
4 files changed, 186 insertions, 2 deletions
diff --git a/lib/kgio.rb b/lib/kgio.rb index f192074..2b420b0 100644 --- a/lib/kgio.rb +++ b/lib/kgio.rb @@ -17,14 +17,20 @@ module Kgio # :wait_writable when waiting for a read is required. WaitWritable = :wait_writable - # autopush is no-op nowadays + # autopush is strongly not recommended nowadays, use MSG_MORE instead @autopush = false class << self - attr_accessor :autopush # :nodoc: + attr_reader :autopush # :nodoc: def autopush? # :nodoc: !!@autopush end + + def autopush=(bool) # :nodoc: + # No require_relative, we remain 1.8-compatible + require 'kgio/autopush' + @autopush = bool + end end end diff --git a/lib/kgio/autopush.rb b/lib/kgio/autopush.rb new file mode 100644 index 0000000..fb33f11 --- /dev/null +++ b/lib/kgio/autopush.rb @@ -0,0 +1,68 @@ +# Copyright (C) 2015 all contributors <kgio-public@bogomips.org> +# License: LGPLv2.1 or later (https://www.gnu.org/licenses/lgpl-2.1.txt) + +require 'socket' +require 'thread' + +# using this code is not recommended, for backwards compatibility only +module Kgio::Autopush # :nodoc: + class SyncArray # :nodoc: + def initialize # :nodoc: + @map = [] + @lock = Mutex.new + end + + def []=(key, val) # :nodoc: + @lock.synchronize { @map[key] = val } + end + + def [](key) # :nodoc: + @lock.synchronize { @map[key] } + end + end + + FDMAP = SyncArray.new # :nodoc: + APState = Struct.new(:obj, :ap_state) # :nodoc: + + # Not using pre-defined socket constants for 1.8 compatibility + if RUBY_PLATFORM.include?('linux') + NOPUSH = 3 # :nodoc: + elsif RUBY_PLATFORM.include?('freebsd') + NOPUSH = 4 # :nodoc: + end + + def kgio_autopush? # :nodoc: + return false unless Kgio.autopush? + state = FDMAP[fileno] + state && state.obj == self && state.ap_state != :ignore + end + + def kgio_autopush=(bool) # :nodoc: + if bool + state = FDMAP[fileno] ||= APState.new + state.ap_state = :writer + state.obj = self + end + bool + end + +private + def kgio_push_pending # :nodoc: + Kgio.autopush or return + state = FDMAP[fileno] or return + state.obj == self and state.ap_state = :written + end + + def kgio_push_pending_data # :nodoc: + Kgio.autopush or return + state = FDMAP[fileno] or return + state.obj == self && state.ap_state == :written or return + setsockopt(Socket::IPPROTO_TCP, NOPUSH, 0) + setsockopt(Socket::IPPROTO_TCP, NOPUSH, 1) + state.ap_state = :writer + end +end +require 'kgio/autopush/sock_rw' +require 'kgio/autopush/acceptor' +Kgio::TCPSocket.__send__(:include, Kgio::Autopush::SockRW) # :nodoc: +Kgio::Socket.__send__(:include, Kgio::Autopush::SockRW) # :nodoc: diff --git a/lib/kgio/autopush/acceptor.rb b/lib/kgio/autopush/acceptor.rb new file mode 100644 index 0000000..2bf6dd9 --- /dev/null +++ b/lib/kgio/autopush/acceptor.rb @@ -0,0 +1,42 @@ +# Copyright (C) 2015 all contributors <kgio-public@bogomips.org> +# License: LGPLv2.1 or later (https://www.gnu.org/licenses/lgpl-2.1.txt) + +# using this code is not recommended, for backwards compatibility only +class Kgio::TCPServer + include Kgio::Autopush + + alias_method :kgio_accept_orig, :kgio_accept + undef_method :kgio_accept + def kgio_accept(*args) + kgio_autopush_post_accept(kgio_accept_orig(*args)) + end + + alias_method :kgio_tryaccept_orig, :kgio_tryaccept + undef_method :kgio_tryaccept + def kgio_tryaccept(*args) + kgio_autopush_post_accept(kgio_tryaccept_orig(*args)) + end + +private + + def kgio_autopush_post_accept(rv) # :nodoc: + return rv unless Kgio.autopush? && rv.respond_to?(:kgio_autopush=) + if my_state = FDMAP[fileno] + if my_state.obj == self + rv.kgio_autopush = true if my_state.ap_state == :acceptor + return rv + end + else + my_state = FDMAP[fileno] ||= Kgio::Autopush::APState.new + end + my_state.obj = self + my_state.ap_state = nil + begin + n = getsockopt(Socket::IPPROTO_TCP, Kgio::Autopush::NOPUSH).unpack('i') + my_state.ap_state = :acceptor if n[0] == 1 + rescue Errno::ENOTSUPP # non-TCP socket + end + rv.kgio_autopush = true if my_state.ap_state == :acceptor + rv + end +end diff --git a/lib/kgio/autopush/sock_rw.rb b/lib/kgio/autopush/sock_rw.rb new file mode 100644 index 0000000..52f7a45 --- /dev/null +++ b/lib/kgio/autopush/sock_rw.rb @@ -0,0 +1,68 @@ +# Copyright (C) 2015 all contributors <kgio-public@bogomips.org> +# License: LGPLv2.1 or later (https://www.gnu.org/licenses/lgpl-2.1.txt) + +# using this code is not recommended, for backwards compatibility only +module Kgio::Autopush::SockRW # :nodoc: + include Kgio::Autopush + + def kgio_read(*) # :nodoc: + kgio_push_pending_data + super + end + + def kgio_read!(*) # :nodoc: + kgio_push_pending_data + super + end + + def kgio_tryread(*) # :nodoc: + kgio_push_pending_data + super + end + + def kgio_trypeek(*) # :nodoc: + kgio_push_pending_data + super + end + + def kgio_peek(*) # :nodoc: + kgio_push_pending_data + super + end + + def kgio_syssend(*) # :nodoc: + kgio_push_pending_data(super) + end if Kgio::SocketMethods.method_defined?(:kgio_syssend) + + def kgio_trysend(*) # :nodoc: + kgio_ap_wrap_writer(super) + end + + def kgio_trywrite(*) # :nodoc: + kgio_ap_wrap_writer(super) + end + + def kgio_trywritev(*) # :nodoc: + kgio_ap_wrap_writer(super) + end + + def kgio_write(*) # :nodoc: + kgio_ap_wrap_writer(super) + end + + def kgio_writev(*) # :nodoc: + kgio_ap_wrap_writer(super) + end + +private + + def kgio_ap_wrap_writer(rv) # :nodoc: + case rv + when :wait_readable, :wait_writable + kgio_push_pending_data + else + kgio_push_pending + end + rv + end +end |