packet 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +4 -0
- data/README +251 -0
- data/Rakefile +87 -0
- data/TODO +0 -0
- data/bin/packet_mongrel.rb +215 -0
- data/bin/runner.rb +35 -0
- data/lib/attribute_accessors.rb +48 -0
- data/lib/bin_parser.rb +61 -0
- data/lib/buftok.rb +127 -0
- data/lib/callback.rb +14 -0
- data/lib/connection.rb +33 -0
- data/lib/core.rb +241 -0
- data/lib/cpu_worker.rb +19 -0
- data/lib/deferrable.rb +210 -0
- data/lib/disconnect_error.rb +8 -0
- data/lib/double_keyed_hash.rb +19 -0
- data/lib/event.rb +25 -0
- data/lib/io_worker.rb +6 -0
- data/lib/meta_pimp.rb +66 -0
- data/lib/nbio.rb +81 -0
- data/lib/packet.rb +37 -0
- data/lib/packet_guid.rb +16 -0
- data/lib/packet_master.rb +148 -0
- data/lib/packet_mongrel.rb +214 -0
- data/lib/periodic_event.rb +27 -0
- data/lib/pimp.rb +31 -0
- data/lib/ruby_hacks.rb +125 -0
- data/lib/worker.rb +120 -0
- metadata +75 -0
data/lib/cpu_worker.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Packet
|
2
|
+
class CPUWorker < Packet::Worker
|
3
|
+
@@worker_type = 'cpu'
|
4
|
+
cattr_accessor :worker_type
|
5
|
+
# this is the place where all the worker specific inititlization has to be done
|
6
|
+
def worker_init
|
7
|
+
@worker_started = true
|
8
|
+
end
|
9
|
+
|
10
|
+
def receive_data p_data
|
11
|
+
p p_data
|
12
|
+
end
|
13
|
+
|
14
|
+
def receive_internal_data p_data
|
15
|
+
p p_data
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
data/lib/deferrable.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
# $Id: deferrable.rb 534 2007-09-15 23:06:15Z blackhedd $
|
2
|
+
#
|
3
|
+
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
+
# Homepage:: http://rubyeventmachine.com
|
5
|
+
# Date:: 16 Jul 2006
|
6
|
+
#
|
7
|
+
# See EventMachine and EventMachine::Connection for documentation and
|
8
|
+
# usage examples.
|
9
|
+
#
|
10
|
+
#----------------------------------------------------------------------------
|
11
|
+
#
|
12
|
+
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
13
|
+
# Gmail: blackhedd
|
14
|
+
#
|
15
|
+
# This program is free software; you can redistribute it and/or modify
|
16
|
+
# it under the terms of either: 1) the GNU General Public License
|
17
|
+
# as published by the Free Software Foundation; either version 2 of the
|
18
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
19
|
+
#
|
20
|
+
# See the file COPYING for complete licensing information.
|
21
|
+
#
|
22
|
+
#---------------------------------------------------------------------------
|
23
|
+
#
|
24
|
+
#
|
25
|
+
|
26
|
+
# FIXME: Packet doesn't support timers on Deferables yet
|
27
|
+
|
28
|
+
require 'forwardable'
|
29
|
+
|
30
|
+
module Packet
|
31
|
+
|
32
|
+
module Deferrable
|
33
|
+
|
34
|
+
# Specify a block to be executed if and when the Deferrable object receives
|
35
|
+
# a status of :succeeded. See #set_deferred_status for more information.
|
36
|
+
#
|
37
|
+
# Calling this method on a Deferrable object whose status is not yet known
|
38
|
+
# will cause the callback block to be stored on an internal list.
|
39
|
+
# If you call this method on a Deferrable whose status is :succeeded, the
|
40
|
+
# block will be executed immediately, receiving the parameters given to the
|
41
|
+
# prior #set_deferred_status call.
|
42
|
+
#
|
43
|
+
#--
|
44
|
+
# If there is no status, add a callback to an internal list.
|
45
|
+
# If status is succeeded, execute the callback immediately.
|
46
|
+
# If status is failed, do nothing.
|
47
|
+
#
|
48
|
+
def callback &block
|
49
|
+
return unless block
|
50
|
+
if @deferred_status == :succeeded
|
51
|
+
block.call(*@deferred_args)
|
52
|
+
elsif @deferred_status != :failed
|
53
|
+
@callbacks ||= []
|
54
|
+
@callbacks.unshift block # << block
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Specify a block to be executed if and when the Deferrable object receives
|
59
|
+
# a status of :failed. See #set_deferred_status for more information.
|
60
|
+
#--
|
61
|
+
# If there is no status, add an errback to an internal list.
|
62
|
+
# If status is failed, execute the errback immediately.
|
63
|
+
# If status is succeeded, do nothing.
|
64
|
+
#
|
65
|
+
def errback &block
|
66
|
+
return unless block
|
67
|
+
if @deferred_status == :failed
|
68
|
+
block.call(*@deferred_args)
|
69
|
+
elsif @deferred_status != :succeeded
|
70
|
+
@errbacks ||= []
|
71
|
+
@errbacks.unshift block # << block
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Sets the "disposition" (status) of the Deferrable object. See also the large set of
|
76
|
+
# sugarings for this method.
|
77
|
+
# Note that if you call this method without arguments,
|
78
|
+
# no arguments will be passed to the callback/errback.
|
79
|
+
# If the user has coded these with arguments, then the
|
80
|
+
# user code will throw an argument exception.
|
81
|
+
# Implementors of deferrable classes <b>must</b>
|
82
|
+
# document the arguments they will supply to user callbacks.
|
83
|
+
#
|
84
|
+
# OBSERVE SOMETHING VERY SPECIAL here: you may call this method even
|
85
|
+
# on the INSIDE of a callback. This is very useful when a previously-registered
|
86
|
+
# callback wants to change the parameters that will be passed to subsequently-registered
|
87
|
+
# ones.
|
88
|
+
#
|
89
|
+
# You may give either :succeeded or :failed as the status argument.
|
90
|
+
#
|
91
|
+
# If you pass :succeeded, then all of the blocks passed to the object using the #callback
|
92
|
+
# method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
|
93
|
+
# passed to the object using #errback will be discarded.
|
94
|
+
#
|
95
|
+
# If you pass :failed, then all of the blocks passed to the object using the #errback
|
96
|
+
# method (if any) will be executed BEFORE the #set_deferred_status method returns. All of the blocks
|
97
|
+
# passed to the object using # callback will be discarded.
|
98
|
+
#
|
99
|
+
# If you pass any arguments to #set_deferred_status in addition to the status argument,
|
100
|
+
# they will be passed as arguments to any callbacks or errbacks that are executed.
|
101
|
+
# It's your responsibility to ensure that the argument lists specified in your callbacks and
|
102
|
+
# errbacks match the arguments given in calls to #set_deferred_status, otherwise Ruby will raise
|
103
|
+
# an ArgumentError.
|
104
|
+
#
|
105
|
+
# --
|
106
|
+
# We're shifting callbacks off and discarding them as we execute them.
|
107
|
+
# This is valid because by definition callbacks are executed no more than
|
108
|
+
# once. It also has the magic effect of permitting recursive calls, which
|
109
|
+
# means that a callback can call #set_deferred_status and change the parameters
|
110
|
+
# that will be sent to subsequent callbacks down the chain.
|
111
|
+
#
|
112
|
+
# Changed @callbacks and @errbacks from push/shift to unshift/pop, per suggestion
|
113
|
+
# by Kirk Haines, to work around the memory leak bug that still exists in many Ruby
|
114
|
+
# versions.
|
115
|
+
#
|
116
|
+
# Changed 15Sep07: after processing callbacks or errbacks, CLEAR the other set of
|
117
|
+
# handlers. This gets us a little closer to the behavior of Twisted's "deferred,"
|
118
|
+
# which only allows status to be set once. Prior to making this change, it was possible
|
119
|
+
# to "succeed" a Deferrable (triggering its callbacks), and then immediately "fail" it,
|
120
|
+
# triggering its errbacks! That is clearly undesirable, but it's just as undesirable
|
121
|
+
# to raise an exception is status is set more than once on a Deferrable. The latter
|
122
|
+
# behavior would invalidate the idiom of resetting arguments by setting status from
|
123
|
+
# within a callback or errback, but more seriously it would cause spurious errors
|
124
|
+
# if a Deferrable was timed out and then an attempt was made to succeed it. See the
|
125
|
+
# comments under the new method #timeout.
|
126
|
+
#
|
127
|
+
def set_deferred_status status, *args
|
128
|
+
#cancel_timeout
|
129
|
+
@deferred_status = status
|
130
|
+
@deferred_args = args
|
131
|
+
case @deferred_status
|
132
|
+
when :succeeded
|
133
|
+
if @callbacks
|
134
|
+
while cb = @callbacks.pop
|
135
|
+
cb.call(*@deferred_args)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
@errbacks.clear if @errbacks
|
139
|
+
when :failed
|
140
|
+
if @errbacks
|
141
|
+
while eb = @errbacks.pop
|
142
|
+
eb.call(*@deferred_args)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
@callbacks.clear if @callbacks
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# Setting a timeout on a Deferrable causes it to go into the failed state after
|
151
|
+
# the Timeout expires (passing no arguments to the object's errbacks).
|
152
|
+
# Setting the status at any time prior to a call to the expiration of the timeout
|
153
|
+
# will cause the timer to be cancelled.
|
154
|
+
#--
|
155
|
+
#
|
156
|
+
#
|
157
|
+
def timeout seconds
|
158
|
+
cancel_timeout
|
159
|
+
me = self
|
160
|
+
@deferred_timeout = EventMachine::Timer.new(seconds) {me.fail}
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# Cancels an outstanding timeout if any. Undoes the action of #timeout.
|
165
|
+
#
|
166
|
+
#
|
167
|
+
def cancel_timeout
|
168
|
+
if @deferred_timeout
|
169
|
+
@deferred_timeout.cancel
|
170
|
+
@deferred_timeout = nil
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
|
175
|
+
# Equivalent to set_deferred_status(:succeeded, ...)
|
176
|
+
#
|
177
|
+
def set_deferred_success *args
|
178
|
+
set_deferred_status :succeeded, *args
|
179
|
+
end
|
180
|
+
|
181
|
+
# Equivalent to set_deferred_status(:failed, ...)
|
182
|
+
#
|
183
|
+
def set_deferred_failure *args
|
184
|
+
set_deferred_status :failed, *args
|
185
|
+
end
|
186
|
+
|
187
|
+
# And still more sugar
|
188
|
+
#
|
189
|
+
def succeed *args
|
190
|
+
set_deferred_success(*args)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Can't get enough sugar
|
194
|
+
#
|
195
|
+
def fail *args
|
196
|
+
set_deferred_failure(*args)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
# DefaultDeferrable is an otherwise empty class that includes Deferrable.
|
202
|
+
# This is very useful when you just need to return a Deferrable object
|
203
|
+
# as a way of communicating deferred status to some other part of a program.
|
204
|
+
#
|
205
|
+
class DefaultDeferrable
|
206
|
+
include Deferrable
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class DoubleKeyedHash
|
2
|
+
def initialize
|
3
|
+
@keys1 = {}
|
4
|
+
@internal_hash = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def []=(key1,key2,value)
|
8
|
+
@keys1[key2] = key1
|
9
|
+
@internal_hash[key1] = value
|
10
|
+
end
|
11
|
+
|
12
|
+
def [] key
|
13
|
+
@internal_hash[key] || @internal_hash[@keys1[key]]
|
14
|
+
end
|
15
|
+
|
16
|
+
def each
|
17
|
+
@internal_hash.each { |key,value| yield(key,value)}
|
18
|
+
end
|
19
|
+
end
|
data/lib/event.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Packet
|
2
|
+
class Event
|
3
|
+
attr_accessor :timer_signature, :block, :cancel_flag
|
4
|
+
def initialize(elapsed_time,&block)
|
5
|
+
@cancel_flag = false
|
6
|
+
@timer_signature = Guid.hexdigest
|
7
|
+
@block = block
|
8
|
+
@scheduled_time = Time.now + elapsed_time
|
9
|
+
end
|
10
|
+
|
11
|
+
def run_now?
|
12
|
+
return true if @scheduled_time <= Time.now
|
13
|
+
return false
|
14
|
+
end
|
15
|
+
|
16
|
+
def cancel
|
17
|
+
@cancel_flag = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def run
|
21
|
+
@block.call
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
# WOW
|
data/lib/io_worker.rb
ADDED
data/lib/meta_pimp.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Class acts as a pimp for workers, which doesn't have a manually created pimp
|
2
|
+
# The idea behind a manually added pimp is to let client handle low level messaging
|
3
|
+
# beween workers. A meta pimp, does it for you.
|
4
|
+
class Packet::MetaPimp < Packet::Pimp
|
5
|
+
# initializer of pimp
|
6
|
+
attr_accessor :callback_hash
|
7
|
+
attr_accessor :worker_status
|
8
|
+
def pimp_init
|
9
|
+
@callback_hash ||= {}
|
10
|
+
@worker_status = nil
|
11
|
+
@tokenizer = BinParser.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# 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
|
+
def receive_data p_data
|
18
|
+
@tokenizer.extract(p_data) do |b_data|
|
19
|
+
t_data = Marshal.load(b_data)
|
20
|
+
handle_object(t_data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def handle_object data_options = {}
|
25
|
+
case data_options[:type]
|
26
|
+
when :request
|
27
|
+
process_request(data_options)
|
28
|
+
when :response
|
29
|
+
process_response(data_options)
|
30
|
+
when :status
|
31
|
+
save_worker_status(data_options)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_worker_status(data_options = { })
|
36
|
+
@worker_status = data_options[:data]
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_request(data_options = {})
|
40
|
+
if requested_worker = data_options[:requested_worker]
|
41
|
+
reactor.live_workers[requested_worker].send_request(data_options)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_response(data_options = {})
|
46
|
+
if callback_signature = data_options[:callback_signature]
|
47
|
+
callback = callback_hash[callback_signature]
|
48
|
+
callback.call(data_options)
|
49
|
+
elsif client_signature = data_options[:client_signature]
|
50
|
+
reactor.connections[client_signature].instance.worker_receive(data_options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# can be used to send request to correspoding worker
|
55
|
+
def send_request(data_options = { })
|
56
|
+
if callback = data_options[:callback]
|
57
|
+
callback_hash[callback.signature] = callback
|
58
|
+
data_options.delete(:callback)
|
59
|
+
data_options[:callback_signature] = callback.signature
|
60
|
+
send_data(data_options)
|
61
|
+
else
|
62
|
+
send_data(data_options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
data/lib/nbio.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module Packet
|
2
|
+
module NbioHelper
|
3
|
+
# nonblocking method of reading data
|
4
|
+
# when method returns nil it probably means that client diconnected
|
5
|
+
def read_data(t_sock)
|
6
|
+
sock_data = ""
|
7
|
+
begin
|
8
|
+
while(sock_data << t_sock.read_nonblock(1023)); end
|
9
|
+
rescue Errno::EAGAIN
|
10
|
+
return sock_data
|
11
|
+
rescue
|
12
|
+
raise DisconnectError.new(t_sock)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def write_data(p_data,p_sock)
|
17
|
+
return unless p_data
|
18
|
+
if p_data.is_a? Fixnum
|
19
|
+
t_data = p_data.to_s
|
20
|
+
else
|
21
|
+
t_data = p_data.dup.to_s
|
22
|
+
end
|
23
|
+
t_length = t_data.length
|
24
|
+
begin
|
25
|
+
p_sock.write_nonblock(t_data)
|
26
|
+
rescue Errno::EAGAIN
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
# loop do
|
31
|
+
# begin
|
32
|
+
# written_length = p_sock.write_nonblock(t_data)
|
33
|
+
# rescue Errno::EAGAIN
|
34
|
+
# break
|
35
|
+
# end
|
36
|
+
# break if written_length >= t_length
|
37
|
+
# t_data = t_data[written_length..-1]
|
38
|
+
# break if t_data.empty?
|
39
|
+
# t_length = t_data.length
|
40
|
+
# end
|
41
|
+
end
|
42
|
+
|
43
|
+
# method writes data to socket in a non blocking manner, but doesn't care if there is a error writing data
|
44
|
+
def write_once(p_data,p_sock)
|
45
|
+
t_data = p_data.dup.to_s
|
46
|
+
begin
|
47
|
+
p_sock.write_nonblock(t_data)
|
48
|
+
rescue Errno::EAGAIN
|
49
|
+
return
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# method dumps the object in a protocol format which can be easily picked by a recursive descent parser
|
54
|
+
def dump_object(p_data,p_sock)
|
55
|
+
object_dump = Marshal.dump(p_data)
|
56
|
+
dump_length = object_dump.length.to_s
|
57
|
+
length_str = dump_length.rjust(9,'0')
|
58
|
+
final_data = length_str + object_dump
|
59
|
+
|
60
|
+
# total_length = final_data.length
|
61
|
+
# loop do
|
62
|
+
# begin
|
63
|
+
# written_length = p_sock.write_nonblock(final_data)
|
64
|
+
# rescue Errno::EAGAIN
|
65
|
+
# break
|
66
|
+
# end
|
67
|
+
# break if written_length >= total_length
|
68
|
+
# final_data = final_data[written_length..-1]
|
69
|
+
# break if final_data.empty?
|
70
|
+
# total_length = final_data.length
|
71
|
+
# end
|
72
|
+
|
73
|
+
begin
|
74
|
+
p_sock.write_nonblock(final_data)
|
75
|
+
rescue Errno::EAGAIN
|
76
|
+
return
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/packet.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "yaml"
|
3
|
+
require "forwardable"
|
4
|
+
require "attribute_accessors"
|
5
|
+
require "buftok"
|
6
|
+
require "bin_parser"
|
7
|
+
|
8
|
+
require "ostruct"
|
9
|
+
require "socket"
|
10
|
+
|
11
|
+
require "packet_guid"
|
12
|
+
require "ruby_hacks"
|
13
|
+
require "double_keyed_hash"
|
14
|
+
require "event"
|
15
|
+
|
16
|
+
require "periodic_event"
|
17
|
+
require "disconnect_error"
|
18
|
+
require "callback"
|
19
|
+
|
20
|
+
require "nbio"
|
21
|
+
require "pimp"
|
22
|
+
require "meta_pimp"
|
23
|
+
require "core"
|
24
|
+
|
25
|
+
require "packet_master"
|
26
|
+
require "connection"
|
27
|
+
require "worker"
|
28
|
+
|
29
|
+
# This file is just a runner of things and hence does basic initialization of thingies required for running
|
30
|
+
# the application.
|
31
|
+
|
32
|
+
|
33
|
+
PACKET_APP = File.expand_path'../' unless defined?(PACKET_APP)
|
34
|
+
|
35
|
+
module Packet
|
36
|
+
VERSION='0.1.0'
|
37
|
+
end
|