packet 0.1.0 → 0.1.1

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