oldmoe-neverblock 0.1.6 → 1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/never_block.rb +5 -78
- data/lib/neverblock.rb +3 -1
- data/lib/{never_block/extensions/fiber_extensions.rb → neverblock/core/fiber.rb} +20 -6
- data/lib/neverblock/core/pool.rb +72 -0
- data/lib/neverblock/core/reactor.rb +50 -0
- data/lib/neverblock/core/system/system.rb +38 -0
- data/lib/neverblock/core/system/timeout.rb +67 -0
- data/lib/{never_block/db/pooled_db_connection.rb → neverblock/io/db/connection.rb} +16 -2
- data/lib/neverblock/io/db/drivers/mysql.rb +73 -0
- data/lib/neverblock/io/db/drivers/postgres.rb +63 -0
- data/lib/neverblock/io/db/fibered_connection_pool.rb +130 -0
- data/lib/{never_block → neverblock/io}/db/fibered_mysql_connection.rb +5 -18
- data/lib/{never_block/pool/fibered_connection_pool.rb → neverblock/io/db/pool.rb} +25 -40
- data/lib/neverblock/io/file.rb +24 -0
- data/lib/neverblock/io/io.rb +219 -0
- data/lib/neverblock/io/socket.rb +75 -0
- data/lib/neverblock_io.rb +6 -0
- data/lib/system.rb +4 -0
- data/neverblock.gemspec +23 -21
- metadata +23 -20
- data/lib/active_record/connection_adapters/neverblock_mysql_adapter.rb +0 -68
- data/lib/active_record/connection_adapters/neverblock_postgresql_adapter.rb +0 -85
- data/lib/never_block/db/fibered_db_connection.rb +0 -72
- data/lib/never_block/db/fibered_postgres_connection.rb +0 -64
- data/lib/never_block/frameworks/activerecord.rb +0 -37
- data/lib/never_block/frameworks/rails.rb +0 -65
- data/lib/never_block/pool/fiber_pool.rb +0 -74
- data/lib/never_block/servers/mongrel.rb +0 -236
- data/lib/never_block/servers/thin.rb +0 -32
- data/lib/neverblock-mysql.rb +0 -5
- data/lib/neverblock-pg.rb +0 -5
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)+'/../lib/neverblock/io/db/drivers/postgres')
|
|
2
|
+
require File.expand_path(File.dirname(__FILE__)+'/../lib/neverblock/io/db/connection')
|
|
3
|
+
|
|
4
|
+
module NeverBlock
|
|
5
|
+
|
|
6
|
+
module DB
|
|
7
|
+
|
|
8
|
+
# A modified postgres connection driver
|
|
9
|
+
# builds on the original pg driver.
|
|
10
|
+
# This driver is able to register the socket
|
|
11
|
+
# at a certain backend (Reacotr)
|
|
12
|
+
# and then whenever the query is executed
|
|
13
|
+
# within the scope of a friendly fiber (NB::Fiber)
|
|
14
|
+
# it will be done in async mode and the fiber
|
|
15
|
+
# will yield
|
|
16
|
+
class Postgres < PGconn
|
|
17
|
+
|
|
18
|
+
# Assuming the use of NeverBlock fiber extensions and that the exec is run in
|
|
19
|
+
# the context of a fiber. One that have the value :neverblock set to true.
|
|
20
|
+
# All neverblock IO classes check this value, setting it to false will force
|
|
21
|
+
# the execution in a blocking way.
|
|
22
|
+
def exec(sql)
|
|
23
|
+
# TODO Still not "killing the query process"-proof
|
|
24
|
+
# In some cases, the query is simply sent but the fiber never yields
|
|
25
|
+
if NB.neverblocking? && NB.reactor.running?
|
|
26
|
+
send_query sql
|
|
27
|
+
while is_busy
|
|
28
|
+
NB.wait(:read, IO.new(socket))
|
|
29
|
+
consume_input
|
|
30
|
+
end
|
|
31
|
+
res, data = 0, []
|
|
32
|
+
while res != nil
|
|
33
|
+
res = self.get_result
|
|
34
|
+
data << res unless res.nil?
|
|
35
|
+
end
|
|
36
|
+
data.last
|
|
37
|
+
else
|
|
38
|
+
super(sql)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
alias_method :query, :exec
|
|
43
|
+
|
|
44
|
+
end #Postgres
|
|
45
|
+
|
|
46
|
+
class PooledPostgres < ::NeverBlock::DB::Connection
|
|
47
|
+
|
|
48
|
+
def initialize(*args)
|
|
49
|
+
options = {}
|
|
50
|
+
if args && (options = args.last).is_a? Hash
|
|
51
|
+
size = options[:size] || 4
|
|
52
|
+
eager = options[:eager] || true
|
|
53
|
+
args.pop
|
|
54
|
+
end
|
|
55
|
+
@pool = NB::DB::Pool.new(:size=>size, :eager=>eager) do
|
|
56
|
+
Postgres.new(*args)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end #PooledMySQL
|
|
61
|
+
end #DB
|
|
62
|
+
|
|
63
|
+
end #NeverBlock
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
module NeverBlock
|
|
2
|
+
module Pool
|
|
3
|
+
# This class represents a pool of connections,
|
|
4
|
+
# you hold or release conncetions from the pool
|
|
5
|
+
# hold requests that cannot be fullfiled will be queued
|
|
6
|
+
# the fiber will be paused and resumed later when
|
|
7
|
+
# a connection is avaialble
|
|
8
|
+
#
|
|
9
|
+
# Large portions of this class were copied and pasted
|
|
10
|
+
# form Sequel's threaded connection pool
|
|
11
|
+
#
|
|
12
|
+
# Example:
|
|
13
|
+
#
|
|
14
|
+
# pool = NeverBlock::Pool::FiberedConnectionPool.new(:size=>16)do
|
|
15
|
+
# # connection creation code goes here
|
|
16
|
+
# end
|
|
17
|
+
# 32.times do
|
|
18
|
+
# Fiber.new do
|
|
19
|
+
# # acquire a connection from the pool
|
|
20
|
+
# pool.hold do |conn|
|
|
21
|
+
# conn.execute('something') # you can use the connection normally now
|
|
22
|
+
# end
|
|
23
|
+
# end.resume
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# The pool has support for transactions, just pass true to the
|
|
27
|
+
# pool#hold method and the connection will not be released after the block
|
|
28
|
+
# is finished
|
|
29
|
+
# It is the responsibility of client code to release the connection
|
|
30
|
+
class FiberedConnectionPool
|
|
31
|
+
|
|
32
|
+
attr_reader :size
|
|
33
|
+
|
|
34
|
+
# initialize the connection pool using the supplied proc to create
|
|
35
|
+
# the connections
|
|
36
|
+
# You can choose to start them eagerly or lazily (lazy by default)
|
|
37
|
+
# Available options are
|
|
38
|
+
# :size => the maximum number of connections to be created in the pool
|
|
39
|
+
# :eager => (true|false) indicates whether connections should be
|
|
40
|
+
# created initially or when need
|
|
41
|
+
def initialize(options = {}, &block)
|
|
42
|
+
@connections, @busy_connections, @queue = [], {},[]
|
|
43
|
+
@connection_proc = block
|
|
44
|
+
@size = options[:size] || 8
|
|
45
|
+
if options[:eager]
|
|
46
|
+
@size.times do
|
|
47
|
+
@connections << @connection_proc.call
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def replace_acquired_connection
|
|
53
|
+
fiber = Fiber.current
|
|
54
|
+
conn = @connection_proc.call
|
|
55
|
+
@busy_connections[fiber] = conn
|
|
56
|
+
fiber[connection_pool_key] = conn
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# If a connection is available, pass it to the block, otherwise pass
|
|
60
|
+
# the fiber to the queue till a connection is available
|
|
61
|
+
def hold()
|
|
62
|
+
fiber = Fiber.current
|
|
63
|
+
conn = acquire(fiber)
|
|
64
|
+
yield conn
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def all_connections
|
|
68
|
+
(@connections + @busy_connections.values).each {|conn| yield(conn)}
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
# Can we find a connection?
|
|
74
|
+
# Can we create one?
|
|
75
|
+
# Wait in the queue then
|
|
76
|
+
def acquire(fiber)
|
|
77
|
+
# A special case for rails when doing ActiveRecord stuff when not yet
|
|
78
|
+
# running in the context of a request (fiber) like in the case of AR
|
|
79
|
+
# queries in environment.rb (Root Fiber)
|
|
80
|
+
return @connections.first unless fiber[:callbacks]
|
|
81
|
+
|
|
82
|
+
fiber[:current_pool_key] = connection_pool_key
|
|
83
|
+
return fiber[connection_pool_key] if fiber[connection_pool_key]
|
|
84
|
+
conn = if !@connections.empty?
|
|
85
|
+
@connections.shift
|
|
86
|
+
elsif (@connections.length + @busy_connections.length) < @size
|
|
87
|
+
@connection_proc.call
|
|
88
|
+
else
|
|
89
|
+
Fiber.yield @queue << fiber
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# They're called in reverse order i.e. release then process_queue
|
|
93
|
+
fiber[:callbacks] << self.method(:process_queue)
|
|
94
|
+
fiber[:callbacks] << self.method(:release)
|
|
95
|
+
|
|
96
|
+
@busy_connections[fiber] = conn
|
|
97
|
+
fiber[connection_pool_key] = conn
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Give the fiber's connection back to the pool
|
|
101
|
+
def release()
|
|
102
|
+
fiber = Fiber.current
|
|
103
|
+
if fiber[connection_pool_key]
|
|
104
|
+
@busy_connections.delete(fiber)
|
|
105
|
+
@connections << fiber[connection_pool_key]
|
|
106
|
+
fiber[connection_pool_key] = nil
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Check if there are waiting fibers and
|
|
111
|
+
# try to process them
|
|
112
|
+
def process_queue
|
|
113
|
+
while !@connections.empty? and !@queue.empty?
|
|
114
|
+
fiber = @queue.shift
|
|
115
|
+
# What is really happening here?
|
|
116
|
+
# we are resuming a fiber from within
|
|
117
|
+
# another, should we call transfer instead?
|
|
118
|
+
fiber.resume @connections.shift
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def connection_pool_key
|
|
123
|
+
@connection_pool_key ||= "connection_pool_#{object_id}".intern
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
end #FiberedConnectionPool
|
|
127
|
+
|
|
128
|
+
end #Pool
|
|
129
|
+
|
|
130
|
+
end #NeverBlock
|
|
@@ -7,10 +7,8 @@ module NeverBlock
|
|
|
7
7
|
# This driver is able to register the socket at a certain backend (EM)
|
|
8
8
|
# and then whenever the query is executed within the scope of a friendly
|
|
9
9
|
# fiber. It will be done in async mode and the fiber will yield
|
|
10
|
-
class FiberedMysqlConnection < Mysql
|
|
10
|
+
class FiberedMysqlConnection < Mysql
|
|
11
11
|
|
|
12
|
-
include FiberedDBConnection
|
|
13
|
-
|
|
14
12
|
# Initializes the connection and remembers the connection params
|
|
15
13
|
def initialize(*args)
|
|
16
14
|
@connection_params = args
|
|
@@ -31,21 +29,10 @@ module NeverBlock
|
|
|
31
29
|
# All neverblock IO classes check this value, setting it to false will force
|
|
32
30
|
# the execution in a blocking way.
|
|
33
31
|
def query(sql)
|
|
34
|
-
if NB.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Fiber.yield register_with_event_loop
|
|
39
|
-
get_result
|
|
40
|
-
rescue Exception => e
|
|
41
|
-
if error = ['not connected', 'gone away', 'Lost connection'].detect{|msg| e.message.include? msg}
|
|
42
|
-
event_loop_connection_close
|
|
43
|
-
unregister_from_event_loop
|
|
44
|
-
remove_unregister_from_event_loop_callbacks
|
|
45
|
-
#connect
|
|
46
|
-
end
|
|
47
|
-
raise e
|
|
48
|
-
end
|
|
32
|
+
if NB.neverblocking? && NB.reactor.running?
|
|
33
|
+
send_query sql
|
|
34
|
+
NB.wait(:read, IO.new(socket))
|
|
35
|
+
get_result
|
|
49
36
|
else
|
|
50
37
|
super(sql)
|
|
51
38
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__)+'/../../../neverblock')
|
|
2
|
+
|
|
1
3
|
module NeverBlock
|
|
2
|
-
module
|
|
4
|
+
module DB
|
|
3
5
|
# This class represents a pool of connections,
|
|
4
6
|
# you hold or release conncetions from the pool
|
|
5
7
|
# hold requests that cannot be fullfiled will be queued
|
|
@@ -11,7 +13,7 @@ module NeverBlock
|
|
|
11
13
|
#
|
|
12
14
|
# Example:
|
|
13
15
|
#
|
|
14
|
-
# pool = NeverBlock::Pool
|
|
16
|
+
# pool = NeverBlock::Pool.new(:size=>16)do
|
|
15
17
|
# # connection creation code goes here
|
|
16
18
|
# end
|
|
17
19
|
# 32.times do
|
|
@@ -23,11 +25,9 @@ module NeverBlock
|
|
|
23
25
|
# end.resume
|
|
24
26
|
# end
|
|
25
27
|
#
|
|
26
|
-
# The pool has support for transactions, just pass true to the
|
|
27
|
-
# pool#hold method and the connection will not be released after the block
|
|
28
|
-
# is finished
|
|
29
28
|
# It is the responsibility of client code to release the connection
|
|
30
|
-
|
|
29
|
+
# using pool.release(conn)
|
|
30
|
+
class Pool
|
|
31
31
|
|
|
32
32
|
attr_reader :size
|
|
33
33
|
|
|
@@ -50,7 +50,7 @@ module NeverBlock
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def replace_acquired_connection
|
|
53
|
-
fiber = Fiber.current
|
|
53
|
+
fiber = NB::Fiber.current
|
|
54
54
|
conn = @connection_proc.call
|
|
55
55
|
@busy_connections[fiber] = conn
|
|
56
56
|
fiber[connection_pool_key] = conn
|
|
@@ -58,10 +58,22 @@ module NeverBlock
|
|
|
58
58
|
|
|
59
59
|
# If a connection is available, pass it to the block, otherwise pass
|
|
60
60
|
# the fiber to the queue till a connection is available
|
|
61
|
-
def hold
|
|
62
|
-
fiber = Fiber.current
|
|
61
|
+
def hold
|
|
62
|
+
fiber = NB::Fiber.current
|
|
63
63
|
conn = acquire(fiber)
|
|
64
|
-
|
|
64
|
+
if block_given?
|
|
65
|
+
yield conn
|
|
66
|
+
release(conn)
|
|
67
|
+
else
|
|
68
|
+
conn
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Give the fiber's connection back to the pool
|
|
73
|
+
def release(conn)
|
|
74
|
+
@busy_connections.delete(conn.object_id)
|
|
75
|
+
@connections << conn unless @connections.include? conn
|
|
76
|
+
process_queue
|
|
65
77
|
end
|
|
66
78
|
|
|
67
79
|
def all_connections
|
|
@@ -74,37 +86,14 @@ module NeverBlock
|
|
|
74
86
|
# Can we create one?
|
|
75
87
|
# Wait in the queue then
|
|
76
88
|
def acquire(fiber)
|
|
77
|
-
# A special case for rails when doing ActiveRecord stuff when not yet
|
|
78
|
-
# running in the context of a request (fiber) like in the case of AR
|
|
79
|
-
# queries in environment.rb (Root Fiber)
|
|
80
|
-
return @connections.first unless fiber[:callbacks]
|
|
81
|
-
|
|
82
|
-
fiber[:current_pool_key] = connection_pool_key
|
|
83
|
-
return fiber[connection_pool_key] if fiber[connection_pool_key]
|
|
84
89
|
conn = if !@connections.empty?
|
|
85
90
|
@connections.shift
|
|
86
91
|
elsif (@connections.length + @busy_connections.length) < @size
|
|
87
92
|
@connection_proc.call
|
|
88
93
|
else
|
|
89
|
-
Fiber.yield @queue << fiber
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# They're called in reverse order i.e. release then process_queue
|
|
93
|
-
fiber[:callbacks] << self.method(:process_queue)
|
|
94
|
-
fiber[:callbacks] << self.method(:release)
|
|
95
|
-
|
|
96
|
-
@busy_connections[fiber] = conn
|
|
97
|
-
fiber[connection_pool_key] = conn
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Give the fiber's connection back to the pool
|
|
101
|
-
def release()
|
|
102
|
-
fiber = Fiber.current
|
|
103
|
-
if fiber[connection_pool_key]
|
|
104
|
-
@busy_connections.delete(fiber)
|
|
105
|
-
@connections << fiber[connection_pool_key]
|
|
106
|
-
fiber[connection_pool_key] = nil
|
|
94
|
+
NB::Fiber.yield @queue << fiber
|
|
107
95
|
end
|
|
96
|
+
@busy_connections[conn.object_id] = conn
|
|
108
97
|
end
|
|
109
98
|
|
|
110
99
|
# Check if there are waiting fibers and
|
|
@@ -118,11 +107,7 @@ module NeverBlock
|
|
|
118
107
|
fiber.resume @connections.shift
|
|
119
108
|
end
|
|
120
109
|
end
|
|
121
|
-
|
|
122
|
-
def connection_pool_key
|
|
123
|
-
@connection_pool_key ||= "connection_pool_#{object_id}".intern
|
|
124
|
-
end
|
|
125
|
-
|
|
110
|
+
|
|
126
111
|
end #FiberedConnectionPool
|
|
127
112
|
|
|
128
113
|
end #Pool
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
|
|
2
|
+
# Copyright:: Copyright (c) 2009 eSpace, Inc.
|
|
3
|
+
# License:: Distributes under the same terms as Ruby
|
|
4
|
+
|
|
5
|
+
require 'thread'
|
|
6
|
+
require File.expand_path(File.dirname(__FILE__)+'/io')
|
|
7
|
+
|
|
8
|
+
class File < IO
|
|
9
|
+
|
|
10
|
+
def self.neverblock(*methods)
|
|
11
|
+
methods.each do |method|
|
|
12
|
+
class_eval %{
|
|
13
|
+
def #{method}(*args)
|
|
14
|
+
return rb_#{method}(*args) unless NB.neverblocking?
|
|
15
|
+
NB.defer(self, :#{method}, args)
|
|
16
|
+
end
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
neverblock :syswrite, :sysread, :write, :read, :readline,
|
|
22
|
+
:readlines, :readchar, :gets, :getc, :print
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
require 'fcntl'
|
|
2
|
+
|
|
3
|
+
# This is an extention to the Ruby IO class that makes it compatable with
|
|
4
|
+
# NeverBlocks event loop to avoid blocking IO calls. That's done by delegating
|
|
5
|
+
# Author:: Mohammad A. Ali (mailto:oldmoe@gmail.com)
|
|
6
|
+
# Copyright:: Copyright (c) 2009 eSpace, Inc.
|
|
7
|
+
# License:: Distributes under the same terms as Ruby
|
|
8
|
+
|
|
9
|
+
$:.unshift
|
|
10
|
+
|
|
11
|
+
require File.expand_path(File.dirname(__FILE__)+'/../../neverblock')
|
|
12
|
+
|
|
13
|
+
class IO
|
|
14
|
+
|
|
15
|
+
NB_BUFFER_LENGTH = 128*1024
|
|
16
|
+
|
|
17
|
+
alias_method :rb_sysread, :sysread
|
|
18
|
+
alias_method :rb_syswrite, :syswrite
|
|
19
|
+
alias_method :rb_read, :read
|
|
20
|
+
alias_method :rb_write, :write
|
|
21
|
+
alias_method :rb_gets, :gets
|
|
22
|
+
alias_method :rb_getc, :getc
|
|
23
|
+
alias_method :rb_readchar, :readchar
|
|
24
|
+
alias_method :rb_readline, :readline
|
|
25
|
+
alias_method :rb_readlines, :readlines
|
|
26
|
+
alias_method :rb_print, :print
|
|
27
|
+
|
|
28
|
+
# This method is the delegation method which reads using read_nonblock()
|
|
29
|
+
# and registers the IO call with event loop if the call blocks. The value
|
|
30
|
+
# @immediate_result is used to get the value that method got before it was blocked.
|
|
31
|
+
|
|
32
|
+
def read_neverblock(*args)
|
|
33
|
+
res = ""
|
|
34
|
+
begin
|
|
35
|
+
old_flags = get_flags
|
|
36
|
+
res << read_nonblock(*args)
|
|
37
|
+
set_flags(old_flags)
|
|
38
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
|
|
39
|
+
set_flags(old_flags)
|
|
40
|
+
NB.wait(:read, self)
|
|
41
|
+
retry
|
|
42
|
+
end
|
|
43
|
+
res
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# The is the main reading method that all other methods use.
|
|
47
|
+
# If the mode is set to neverblock it uses the delegation method.
|
|
48
|
+
# Otherwise it uses the original ruby read method.
|
|
49
|
+
|
|
50
|
+
def sysread(length)
|
|
51
|
+
self.neverblock? ? read_neverblock(length) : rb_sysread(length)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def read(length=nil, sbuffer=nil)
|
|
55
|
+
return rb_read(length, sbuffer) if self.file?
|
|
56
|
+
return '' if length == 0
|
|
57
|
+
if sbuffer.nil?
|
|
58
|
+
sbuffer = ''
|
|
59
|
+
else
|
|
60
|
+
sbuffer = sbuffer.to_str
|
|
61
|
+
sbuffer.delete!(sbuffer)
|
|
62
|
+
end
|
|
63
|
+
if length.nil?
|
|
64
|
+
# we need to read till end of stream
|
|
65
|
+
loop do
|
|
66
|
+
begin
|
|
67
|
+
sbuffer << sysread(NB_BUFFER_LENGTH)
|
|
68
|
+
rescue EOFError
|
|
69
|
+
break
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
return sbuffer
|
|
73
|
+
else # length != nil
|
|
74
|
+
if self.buffer.length >= length
|
|
75
|
+
sbuffer << self.buffer.slice!(0, length)
|
|
76
|
+
return sbuffer
|
|
77
|
+
elsif self.buffer.length > 0
|
|
78
|
+
sbuffer << self.buffer
|
|
79
|
+
end
|
|
80
|
+
self.buffer = ''
|
|
81
|
+
remaining_length = length - sbuffer.length
|
|
82
|
+
while sbuffer.length < length && remaining_length > 0
|
|
83
|
+
begin
|
|
84
|
+
sbuffer << sysread(NB_BUFFER_LENGTH < remaining_length ? remaining_length : NB_BUFFER_LENGTH)
|
|
85
|
+
remaining_length = remaining_length - sbuffer.length
|
|
86
|
+
rescue EOFError
|
|
87
|
+
break
|
|
88
|
+
end #begin
|
|
89
|
+
end #while
|
|
90
|
+
end #if length
|
|
91
|
+
return nil if sbuffer.length.zero? && length > 0
|
|
92
|
+
return sbuffer if sbuffer.length <= length
|
|
93
|
+
self.buffer << sbuffer.slice!(length, sbuffer.length-1)
|
|
94
|
+
return sbuffer
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def readpartial(length=nil,sbuffer=nil)
|
|
98
|
+
raise ArgumentError if !length.nil? && length < 0
|
|
99
|
+
if sbuffer.nil?
|
|
100
|
+
sbuffer = ''
|
|
101
|
+
else
|
|
102
|
+
sbuffer = sbuffer.to_str
|
|
103
|
+
sbuffer.delete!(sbuffer)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if self.buffer.length >= length
|
|
107
|
+
sbuffer << self.buffer.slice!(0, length)
|
|
108
|
+
elsif self.buffer.length > 0
|
|
109
|
+
sbuffer << self.buffer.slice!(0, self.buffer.length-1)
|
|
110
|
+
else
|
|
111
|
+
sbuffer << rb_sysread(length)
|
|
112
|
+
end
|
|
113
|
+
return sbuffer
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def write_neverblock(data)
|
|
117
|
+
written = 0
|
|
118
|
+
begin
|
|
119
|
+
old_flags = get_flags
|
|
120
|
+
written = written + write_nonblock(data[written,data.length])
|
|
121
|
+
set_flags(old_flags)
|
|
122
|
+
raise Errno::EAGAIN if written < data.length
|
|
123
|
+
rescue Errno::EWOULDBLOCK, Errno::EAGAIN, Errno::EINTR
|
|
124
|
+
set_flags(old_flags)
|
|
125
|
+
NB.wait(:write, self)
|
|
126
|
+
retry
|
|
127
|
+
end
|
|
128
|
+
written
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def syswrite(*args)
|
|
132
|
+
return rb_syswrite(*args) unless self.neverblock?
|
|
133
|
+
write_neverblock(*args)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def write(data)
|
|
137
|
+
return 0 if data.to_s.empty?
|
|
138
|
+
return rb_write(data) if self.file?
|
|
139
|
+
syswrite(data)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def gets(sep=$/)
|
|
143
|
+
return rb_gets(sep) if self.file?
|
|
144
|
+
res = ""
|
|
145
|
+
sep = "\n\n" if sep == ""
|
|
146
|
+
sep = $/ if sep.nil?
|
|
147
|
+
while res.index(sep).nil?
|
|
148
|
+
break if (c = read(1)).nil?
|
|
149
|
+
res << c
|
|
150
|
+
end
|
|
151
|
+
$_ = res
|
|
152
|
+
res
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def readlines(sep=$/)
|
|
156
|
+
return rb_readlines(sep) if self.file?
|
|
157
|
+
res = []
|
|
158
|
+
begin
|
|
159
|
+
loop{res << readline(sep)}
|
|
160
|
+
rescue EOFError
|
|
161
|
+
end
|
|
162
|
+
res
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def readchar
|
|
166
|
+
return rb_readchar if self.file?
|
|
167
|
+
ch = read(1)
|
|
168
|
+
raise EOFError if ch.nil?
|
|
169
|
+
ch
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def getc
|
|
173
|
+
return rb_getc if self.file?
|
|
174
|
+
begin
|
|
175
|
+
res = readchar
|
|
176
|
+
rescue EOFError
|
|
177
|
+
res = nil
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def readline(sep = $/)
|
|
182
|
+
return rb_readline(sep) if self.file?
|
|
183
|
+
res = gets(sep)
|
|
184
|
+
raise EOFError if res == nil
|
|
185
|
+
res
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def print(*args)
|
|
189
|
+
return rb_print if self.file?
|
|
190
|
+
args.each{|element|syswrite(element)}
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
protected
|
|
194
|
+
|
|
195
|
+
def get_flags
|
|
196
|
+
self.fcntl(Fcntl::F_GETFL, 0)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def set_flags(flags)
|
|
200
|
+
self.fcntl(Fcntl::F_SETFL, flags)
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def buffer
|
|
204
|
+
@buffer ||= ""
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def buffer=(value)
|
|
208
|
+
@buffer = value
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def file?
|
|
212
|
+
@file ||= self.stat.file?
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def neverblock?
|
|
216
|
+
!file? && NB.neverblocking?
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
end
|