green 0.0.1 → 0.1

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.
@@ -0,0 +1,147 @@
1
+ require 'nio'
2
+ require 'algorithms'
3
+
4
+ class Green
5
+ class Hub
6
+ class Nio4r < Hub
7
+ class SocketWaiter < Green::SocketWaiter
8
+ attr_reader :reactor
9
+ def initialize(reactor, socket)
10
+ super socket
11
+ @reactor = reactor
12
+ end
13
+
14
+ def wait_read
15
+ make_monitor :r
16
+ end
17
+
18
+ def wait_write
19
+ make_monitor :w
20
+ end
21
+
22
+ def make_monitor(interest)
23
+ m = reactor.selector.register(socket, interest)
24
+ g = Green.current
25
+ m.value = proc { g.switch }
26
+ Green.hub.switch
27
+ ensure
28
+ reactor.selector.deregister socket
29
+ end
30
+ end
31
+
32
+ class Timer
33
+ attr_reader :reactor, :fire_at, :clb
34
+ def initialize(reactor, fire_at, &clb)
35
+ @reactor, @fire_at, @clb = reactor, fire_at, clb
36
+ end
37
+
38
+ def run
39
+ @runned = true
40
+ clb.call
41
+ end
42
+
43
+ def green_cancel
44
+ return if @canceled || @runned
45
+ @canceled = true
46
+ reactor.cancel_timer self
47
+ end
48
+
49
+ def <=>(v)
50
+ @fire_at <=> v.fire_at
51
+ end
52
+ end
53
+
54
+ MIN_TIMEOUT = 0.0001
55
+ MAX_TIMEOUT = 0.01
56
+
57
+ attr_reader :callbacks, :timers, :cancel_timers
58
+ def initialize(*args)
59
+ @callbacks = []
60
+ @timers = Containers::MinHeap.new
61
+ @cancel_timers = {}
62
+ super
63
+ end
64
+
65
+ def reactor_running?
66
+ @reactor_running
67
+ end
68
+
69
+ def run
70
+ @reactor_running = true
71
+ @selector = NIO::Selector.new
72
+ while @reactor_running
73
+ run_callbacks
74
+ run_timers
75
+ @selector.select(time_till_first_event) do |m|
76
+ m.value.call
77
+ end
78
+ end
79
+ end
80
+
81
+ def run_timers
82
+ now = Time.now
83
+ while (t = @timers.next)
84
+ if t.fire_at <= now
85
+ @timers.pop
86
+ if @cancel_timers[t]
87
+ @cancel_timers.delete t
88
+ else
89
+ t.run
90
+ end
91
+ else
92
+ break
93
+ end
94
+ end
95
+ end
96
+
97
+ def run_callbacks
98
+ jobs, @callbacks = @callbacks, []
99
+ begin
100
+ i = 0
101
+ while i < jobs.size
102
+ job = jobs[i]
103
+ jobs[i] = nil
104
+ job.call
105
+ i += 1
106
+ end
107
+ ensure
108
+ @callbacks[0...0] = jobs[i..-1] if i < jobs.size
109
+ end
110
+ end
111
+
112
+ def time_till_first_event
113
+ if @callbacks.size > 0
114
+ MIN_TIMEOUT
115
+ elsif @timers.size > 0
116
+ @timers.next.fire_at - Time.now + MIN_TIMEOUT
117
+ else
118
+ MAX_TIMEOUT
119
+ end
120
+ end
121
+
122
+ def selector
123
+ @selector
124
+ end
125
+
126
+ def cancel_timer(timer)
127
+ @cancel_timers[timer] = true
128
+ end
129
+
130
+ def timer(n, &blk)
131
+ @timers << Timer.new(self, Time.now + n, &blk)
132
+ end
133
+
134
+ def callback(cb=nil, &blk)
135
+ @callbacks << (cb || blk)
136
+ end
137
+
138
+ def socket_waiter(socket)
139
+ SocketWaiter.new self, socket
140
+ end
141
+
142
+ def stop
143
+ @reactor_running = false
144
+ end
145
+ end
146
+ end
147
+ end
data/lib/green/hub.rb CHANGED
@@ -14,17 +14,18 @@ class Green
14
14
  g.switch
15
15
  end
16
16
 
17
- def wait(waiter, *args)
17
+ def wait(proc = nil, &cancel_clb)
18
18
  switch
19
19
  rescue => e
20
- waiter.green_cancel(*args)
20
+ (proc || cancel_clb).call
21
21
  raise e
22
22
  end
23
23
 
24
24
  def sleep(n)
25
25
  g = Green.current
26
26
  t = timer(n) { g.switch }
27
- wait t
27
+ wait { t.green_cancel }
28
+ t
28
29
  end
29
30
 
30
31
  def run
@@ -38,5 +39,13 @@ class Green
38
39
  def callback(&blk)
39
40
  raise "override"
40
41
  end
42
+
43
+ def socket_waiter(socket)
44
+ raise "override"
45
+ end
46
+
47
+ def stop
48
+ raise "override"
49
+ end
41
50
  end
42
51
  end
data/lib/green/monkey.rb CHANGED
@@ -1,13 +1,11 @@
1
- class Green
2
- module Monkey
3
- extend self
4
-
5
- def patch_constants
6
-
7
- end
8
-
9
- def patch_socket
10
-
11
- end
12
- end
13
- end
1
+ require 'green/socket'
2
+
3
+ original_verbosity = $VERBOSE
4
+ $VERBOSE = nil
5
+
6
+ Socket = Green::Socket
7
+ TCPSocket = Green::TCPSocket
8
+ TCPServer = Green::TCPServer
9
+
10
+
11
+ $VERBOSE = original_verbosity
@@ -0,0 +1,16 @@
1
+ require 'mysql2'
2
+ class Green
3
+ module Mysql2
4
+ class Client < ::Mysql2::Client
5
+ def query(sql, opts={})
6
+ super(sql, opts.merge(:async => true))
7
+ green_waiter.wait_read
8
+ async_result
9
+ end
10
+
11
+ def green_waiter
12
+ @green_waiter ||= Green.hub.socket_waiter(Socket.for_fd(self.socket))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,12 +1,16 @@
1
1
  class Green
2
2
  class Semaphore
3
- include Green::Waiter
4
- attr_accessor :counter
3
+ attr_accessor :counter, :value
5
4
  def initialize(value = 1)
5
+ @value = value
6
6
  @counter = value
7
7
  @links = []
8
8
  end
9
9
 
10
+ def wait_links
11
+ @wait_links ||= []
12
+ end
13
+
10
14
  def acquire
11
15
  if counter > 0
12
16
  self.counter -= 1
@@ -14,7 +18,7 @@ class Green
14
18
  else
15
19
  g = Green.current
16
20
  clb = rawlink { g.switch }
17
- Green.hub.wait self, clb
21
+ Green.hub.wait { unlink clb }
18
22
  self.counter -= 1
19
23
  true
20
24
  end
@@ -22,10 +26,11 @@ class Green
22
26
 
23
27
  def release
24
28
  self.counter += 1
29
+ wait_links.dup.each(&:call)
25
30
  if @links.size > 0
26
31
  l = @links.pop
27
32
  Green.hub.callback { l.call }
28
- end
33
+ end
29
34
  end
30
35
 
31
36
  def rawlink(&clb)
@@ -37,8 +42,133 @@ class Green
37
42
  @links.delete clb
38
43
  end
39
44
 
40
- def green_cancel(clb)
41
- unlink clb
45
+ def wait(v = value)
46
+ if counter >= v
47
+ return counter
48
+ else
49
+ g = Green.current
50
+ clb = proc do
51
+ if counter >= v && @links.size == 0
52
+ wait_links.delete clb
53
+ Green.hub.callback { g.switch }
54
+ end
55
+ end
56
+ wait_links << clb
57
+ Green.hub.wait { wait_links.delete clb }
58
+ end
59
+ end
60
+
61
+ def wait_avaliable
62
+ wait value - 1
63
+ end
64
+ end
65
+
66
+ # With Ruby compatible API
67
+ class Mutex < Semaphore
68
+ def initialize
69
+ super 1
70
+ @slept = {}
71
+ end
72
+
73
+ def synchronize
74
+ lock
75
+ yield
76
+ ensure
77
+ unlock
78
+ end
79
+
80
+ def lock
81
+ if Green.current.locals["mutex_locked_#{self.object_id}"]
82
+ Green.current.locals.delete "mutex_locked_#{self.object_id}"
83
+ raise Green::GreenError.new
84
+ end
85
+ Green.current.locals["mutex_locked_#{self.object_id}"] = true
86
+ acquire
87
+ end
88
+
89
+ def unlock
90
+ raise Green::GreenError.new unless Green.current.locals["mutex_locked_#{self.object_id}"]
91
+ Green.current.locals.delete "mutex_locked_#{self.object_id}"
92
+ release
93
+ end
94
+
95
+ def _wakeup(green)
96
+ if @slept.delete(green)
97
+ Green.hub.callback { green.switch }
98
+ end
99
+ end
100
+
101
+ def sleep(timeout = nil)
102
+ unlock
103
+ beg = Time.now
104
+ current = Green.current
105
+ @slept[current] = true
106
+ begin
107
+ if timeout
108
+ t = Green.hub.timer(timeout) { _wakeup(current) }
109
+ Green.hub.switch
110
+ t.green_cancel
111
+ else
112
+ Green.hub.switch
113
+ end
114
+ ensure
115
+ @slept.delete current
116
+ end
117
+ yield if block_given?
118
+ lock
119
+ Time.now - beg
120
+ end
121
+ end
122
+
123
+ class ConditionVariable
124
+ def initialize
125
+ @waiters = []
126
+ end
127
+
128
+ #
129
+ # Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
130
+ #
131
+ # If +timeout+ is given, this method returns after +timeout+ seconds passed,
132
+ # even if no other thread doesn't signal.
133
+ #
134
+ def wait(mutex, timeout=nil)
135
+ current = Green.current
136
+ pair = [mutex, current]
137
+ @waiters << pair
138
+ mutex.sleep timeout do
139
+ @waiters.delete pair
140
+ end
141
+ self
142
+ end
143
+
144
+ def _wakeup(mutex, green)
145
+ if alive = green.alive?
146
+ Green.hub.callback {
147
+ mutex._wakeup(green)
148
+ }
149
+ end
150
+ alive
151
+ end
152
+
153
+ #
154
+ # Wakes up the first thread in line waiting for this lock.
155
+ #
156
+ def signal
157
+ while (pair = @waiters.shift)
158
+ break if _wakeup(*pair)
159
+ end
160
+ self
161
+ end
162
+
163
+ #
164
+ # Wakes up all threads waiting for this lock.
165
+ #
166
+ def broadcast
167
+ @waiters.each do |mutex, green|
168
+ _wakeup(mutex, green)
169
+ end
170
+ @waiters.clear
171
+ self
42
172
  end
43
173
  end
44
174
  end
@@ -0,0 +1,124 @@
1
+ require "fcntl"
2
+ require 'socket'
3
+ require 'kgio'
4
+
5
+ class Green
6
+
7
+ ERRORS = Errno::constants.each_with_object({}) do |c, h|
8
+ const = Errno.const_get(c)
9
+ h[const::Errno] = const
10
+ end
11
+
12
+ # TODO puts
13
+ class Socket < ::Socket
14
+ READ_BUFFER_SIZE = 65536
15
+
16
+ include Kgio::SocketMethods
17
+
18
+ def write(str)
19
+ kgio_write(str)
20
+ str.bytesize
21
+ end
22
+
23
+ def read(length = nil, buffer = nil)
24
+ res = []
25
+ readed = 0
26
+ begin # begin ... while
27
+ need_read = if length
28
+ length - readed
29
+ else
30
+ READ_BUFFER_SIZE
31
+ end
32
+ data = kgio_read(need_read)
33
+ if data.nil? && length.nil? && readed == 0
34
+ return ''
35
+ elsif data.nil? && readed == 0
36
+ return nil
37
+ elsif data.nil?
38
+ return buffer ? buffer.replace(res * '') : res * ''
39
+ else
40
+ readed += data.size
41
+ res << data
42
+ end
43
+ end while length != readed
44
+ return buffer ? buffer.replace(res * '') : res * ''
45
+ end
46
+
47
+ def recv(maxlen, flags = 0)
48
+ recv_nonblock(maxlen)
49
+ rescue Errno::EAGAIN
50
+ waiter.wait_read
51
+ retry
52
+ end
53
+
54
+ def send(mesg, flags, dest_sockaddr = nil)
55
+ # FIXME
56
+ write mesg
57
+ end
58
+
59
+ def kgio_wait_readable
60
+ waiter.wait_write
61
+ end
62
+
63
+ def kgio_wait_readable
64
+ waiter.wait_read
65
+ end
66
+
67
+ def self.accept_socket_class
68
+ self
69
+ end
70
+
71
+ def accept
72
+ s, a = accept_nonblock
73
+ [self.class.accept_socket_class.for_fd(s.fileno), a]
74
+ rescue Errno::EAGAIN
75
+ waiter.wait_read
76
+ retry
77
+ end
78
+
79
+ def connect(sock_addr)
80
+ connect_nonblock(sock_addr)
81
+ rescue Errno::EINPROGRESS
82
+ error, = getsockopt(::Socket::SOL_SOCKET, ::Socket::SO_ERROR).unpack('i')
83
+ if error != 0
84
+ raise ERRORS[error]
85
+ else
86
+ waiter.wait_write
87
+ end
88
+ end
89
+
90
+ def waiter
91
+ @waiter ||= Green.hub.socket_waiter(self)
92
+ end
93
+ end
94
+
95
+ class TCPSocket < Socket
96
+ def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
97
+ addrinfo = Addrinfo.tcp(remote_host, remote_port)
98
+ super(addrinfo.ipv4? ? :INET : :INET6, :STREAM, 0)
99
+ if local_host && local_port
100
+ bind(Addrinfo.tcp(local_host, local_port))
101
+ end
102
+ connect addrinfo
103
+ end
104
+ end
105
+
106
+ class TCPServer < Socket
107
+ def initialize(host, port)
108
+ addrinfo = Addrinfo.tcp(host, port)
109
+ super(addrinfo.ipv4? ? :INET : :INET6, :STREAM, 0)
110
+ setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
111
+ bind(addrinfo)
112
+ listen(5)
113
+ end
114
+
115
+
116
+ def self.accept_socket_class
117
+ TCPSocket
118
+ end
119
+
120
+ def accept
121
+ super[0]
122
+ end
123
+ end
124
+ end
data/lib/green/zmq.rb ADDED
@@ -0,0 +1,92 @@
1
+ require 'green'
2
+ require 'ffi-rzmq'
3
+
4
+ class Green
5
+ module ZMQ
6
+ class Waiter
7
+ attr_reader :socket_waiter, :waiters
8
+ def initialize(fd)
9
+ @socket_waiter = Green.hub.socket_waiter(Socket.for_fd(fd))
10
+ @waiters = []
11
+ @notifier = Green.spawn do
12
+ while true
13
+ @socket_waiter.wait_read
14
+ wake
15
+ end
16
+ end
17
+ end
18
+
19
+ def lock
20
+ g = Green.current
21
+ @waiters << g
22
+ Green.hub.wait { @waiters.delete g }
23
+ end
24
+
25
+ def wake
26
+ w = @waiters.shift
27
+ Green.hub.callback { w.switch } if w
28
+ end
29
+
30
+ def cancel
31
+ @notifier.kill
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ module ZMQ
38
+ class Context
39
+ alias :original_terminate :terminate
40
+ def terminate
41
+ Green.hub.callback do
42
+ original_terminate
43
+ end
44
+ end
45
+ end
46
+
47
+ class Socket
48
+ def initialize(*args)
49
+ super
50
+ fd, = [].tap { |a| getsockopt(ZMQ::FD, a) }
51
+ @waiter = Green::ZMQ::Waiter.new fd
52
+ end
53
+
54
+ alias :bsendmsg :sendmsg
55
+ def sendmsg(message, flags = 0)
56
+ return bsendmsg(message, flags) if (flags & ZMQ::NonBlocking) != 0
57
+ flags |= ZMQ::NonBlocking
58
+ loop do
59
+ rc = bsendmsg message, flags
60
+ if rc == -1 && ZMQ::Util.errno == EAGAIN
61
+ @waiter.lock
62
+ else
63
+ @waiter.wake
64
+ return rc
65
+ end
66
+ end
67
+ end
68
+
69
+ alias :brecvmsg :recvmsg
70
+ def recvmsg(message, flags = 0)
71
+ return brecvmsg(message, flags) if (flags & ZMQ::NonBlocking) != 0
72
+ flags |= ZMQ::NonBlocking
73
+ loop do
74
+ rc = brecvmsg message, flags
75
+ if rc == -1 && ZMQ::Util.errno == EAGAIN
76
+ @waiter.lock
77
+ else
78
+ @waiter.wake
79
+ return rc
80
+ end
81
+ end
82
+ end
83
+
84
+ alias :original_close :close
85
+ def close
86
+ @waiter.cancel
87
+ Green.hub.callback do
88
+ original_close
89
+ end
90
+ end
91
+ end
92
+ end
@@ -12,15 +12,14 @@ module EventMachine
12
12
  alias :a#{type} :#{type}
13
13
  def #{type}(options = {}, &blk)
14
14
  g = Green.current
15
-
16
15
  conn = setup_request(:#{type}, options, &blk)
17
16
  if conn.error.nil?
18
17
  conn.callback { g.switch(conn) }
19
- conn.errback { g.throw(HTTPException.new(conn)) }
18
+ conn.errback { g.throw(HTTPException.new) }
20
19
 
21
- Green.hub.wait(conn)
20
+ Green.hub.wait { conn.green_cancel }
22
21
  else
23
- raise HTTPException.new(conn)
22
+ raise HTTPException.new
24
23
  end
25
24
  end
26
25
  ]