packet 0.1.0 → 0.1.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,4 @@
1
+ #ifndef EXTCONF_H
2
+ #define EXTCONF_H
3
+ #define HAVE_SYS_EPOLL_H 1
4
+ #endif
@@ -0,0 +1,10 @@
1
+ require 'mkmf'
2
+
3
+ unless have_header("sys/epoll.h") || have_header("sys/event.h")
4
+ message "epoll or kqueue required.\n"
5
+ exit 1
6
+ end
7
+
8
+ create_header
9
+ create_makefile 'asteroid'
10
+
@@ -0,0 +1,24 @@
1
+ require "rubygems"
2
+ require "eventmachine"
3
+
4
+ module Client
5
+ def receive_data data
6
+ @tokenizer.extract(data).each do |b_data|
7
+ client_data = b_data
8
+ p "Client has sent #{client_data}"
9
+ end
10
+ end
11
+
12
+ def start_push_on_client
13
+ send_data("Hello World : #{Time.now}\n")
14
+ end
15
+
16
+ def post_init
17
+ @tokenizer = BufferedTokenizer.new
18
+ EM::add_periodic_timer(1) { start_push_on_client }
19
+ end
20
+ end
21
+
22
+ EM.run do
23
+ EM.start_server("localhost",11009,Client)
24
+ end
@@ -0,0 +1,19 @@
1
+ require "asteroid"
2
+
3
+ module Server
4
+ def receive_data data
5
+
6
+ end
7
+
8
+ def post_init
9
+ puts "A new client connected"
10
+ end
11
+
12
+ def unbind
13
+ puts "client disconnected"
14
+ end
15
+ end
16
+
17
+ Asteroid::run("0.0.0.0", 11007, Server) do
18
+ puts "Someone connected"
19
+ end
@@ -0,0 +1,3 @@
1
+ require "concurrent_thread"
2
+ a = ConcurrentThread.new
3
+ a.defer
@@ -1,4 +1,3 @@
1
- # FIXME: write this stupid parser in C.
2
1
  class BinParser
3
2
  def initialize
4
3
  @size = 0
@@ -10,8 +9,8 @@ class BinParser
10
9
  @numeric_length = 0
11
10
  end
12
11
 
13
- # if you look at it, it could be a suicidal function
14
- def extract new_data
12
+ def extract new_data, &block
13
+ extracter_block = block
15
14
  if @parser_state == 0
16
15
  length_to_read = 9 - @length_string.length
17
16
  len_str,remaining = new_data.unpack("a#{length_to_read}a*")
@@ -25,13 +24,22 @@ class BinParser
25
24
  if remaining.length < @numeric_length
26
25
  @data << remaining
27
26
  @numeric_length = @numeric_length - remaining.length
28
- else
27
+ elsif remaining.length == @numeric_length
29
28
  @data << remaining
30
- yield(@data.join)
29
+ extracter_block.call(@data.join)
30
+ @data = []
31
+ @parser_state = 0
32
+ @length_string = ""
33
+ @numeric_length = 0
34
+ else
35
+ pack_data,remaining = remaining.unpack("a#{@numeric_length}a*")
36
+ @data << pack_data
37
+ extracter_block.call(@data.join)
31
38
  @data = []
32
39
  @parser_state = 0
33
40
  @length_string = ""
34
41
  @numeric_length = 0
42
+ extract(remaining,&extracter_block)
35
43
  end
36
44
  end
37
45
  elsif @parser_state == 1
@@ -41,19 +49,19 @@ class BinParser
41
49
  @numeric_length = @numeric_length - pack_data.length
42
50
  elsif pack_data.length == @numeric_length
43
51
  @data << pack_data
44
- yield(@data.join)
52
+ extracter_block.call(@data.join)
45
53
  @data = []
46
54
  @parser_state = 0
47
55
  @length_string = ""
48
56
  @numeric_length = 0
49
57
  else
50
58
  @data << pack_data
51
- yield(@data.join)
59
+ extracter_block.call(@data.join)
52
60
  @data = []
53
61
  @parser_state = 0
54
62
  @length_string = ""
55
63
  @numeric_length = 0
56
- extract(remaining)
64
+ extract(remaining,&extracter_block)
57
65
  end
58
66
  end
59
67
  end
@@ -0,0 +1,74 @@
1
+ module Packet
2
+ module ClassHelpers
3
+ def metaclass; class << self; self; end; end
4
+
5
+ def iattr_accessor *args
6
+ metaclass.instance_eval do
7
+ attr_accessor *args
8
+ args.each do |attr|
9
+ define_method("set_#{attr}") do |b_value|
10
+ self.send("#{attr}=",b_value)
11
+ end
12
+ end
13
+ end
14
+
15
+ args.each do |attr|
16
+ class_eval do
17
+ define_method(attr) do
18
+ self.class.send(attr)
19
+ end
20
+ define_method("#{attr}=") do |b_value|
21
+ self.class.send("#{attr}=",b_value)
22
+ end
23
+ end
24
+ end
25
+ end # end of method iattr_accessor
26
+
27
+ def cattr_reader(*syms)
28
+ syms.flatten.each do |sym|
29
+ next if sym.is_a?(Hash)
30
+ class_eval(<<-EOS, __FILE__, __LINE__)
31
+ unless defined? @@#{sym}
32
+ @@#{sym} = nil
33
+ end
34
+
35
+ def self.#{sym}
36
+ @@#{sym}
37
+ end
38
+
39
+ def #{sym}
40
+ @@#{sym}
41
+ end
42
+ EOS
43
+ end
44
+ end
45
+
46
+ def cattr_writer(*syms)
47
+ options = syms.last.is_a?(Hash) ? syms.pop : {}
48
+ syms.flatten.each do |sym|
49
+ class_eval(<<-EOS, __FILE__, __LINE__)
50
+ unless defined? @@#{sym}
51
+ @@#{sym} = nil
52
+ end
53
+
54
+ def self.#{sym}=(obj)
55
+ @@#{sym} = obj
56
+ end
57
+
58
+ #{"
59
+ def #{sym}=(obj)
60
+ @@#{sym} = obj
61
+ end
62
+ " unless options[:instance_writer] == false }
63
+ EOS
64
+ end
65
+ end
66
+
67
+ def cattr_accessor(*syms)
68
+ cattr_reader(*syms)
69
+ cattr_writer(*syms)
70
+ end
71
+ module_function :metaclass,:iattr_accessor, :cattr_writer, :cattr_reader, :cattr_accessor
72
+ end # end of module ClassHelpers
73
+ end
74
+
@@ -2,32 +2,32 @@
2
2
  # main eventloop.
3
3
 
4
4
  module Packet
5
- class Connection
6
- # method gets called when connection to external server is completed
7
- def connection_completed
8
-
9
- end
10
-
11
- # method gets called when external client is disconnected
12
- def unbind
13
-
5
+ module Connection
6
+ def send_data p_data
7
+ begin
8
+ write_data(p_data,connection)
9
+ rescue DisconnectError => sock_error
10
+ close_connection
11
+ end
14
12
  end
15
13
 
16
- # method gets called just at the beginning of initializing things.
17
- def post_init
18
-
14
+ def invoke_init
15
+ @initialized = true
16
+ post_init if respond_to?(:post_init)
19
17
  end
20
18
 
21
- def send_data
22
-
19
+ def close_connection
20
+ unbind if respond_to?(:unbind)
21
+ reactor.remove_connection(connection)
23
22
  end
24
23
 
25
- def ask_worker
26
-
24
+ def close_connection_after_writing
25
+ connection.flush
26
+ close_connection
27
27
  end
28
28
 
29
- def receive_data
30
-
29
+ def send_object p_object
30
+ dump_object(p_object,connection)
31
31
  end
32
32
  end # end of class Connection
33
33
  end # end of module Packet
@@ -4,17 +4,19 @@ module Packet
4
4
  def self.included(base_klass)
5
5
  base_klass.extend(ClassMethods)
6
6
  base_klass.instance_eval do
7
- # iattr_accessor :connection_callbacks
8
7
  @@connection_callbacks ||= {}
8
+
9
+ iattr_accessor :thread_pool_size
9
10
  cattr_accessor :connection_callbacks
10
11
  attr_accessor :read_ios, :write_ios, :listen_sockets
11
12
  attr_accessor :connection_completion_awaited
12
- attr_accessor :connections
13
+ attr_accessor :connections, :thread_pool, :windows_flag
13
14
  include CommonMethods
14
15
  end
15
16
  end
16
17
 
17
18
  module ClassMethods
19
+ include Packet::ClassHelpers
18
20
  def after_connection p_method
19
21
  connection_callbacks[:after_connection] ||= []
20
22
  connection_callbacks[:after_connection] << p_method
@@ -64,9 +66,9 @@ module Packet
64
66
 
65
67
  def accept_connection(sock_opts)
66
68
  sock_io = sock_opts[:socket]
67
-
68
69
  begin
69
70
  client_socket,client_sockaddr = sock_io.accept_nonblock
71
+ client_socket.setsockopt(Socket::IPPROTO_TCP,Socket::TCP_NODELAY,1)
70
72
  rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
71
73
  puts "not ready yet"
72
74
  return
@@ -87,7 +89,8 @@ module Packet
87
89
 
88
90
  read_ios << t_sock if actually_connected
89
91
  write_ios.delete(t_sock)
90
- decorate_handler(t_sock,actually_connected,sock_opts[:sock_addr],sock_opts[:module],&sock_opts[:block])
92
+ decorate_handler(t_sock,actually_connected,sock_opts[:sock_addr],\
93
+ sock_opts[:module],&sock_opts[:block])
91
94
  connection_completion_awaited.delete(t_sock.fileno)
92
95
  end
93
96
 
@@ -110,12 +113,9 @@ module Packet
110
113
 
111
114
  # method opens a socket for listening
112
115
  def start_server(ip,port,t_module,&block)
113
- t_socket = Socket.new(Socket::AF_INET,Socket::SOCK_STREAM,0)
114
- t_socket.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR,true)
115
- sockaddr = Socket.sockaddr_in(port.to_i,ip)
116
- t_socket.bind(sockaddr)
117
- t_socket.listen(50)
118
- t_socket.setsockopt(Socket::IPPROTO_TCP,Socket::TCP_NODELAY,1)
116
+ BasicSocket.do_not_reverse_lookup = true
117
+ t_socket = TCPServer.new(ip,port.to_i)
118
+ # t_socket.setsockopt(*@tcp_defer_accept_opts) rescue nil
119
119
  listen_sockets[t_socket.fileno] = { :socket => t_socket,:block => block,:module => t_module }
120
120
  @read_ios << t_socket
121
121
  end
@@ -126,11 +126,15 @@ module Packet
126
126
  Signal.trap("INT") { shutdown }
127
127
  loop do
128
128
  check_for_timer_events
129
+ user_thread_window #=> let user level threads run for a while
129
130
  ready_fds = select(@read_ios,@write_ios,nil,0.005)
130
- next if ready_fds.blank?
131
+ #next if ready_fds.blank?
132
+
133
+ next if !ready_fds or ready_fds.empty?
134
+
131
135
  ready_fds = ready_fds.flatten.compact
132
136
  ready_fds.each do |t_sock|
133
- if t_sock.is_a? UNIXSocket
137
+ if(unix? && t_sock.is_a?(UNIXSocket))
134
138
  handle_internal_messages(t_sock)
135
139
  else
136
140
  handle_external_messages(t_sock)
@@ -139,11 +143,18 @@ module Packet
139
143
  end
140
144
  end
141
145
 
146
+ def user_thread_window
147
+ # run_user_threads if respond_to?(:run_user_threads)
148
+ @thread_pool.exclusive_run
149
+ end
150
+
142
151
  def terminate_me
152
+ # FIXME: close the open sockets
143
153
  exit
144
154
  end
145
155
 
146
156
  def shutdown
157
+ # FIXME: close the open sockets
147
158
  exit
148
159
  end
149
160
 
@@ -182,6 +193,7 @@ module Packet
182
193
 
183
194
  def add_timer(elapsed_time,&block)
184
195
  t_timer = Event.new(elapsed_time,&block)
196
+ # @timer_hash.store(timer)
185
197
  @timer_hash[t_timer.timer_signature] = t_timer
186
198
  return t_timer
187
199
  end
@@ -197,11 +209,24 @@ module Packet
197
209
  @connections ||= {}
198
210
  @listen_sockets ||= {}
199
211
 
212
+ # @timer_hash = Packet::TimerStore
200
213
  @timer_hash ||= {}
214
+ @thread_pool = ThreadPool.new(thread_pool_size || 20)
215
+ @windows_flag = windows?
216
+ end
217
+
218
+ def windows?
219
+ return true if RUBY_PLATFORM =~ /win32/i
220
+ return false
221
+ end
222
+
223
+ def unix?
224
+ !@windows_flag
201
225
  end
202
226
 
203
227
  def check_for_timer_events
204
228
  @timer_hash.each do |key,timer|
229
+ @timer_hash.delete(key) if timer.cancel_flag
205
230
  if timer.run_now?
206
231
  timer.run
207
232
  @timer_hash.delete(key) if !timer.respond_to?(:interval)
@@ -231,7 +256,7 @@ module Packet
231
256
  t_signature = Guid.hexdigest
232
257
  handler_instance.signature = t_signature
233
258
  connections[t_socket.fileno] =
234
- OpenStruct.new( :socket => t_socket, :instance => handler_instance, :signature => t_signature,:sock_addr => sock_addr)
259
+ OpenStruct.new(:socket => t_socket, :instance => handler_instance, :signature => t_signature,:sock_addr => sock_addr)
235
260
  block.call(handler_instance) if block
236
261
  handler_instance.connection_completed if handler_instance.respond_to?(:connection_completed)
237
262
  end
@@ -1,4 +1,5 @@
1
1
  class DoubleKeyedHash
2
+ attr_accessor :internal_hash
2
3
  def initialize
3
4
  @keys1 = {}
4
5
  @internal_hash = {}
@@ -13,6 +14,11 @@ class DoubleKeyedHash
13
14
  @internal_hash[key] || @internal_hash[@keys1[key]]
14
15
  end
15
16
 
17
+ def delete(key)
18
+ @keys1.delete(key)
19
+ @internal_hash.delete(key)
20
+ end
21
+
16
22
  def each
17
23
  @internal_hash.each { |key,value| yield(key,value)}
18
24
  end
@@ -4,16 +4,16 @@
4
4
  class Packet::MetaPimp < Packet::Pimp
5
5
  # initializer of pimp
6
6
  attr_accessor :callback_hash
7
- attr_accessor :worker_status
7
+ attr_accessor :worker_status, :worker_key
8
8
  def pimp_init
9
9
  @callback_hash ||= {}
10
10
  @worker_status = nil
11
+ @worker_result = nil
12
+ @worker_key = nil
11
13
  @tokenizer = BinParser.new
12
14
  end
13
15
 
14
16
  # will be invoked whenever there is a response from the worker
15
- # Possible bug with reading large responses.
16
- # response would be binary but base64 encoded and must be decoded and then loaded
17
17
  def receive_data p_data
18
18
  @tokenizer.extract(p_data) do |b_data|
19
19
  t_data = Marshal.load(b_data)
@@ -29,11 +29,18 @@ class Packet::MetaPimp < Packet::Pimp
29
29
  process_response(data_options)
30
30
  when :status
31
31
  save_worker_status(data_options)
32
+ when :result
33
+ save_worker_result(data_options)
32
34
  end
33
35
  end
34
36
 
37
+ def save_worker_result(data_options = { })
38
+ @worker_result = data_options[:data]
39
+ end
40
+
35
41
  def save_worker_status(data_options = { })
36
- @worker_status = data_options[:data]
42
+ # @worker_status = data_options[:data]
43
+ reactor.update_result(worker_key,data_options[:data])
37
44
  end
38
45
 
39
46
  def process_request(data_options = {})
@@ -45,9 +52,16 @@ class Packet::MetaPimp < Packet::Pimp
45
52
  def process_response(data_options = {})
46
53
  if callback_signature = data_options[:callback_signature]
47
54
  callback = callback_hash[callback_signature]
48
- callback.call(data_options)
55
+ # there coule be bug when you are trying to send the data back to the client
56
+ begin
57
+ callback.invoke(data_options)
58
+ rescue
59
+ end
49
60
  elsif client_signature = data_options[:client_signature]
50
- reactor.connections[client_signature].instance.worker_receive(data_options)
61
+ begin
62
+ reactor.connections[client_signature].instance.worker_receive(data_options)
63
+ rescue
64
+ end
51
65
  end
52
66
  end
53
67