lightio 0.3.2 → 0.4.0.pre

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.
@@ -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