green 0.0.1 → 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  ]