lightio 0.3.2 → 0.4.0.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,6 +5,11 @@ module LightIO::Library
5
5
  ErrNoWaitingThread = ::ThreadsWait::ErrNoWaitingThread
6
6
  ErrNoFinishedThread = ::ThreadsWait::ErrNoFinishedThread
7
7
 
8
+ extend Base::MockMethods
9
+ mock ::ThreadsWait
10
+
11
+ extend LightIO::Module::ThreadsWait::ClassMethods
12
+
8
13
  attr_reader :threads
9
14
 
10
15
  def initialize(*threads)
@@ -36,7 +41,7 @@ module LightIO::Library
36
41
  end
37
42
 
38
43
  def next_wait(nonblock=nil)
39
- raise ThreadsWait::ErrNoWaitingThread, 'No threads for waiting.' if empty?
44
+ raise ::ThreadsWait::ErrNoWaitingThread, 'No threads for waiting.' if empty?
40
45
  @threads.each do |thr|
41
46
  if thr.alive? && nonblock
42
47
  next
@@ -47,13 +52,7 @@ module LightIO::Library
47
52
  @threads.delete(thr)
48
53
  return thr
49
54
  end
50
- raise ThreadsWait::ErrNoFinishedThread, 'No finished threads.'
51
- end
52
-
53
- class << self
54
- def all_waits(*threads, &blk)
55
- new(*threads).all_waits(&blk)
56
- end
55
+ raise ::ThreadsWait::ErrNoFinishedThread, 'No finished threads.'
57
56
  end
58
57
  end
59
58
  end
@@ -0,0 +1,11 @@
1
+ require_relative 'module/base'
2
+ require_relative 'module/io'
3
+ require_relative 'module/file'
4
+ require_relative 'module/socket'
5
+ require_relative 'module/thread'
6
+ require_relative 'module/threads_wait'
7
+
8
+ module LightIO
9
+ module Module
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ module LightIO::Module
2
+ module Base
3
+ module NewHelper
4
+ protected
5
+ def define_new_for_modules(*mods)
6
+ mods.each {|mod| define_new_for_module(mod)}
7
+ end
8
+
9
+ def define_new_for_module(mod)
10
+ LightIO::Module.send(:module_eval, <<-STR, __FILE__, __LINE__ + 1)
11
+ module #{mod}
12
+ module ClassMethods
13
+ def new(*args, &blk)
14
+ obj = LightIO::Library::#{mod}.__send__ :allocate
15
+ obj.__send__ :initialize, *args, &blk
16
+ obj
17
+ end
18
+ end
19
+ end
20
+ STR
21
+ end
22
+ end
23
+
24
+ module Helper
25
+ protected
26
+ def wrap_to_library(obj)
27
+ return _wrap(obj) if self.respond_to?(:_wrap)
28
+ find_library_class._wrap(obj)
29
+ end
30
+
31
+ # private
32
+ def find_library_class
33
+ name = self.name
34
+ namespace_index = name.rindex("::")
35
+ class_name = namespace_index.nil? ? name : name[(namespace_index + 2)..-1]
36
+ LightIO::Library.const_get(class_name)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,10 @@
1
+ module LightIO::Module
2
+ extend Base::NewHelper
3
+
4
+ define_new_for_module "File"
5
+ module File
6
+ include Base
7
+ module ClassMethods
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,88 @@
1
+ module LightIO::Module
2
+ extend Base::NewHelper
3
+
4
+ define_new_for_module "IO"
5
+
6
+ module IO
7
+ include LightIO::Module::Base
8
+
9
+ class << self
10
+ # helper methods
11
+ def convert_to_io(io)
12
+ unless io.respond_to?(:to_io)
13
+ raise TypeError, "no implicit conversion of #{io.class} into IO"
14
+ end
15
+ to_io = io.to_io
16
+ unless to_io.is_a?(LightIO::Library::IO)
17
+ raise TypeError, "can't process raw IO, use LightIO::IO._wrap(obj) to wrap it" if to_io.is_a?(::IO)
18
+ raise TypeError, "can't convert #{io.class} to IO (#{io.class}#to_io gives #{to_io.class})"
19
+ end
20
+ to_io
21
+ end
22
+
23
+ def get_io_watcher(io)
24
+ unless io.is_a?(LightIO::Library::IO)
25
+ io = convert_to_io(io)
26
+ end
27
+ io.__send__(:io_watcher)
28
+ end
29
+ end
30
+
31
+ module ClassMethods
32
+ include LightIO::Module::Base::Helper
33
+
34
+ def open(*args)
35
+ io = self.new(*args)
36
+ return io unless block_given?
37
+ begin
38
+ yield io
39
+ ensure
40
+ io.close if io.respond_to? :close
41
+ end
42
+ end
43
+
44
+ def pipe(*args)
45
+ r, w = origin_pipe(*args)
46
+ if block_given?
47
+ begin
48
+ return yield r, w
49
+ ensure
50
+ w.close
51
+ r.close
52
+ end
53
+ end
54
+ # [r, w]
55
+ [wrap_to_library(r), wrap_to_library(w)]
56
+ end
57
+
58
+ def select(read_fds, write_fds=nil, _except_fds=nil, timeout=nil)
59
+ timer = timeout && Time.now
60
+ read_fds ||= []
61
+ write_fds ||= []
62
+ loop do
63
+ # make sure io registered, then clear io watcher status
64
+ read_fds.each {|fd| LightIO::Module::IO.get_io_watcher(fd).tap {|io| io.readable?; io.clear_status}}
65
+ write_fds.each {|fd| LightIO::Module::IO.get_io_watcher(fd).tap {|io| io.writable?; io.clear_status}}
66
+ # run ioloop once
67
+ LightIO.sleep 0
68
+ r_fds = read_fds.select {|fd|
69
+ io = LightIO::Module::IO.convert_to_io(fd)
70
+ io.closed? ? raise(IOError, 'closed stream') : LightIO::Module::IO.get_io_watcher(io).readable?
71
+ }
72
+ w_fds = write_fds.select {|fd|
73
+ io = LightIO::Module::IO.convert_to_io(fd)
74
+ io.closed? ? raise(IOError, 'closed stream') : LightIO::Module::IO.get_io_watcher(io).writable?
75
+ }
76
+ e_fds = []
77
+ if r_fds.empty? && w_fds.empty?
78
+ if timeout && Time.now - timer > timeout
79
+ return nil
80
+ end
81
+ else
82
+ return [r_fds, w_fds, e_fds]
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,165 @@
1
+ require 'socket'
2
+
3
+ module LightIO::Module
4
+ extend Base::NewHelper
5
+
6
+ define_new_for_modules *%w{Addrinfo Socket IPSocket TCPSocket TCPServer UDPSocket UNIXSocket UNIXServer}
7
+
8
+ module Addrinfo
9
+ include LightIO::Module::Base
10
+
11
+ module WrapperHelper
12
+ protected
13
+ include LightIO::Module::Base::Helper
14
+
15
+ def wrap_socket_method(method)
16
+ define_method method do |*args|
17
+ socket = wrap_to_library(@obj.send(method, *args))
18
+ if block_given?
19
+ begin
20
+ yield socket
21
+ ensure
22
+ socket.close
23
+ end
24
+ else
25
+ socket
26
+ end
27
+ end
28
+ end
29
+
30
+ def wrap_socket_methods(*methods)
31
+ methods.each {|m| wrap_socket_method(m)}
32
+ end
33
+
34
+ def wrap_addrinfo_return_method(method)
35
+ define_method method do |*args|
36
+ result = @obj.send(method, *args)
37
+ if result.is_a?(::Addrinfo)
38
+ wrap_to_library(result)
39
+ elsif result.respond_to?(:map)
40
+ result.map {|r| wrap_to_library(r)}
41
+ else
42
+ result
43
+ end
44
+ end
45
+ end
46
+
47
+ def wrap_addrinfo_return_methods(*methods)
48
+ methods.each {|m| wrap_addrinfo_return_method(m)}
49
+ end
50
+
51
+ def wrap_class_addrinfo_return_method(method)
52
+ define_method method do |*args|
53
+ result = __send__(:"origin_#{method}", *args)
54
+ if result.is_a?(::Addrinfo)
55
+ wrap_to_library(result)
56
+ elsif result.respond_to?(:map)
57
+ result.map {|r| wrap_to_library(r)}
58
+ else
59
+ result
60
+ end
61
+ end
62
+ end
63
+
64
+ def wrap_class_addrinfo_return_methods(*methods)
65
+ methods.each {|m| wrap_class_addrinfo_return_method(m)}
66
+ end
67
+ end
68
+
69
+ module ClassMethods
70
+ extend WrapperHelper
71
+
72
+ def foreach(*args, &block)
73
+ Addrinfo.getaddrinfo(*args).each(&block)
74
+ end
75
+
76
+ wrap_class_addrinfo_return_methods :getaddrinfo, :ip, :udp, :tcp, :unix
77
+ end
78
+ end
79
+
80
+ module BasicSocket
81
+ include LightIO::Module::Base
82
+
83
+ module ClassMethods
84
+ include LightIO::Module::Base::Helper
85
+
86
+ def for_fd(fd)
87
+ wrap_to_library(origin_for_fd(fd))
88
+ end
89
+ end
90
+ end
91
+
92
+ module Socket
93
+ include LightIO::Module::Base
94
+
95
+ module ClassMethods
96
+ include LightIO::Module::Base::Helper
97
+ extend LightIO::Wrap::Wrapper::HelperMethods
98
+ ## implement ::Socket class methods
99
+ wrap_methods_run_in_threads_pool :getaddrinfo, :gethostbyaddr, :gethostbyname, :gethostname,
100
+ :getnameinfo, :getservbyname
101
+
102
+ def getifaddrs
103
+ origin_getifaddrs.map {|ifaddr| LightIO::Library::Socket::Ifaddr._wrap(ifaddr)}
104
+ end
105
+
106
+ def socketpair(domain, type, protocol)
107
+ origin_socketpair(domain, type, protocol).map {|s| wrap_to_library(s)}
108
+ end
109
+
110
+ alias_method :pair, :socketpair
111
+
112
+ def unix_server_socket(path)
113
+ if block_given?
114
+ origin_unix_server_socket(path) {|s| yield wrap_to_library(s)}
115
+ else
116
+ wrap_to_library(origin_unix_server_socket(path))
117
+ end
118
+ end
119
+
120
+ def ip_sockets_port0(ai_list, reuseaddr)
121
+ origin_ip_sockets_port0(ai_list, reuseaddr).map {|s| wrap_to_library(s)}
122
+ end
123
+ end
124
+ end
125
+
126
+
127
+ module IPSocket
128
+ include LightIO::Module::Base
129
+
130
+ module ClassMethods
131
+ extend LightIO::Wrap::Wrapper::HelperMethods
132
+ wrap_methods_run_in_threads_pool :getaddress
133
+ end
134
+ end
135
+
136
+ module TCPSocket
137
+ include LightIO::Module::Base
138
+ end
139
+
140
+ module TCPServer
141
+ include LightIO::Module::Base
142
+ end
143
+
144
+ module UDPSocket
145
+ include LightIO::Module::Base
146
+ end
147
+
148
+ module UNIXSocket
149
+ include LightIO::Module::Base
150
+
151
+ module ClassMethods
152
+ include LightIO::Module::Base::Helper
153
+
154
+ def socketpair(*args)
155
+ origin_socketpair(*args).map {|io| wrap_to_library(io)}
156
+ end
157
+
158
+ alias_method :pair, :socketpair
159
+ end
160
+ end
161
+
162
+ module UNIXServer
163
+ include LightIO::Module::Base
164
+ end
165
+ end
@@ -0,0 +1,76 @@
1
+ require 'thread'
2
+
3
+ module LightIO::Module
4
+ extend Base::NewHelper
5
+
6
+ define_new_for_modules *%w{ThreadGroup Mutex Queue SizedQueue ConditionVariable}
7
+
8
+ module Thread
9
+ include LightIO::Module::Base
10
+
11
+ module ClassMethods
12
+ extend Forwardable
13
+
14
+ def new(*args, &blk)
15
+ obj = LightIO::Library::Thread.__send__ :allocate
16
+ obj.__send__ :initialize, *args, &blk
17
+ obj
18
+ end
19
+
20
+ def fork(*args, &blk)
21
+ obj = LightIO::Library::Thread.__send__ :allocate
22
+ obj.send(:init_core, *args, &blk)
23
+ obj
24
+ end
25
+
26
+ alias start fork
27
+
28
+ def kill(thr)
29
+ thr.kill
30
+ end
31
+
32
+ def current
33
+ return main if LightIO::Core::LightFiber.is_root?(Fiber.current)
34
+ LightIO::Library::Thread.instance_variable_get(:@current_thread) || origin_current
35
+ end
36
+
37
+ def exclusive(&blk)
38
+ LightIO::Library::Thread.__send__(:thread_mutex).synchronize(&blk)
39
+ end
40
+
41
+ def list
42
+ thread_list = []
43
+ LightIO::Library::Thread.__send__(:threads).keys.each {|id|
44
+ begin
45
+ thr = ObjectSpace._id2ref(id)
46
+ unless thr.alive?
47
+ # manually remove thr from threads
48
+ thr.kill
49
+ next
50
+ end
51
+ thread_list << thr
52
+ rescue RangeError
53
+ # mean object is recycled
54
+ # just wait ruby GC call finalizer to remove it from threads
55
+ next
56
+ end
57
+ }
58
+ thread_list
59
+ end
60
+
61
+ def pass
62
+ LightIO::Beam.pass
63
+ end
64
+
65
+ alias stop pass
66
+
67
+ def finalizer(object_id)
68
+ proc {LightIO::Library::Thread.__send__(:threads).delete(object_id)}
69
+ end
70
+
71
+ def main
72
+ origin_main
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,13 @@
1
+ require 'thwait'
2
+
3
+ module LightIO::Module
4
+ module ThreadsWait
5
+ include LightIO::Module::Base
6
+
7
+ module ClassMethods
8
+ def all_waits(*threads, &blk)
9
+ LightIO::Library::ThreadsWait.new(*threads).all_waits(&blk)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,150 @@
1
+ module LightIO
2
+ module Monkey
3
+ class PatchError < StandardError
4
+ end
5
+
6
+ IO_PATCH_CONSTANTS = %w{IO File Socket Socket::Ifaddr TCPServer TCPSocket BasicSocket Addrinfo IPSocket UDPSocket UNIXSocket UNIXServer}.freeze
7
+ THREAD_PATCH_CONSTANTS = %w{Thread ThreadGroup Queue SizedQueue ConditionVariable Mutex ThreadsWait}.freeze
8
+
9
+ @patched
10
+
11
+ class << self
12
+ def patch_all!
13
+ patch_thread!
14
+ patch_io!
15
+ patch_kernel!
16
+ end
17
+
18
+ def unpatch_all!
19
+ unpatch_thread!
20
+ unpatch_io!
21
+ unpatch_kernel!
22
+ end
23
+
24
+ def patched?(obj)
25
+ patched.key?(obj) && !patched[obj]&.empty?
26
+ end
27
+
28
+ def patch_thread!
29
+ require 'thread'
30
+ THREAD_PATCH_CONSTANTS.each {|klass_name| patch!(klass_name)}
31
+ patch_method!(Timeout, :timeout, LightIO::Timeout.method(:timeout))
32
+ end
33
+
34
+ def unpatch_thread!
35
+ require 'thread'
36
+ THREAD_PATCH_CONSTANTS.each {|klass_name| unpatch!(klass_name)}
37
+ unpatch_method!(Timeout, :timeout)
38
+ end
39
+
40
+ def patch_io!
41
+ require 'socket'
42
+ IO_PATCH_CONSTANTS.each {|klass_name| patch!(klass_name)}
43
+ end
44
+
45
+ def unpatch_io!
46
+ require 'socket'
47
+ IO_PATCH_CONSTANTS.each {|klass_name| unpatch!(klass_name)}
48
+ end
49
+
50
+ def patch_kernel!
51
+ patch_method!(Kernel, :sleep, LightIO.method(:sleep))
52
+ patch_method!(Kernel, :select, LightIO::Library::IO.method(:select))
53
+ patch_instance_method!(Kernel, :sleep, LightIO.method(:sleep))
54
+ patch_instance_method!(Kernel, :select, LightIO::Library::IO.method(:select))
55
+ end
56
+
57
+ def unpatch_kernel!
58
+ unpatch_method!(Kernel, :sleep, LightIO.method(:sleep))
59
+ unpatch_method!(Kernel, :select, LightIO::Library::IO.method(:select))
60
+ unpatch_instance_method!(Kernel, :sleep, LightIO.method(:sleep))
61
+ unpatch_instance_method!(Kernel, :select, LightIO::Library::IO.method(:select))
62
+ end
63
+
64
+ private
65
+ def patch!(klass_name)
66
+ klass = Object.const_get(klass_name)
67
+ raise PatchError, "already patched constant #{klass}" if patched?(klass)
68
+ patched[klass] = {}
69
+ class_methods_module = find_class_methods_module(klass_name)
70
+ methods = class_methods_module && find_monkey_patch_class_methods(klass_name)
71
+ return unless class_methods_module && methods
72
+ methods.each do |method_name|
73
+ method = class_methods_module.instance_method(method_name)
74
+ patch_method!(klass, method_name, method)
75
+ end
76
+ rescue
77
+ patched.delete(klass)
78
+ raise
79
+ end
80
+
81
+ def unpatch!(klass_name)
82
+ klass = Object.const_get(klass_name)
83
+ raise PatchError, "can't find patched constant #{klass}" unless patched?(klass)
84
+ unpatch_method!(klass, :new)
85
+ find_monkey_patch_class_methods(klass_name).each do |method_name|
86
+ unpatch_method!(klass, method_name)
87
+ end
88
+ patched.delete(klass)
89
+ end
90
+
91
+ def find_class_methods_module(klass_name)
92
+ LightIO::Module.const_get("#{klass_name}::ClassMethods", false)
93
+ rescue NameError
94
+ nil
95
+ end
96
+
97
+ def find_monkey_patch_class_methods(klass_name)
98
+ find_class_methods_module(klass_name).instance_methods
99
+ end
100
+
101
+ def patched_method?(obj, method)
102
+ patched?(obj) && patched[obj].key?(method)
103
+ end
104
+
105
+ def patched_methods(const)
106
+ patched[const] ||= {}
107
+ end
108
+
109
+ def patch_method!(const, method, patched_method)
110
+ raise PatchError, "already patched method #{const}.#{method}" if patched_method?(const, method)
111
+ patched_methods(const)[method] = patched_method
112
+ const.send(:define_singleton_method, method, patched_method)
113
+ nil
114
+ end
115
+
116
+ def unpatch_method!(const, method)
117
+ raise PatchError, "can't find patched method #{const}.#{method}" unless patched_method?(const, method)
118
+ origin_method = patched_methods(const).delete(method)
119
+ const.send(:define_singleton_method, method, origin_method)
120
+ nil
121
+ end
122
+
123
+ def patched_instance_method?(obj, method)
124
+ patched_instance_methods(obj).key?(method)
125
+ end
126
+
127
+ def patched_instance_methods(const)
128
+ (patched[:instance_methods] ||= {})[const] ||= {}
129
+ end
130
+
131
+ def patch_instance_method!(const, method, patched_method)
132
+ raise PatchError, "already patched method #{const}.#{method}" if patched_instance_method?(const, method)
133
+ patched_instance_methods(const)[method] = patched_method
134
+ const.send(:define_method, method, patched_method)
135
+ nil
136
+ end
137
+
138
+ def unpatch_instance_method!(const, method)
139
+ raise PatchError, "can't find patched method #{const}.#{method}" unless patched_instance_method?(const, method)
140
+ origin_method = patched_instance_methods(const).delete(method)
141
+ const.send(:define_method, method, origin_method)
142
+ nil
143
+ end
144
+
145
+ def patched
146
+ @patched ||= {}
147
+ end
148
+ end
149
+ end
150
+ end