thrift 0.2.0 → 0.2.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,278 @@
1
+ require 'thrift/server'
2
+ require 'logger'
3
+ require 'thread'
4
+
5
+ module Thrift
6
+ # this class expects to always use a FramedTransport for reading messages
7
+ class NonblockingServer < Server
8
+ def initialize(processor, serverTransport, transportFactory=nil, protocolFactory=nil, num=20, logger = nil)
9
+ super(processor, serverTransport, transportFactory, protocolFactory)
10
+ @num_threads = num
11
+ if logger.nil?
12
+ @logger = Logger.new(STDERR)
13
+ @logger.level = Logger::WARN
14
+ else
15
+ @logger = logger
16
+ end
17
+ @shutdown_semaphore = Mutex.new
18
+ @transport_semaphore = Mutex.new
19
+ end
20
+
21
+ def serve
22
+ @logger.info "Starting #{self}"
23
+ @serverTransport.listen
24
+ @io_manager = start_io_manager
25
+
26
+ begin
27
+ loop do
28
+ break if @serverTransport.closed?
29
+ rd, = select([@serverTransport], nil, nil, 0.1)
30
+ next if rd.nil?
31
+ socket = @serverTransport.accept
32
+ @logger.debug "Accepted socket: #{socket.inspect}"
33
+ @io_manager.add_connection socket
34
+ end
35
+ rescue IOError => e
36
+ end
37
+ # we must be shutting down
38
+ @logger.info "#{self} is shutting down, goodbye"
39
+ ensure
40
+ @transport_semaphore.synchronize do
41
+ @serverTransport.close
42
+ end
43
+ @io_manager.ensure_closed unless @io_manager.nil?
44
+ end
45
+
46
+ def shutdown(timeout = 0, block = true)
47
+ @shutdown_semaphore.synchronize do
48
+ return if @is_shutdown
49
+ @is_shutdown = true
50
+ end
51
+ # nonblocking is intended for calling from within a Handler
52
+ # but we can't change the order of operations here, so lets thread
53
+ shutdown_proc = lambda do
54
+ @io_manager.shutdown(timeout)
55
+ @transport_semaphore.synchronize do
56
+ @serverTransport.close # this will break the accept loop
57
+ end
58
+ end
59
+ if block
60
+ shutdown_proc.call
61
+ else
62
+ Thread.new &shutdown_proc
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def start_io_manager
69
+ iom = IOManager.new(@processor, @serverTransport, @transportFactory, @protocolFactory, @num_threads, @logger)
70
+ iom.spawn
71
+ iom
72
+ end
73
+
74
+ class IOManager # :nodoc:
75
+ DEFAULT_BUFFER = 2**20
76
+
77
+ def initialize(processor, serverTransport, transportFactory, protocolFactory, num, logger)
78
+ @processor = processor
79
+ @serverTransport = serverTransport
80
+ @transportFactory = transportFactory
81
+ @protocolFactory = protocolFactory
82
+ @num_threads = num
83
+ @logger = logger
84
+ @connections = []
85
+ @buffers = Hash.new { |h,k| h[k] = '' }
86
+ @signal_queue = Queue.new
87
+ @signal_pipes = IO.pipe
88
+ @signal_pipes[1].sync = true
89
+ @worker_queue = Queue.new
90
+ @shutdown_queue = Queue.new
91
+ end
92
+
93
+ def add_connection(socket)
94
+ signal [:connection, socket]
95
+ end
96
+
97
+ def spawn
98
+ @iom_thread = Thread.new do
99
+ @logger.debug "Starting #{self}"
100
+ run
101
+ end
102
+ end
103
+
104
+ def shutdown(timeout = 0)
105
+ @logger.debug "#{self} is shutting down workers"
106
+ @worker_queue.clear
107
+ @num_threads.times { @worker_queue.push [:shutdown] }
108
+ signal [:shutdown, timeout]
109
+ @shutdown_queue.pop
110
+ @signal_pipes[0].close
111
+ @signal_pipes[1].close
112
+ @logger.debug "#{self} is shutting down, goodbye"
113
+ end
114
+
115
+ def ensure_closed
116
+ kill_worker_threads if @worker_threads
117
+ @iom_thread.kill
118
+ end
119
+
120
+ private
121
+
122
+ def run
123
+ spin_worker_threads
124
+
125
+ loop do
126
+ rd, = select([@signal_pipes[0], *@connections])
127
+ if rd.delete @signal_pipes[0]
128
+ break if read_signals == :shutdown
129
+ end
130
+ rd.each do |fd|
131
+ if fd.handle.eof?
132
+ remove_connection fd
133
+ else
134
+ read_connection fd
135
+ end
136
+ end
137
+ end
138
+ join_worker_threads(@shutdown_timeout)
139
+ ensure
140
+ @shutdown_queue.push :shutdown
141
+ end
142
+
143
+ def read_connection(fd)
144
+ @buffers[fd] << fd.read(DEFAULT_BUFFER)
145
+ frame = slice_frame!(@buffers[fd])
146
+ if frame
147
+ @logger.debug "#{self} is processing a frame"
148
+ @worker_queue.push [:frame, fd, frame]
149
+ end
150
+ end
151
+
152
+ def spin_worker_threads
153
+ @logger.debug "#{self} is spinning up worker threads"
154
+ @worker_threads = []
155
+ @num_threads.times do
156
+ @worker_threads << spin_thread
157
+ end
158
+ end
159
+
160
+ def spin_thread
161
+ Worker.new(@processor, @transportFactory, @protocolFactory, @logger, @worker_queue).spawn
162
+ end
163
+
164
+ def signal(msg)
165
+ @signal_queue << msg
166
+ @signal_pipes[1].write " "
167
+ end
168
+
169
+ def read_signals
170
+ # clear the signal pipe
171
+ # note that since read_nonblock is broken in jruby,
172
+ # we can only read up to a set number of signals at once
173
+ sigstr = @signal_pipes[0].readpartial(1024)
174
+ # now read the signals
175
+ begin
176
+ sigstr.length.times do
177
+ signal, obj = @signal_queue.pop(true)
178
+ case signal
179
+ when :connection
180
+ @connections << obj
181
+ when :shutdown
182
+ @shutdown_timeout = obj
183
+ return :shutdown
184
+ end
185
+ end
186
+ rescue ThreadError
187
+ # out of signals
188
+ # note that in a perfect world this would never happen, since we're
189
+ # only reading the number of signals pushed on the pipe, but given the lack
190
+ # of locks, in theory we could clear the pipe/queue while a new signal is being
191
+ # placed on the pipe, at which point our next read_signals would hit this error
192
+ end
193
+ end
194
+
195
+ def remove_connection(fd)
196
+ # don't explicitly close it, a thread may still be writing to it
197
+ @connections.delete fd
198
+ @buffers.delete fd
199
+ end
200
+
201
+ def join_worker_threads(shutdown_timeout)
202
+ start = Time.now
203
+ @worker_threads.each do |t|
204
+ if shutdown_timeout > 0
205
+ timeout = (start + shutdown_timeout) - Time.now
206
+ break if timeout <= 0
207
+ t.join(timeout)
208
+ else
209
+ t.join
210
+ end
211
+ end
212
+ kill_worker_threads
213
+ end
214
+
215
+ def kill_worker_threads
216
+ @worker_threads.each do |t|
217
+ t.kill if t.status
218
+ end
219
+ @worker_threads.clear
220
+ end
221
+
222
+ def slice_frame!(buf)
223
+ if buf.length >= 4
224
+ size = buf.unpack('N').first
225
+ if buf.length >= size + 4
226
+ buf.slice!(0, size + 4)
227
+ else
228
+ nil
229
+ end
230
+ else
231
+ nil
232
+ end
233
+ end
234
+
235
+ class Worker # :nodoc:
236
+ def initialize(processor, transportFactory, protocolFactory, logger, queue)
237
+ @processor = processor
238
+ @transportFactory = transportFactory
239
+ @protocolFactory = protocolFactory
240
+ @logger = logger
241
+ @queue = queue
242
+ end
243
+
244
+ def spawn
245
+ Thread.new do
246
+ @logger.debug "#{self} is spawning"
247
+ run
248
+ end
249
+ end
250
+
251
+ private
252
+
253
+ def run
254
+ loop do
255
+ cmd, *args = @queue.pop
256
+ case cmd
257
+ when :shutdown
258
+ @logger.debug "#{self} is shutting down, goodbye"
259
+ break
260
+ when :frame
261
+ fd, frame = args
262
+ begin
263
+ otrans = @transportFactory.get_transport(fd)
264
+ oprot = @protocolFactory.get_protocol(otrans)
265
+ membuf = MemoryBuffer.new(frame)
266
+ itrans = @transportFactory.get_transport(membuf)
267
+ iprot = @protocolFactory.get_protocol(itrans)
268
+ @processor.process(iprot, oprot)
269
+ rescue => e
270
+ @logger.error "#{Thread.current.inspect} raised error: #{e.inspect}\n#{e.backtrace.join("\n")}"
271
+ end
272
+ end
273
+ end
274
+ end
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,2 @@
1
+ require 'thrift/deprecation'
2
+ require 'thrift/server/httpserver'
@@ -0,0 +1,2 @@
1
+ require 'thrift/deprecation'
2
+ require 'thrift/server'
@@ -0,0 +1,268 @@
1
+ #
2
+ # Autogenerated by Thrift
3
+ #
4
+ # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
5
+ #
6
+
7
+ require 'thrift'
8
+ require 'thrift/protocol'
9
+ require File.dirname(__FILE__) + '/ThriftSpec_types'
10
+
11
+ module SpecNamespace
12
+ module NonblockingService
13
+ class Client
14
+ include ::Thrift::Client
15
+
16
+ def greeting(english)
17
+ send_greeting(english)
18
+ return recv_greeting()
19
+ end
20
+
21
+ def send_greeting(english)
22
+ send_message('greeting', Greeting_args, :english => english)
23
+ end
24
+
25
+ def recv_greeting()
26
+ result = receive_message(Greeting_result)
27
+ return result.success unless result.success.nil?
28
+ raise Thrift::ApplicationException.new(Thrift::ApplicationException::MISSING_RESULT, 'greeting failed: unknown result')
29
+ end
30
+
31
+ def block()
32
+ send_block()
33
+ return recv_block()
34
+ end
35
+
36
+ def send_block()
37
+ send_message('block', Block_args)
38
+ end
39
+
40
+ def recv_block()
41
+ result = receive_message(Block_result)
42
+ return result.success unless result.success.nil?
43
+ raise Thrift::ApplicationException.new(Thrift::ApplicationException::MISSING_RESULT, 'block failed: unknown result')
44
+ end
45
+
46
+ def unblock(n)
47
+ send_unblock(n)
48
+ end
49
+
50
+ def send_unblock(n)
51
+ send_message('unblock', Unblock_args, :n => n)
52
+ end
53
+ def shutdown()
54
+ send_shutdown()
55
+ end
56
+
57
+ def send_shutdown()
58
+ send_message('shutdown', Shutdown_args)
59
+ end
60
+ def sleep(seconds)
61
+ send_sleep(seconds)
62
+ recv_sleep()
63
+ end
64
+
65
+ def send_sleep(seconds)
66
+ send_message('sleep', Sleep_args, :seconds => seconds)
67
+ end
68
+
69
+ def recv_sleep()
70
+ result = receive_message(Sleep_result)
71
+ return
72
+ end
73
+
74
+ end
75
+
76
+ class Processor
77
+ include ::Thrift::Processor
78
+
79
+ def process_greeting(seqid, iprot, oprot)
80
+ args = read_args(iprot, Greeting_args)
81
+ result = Greeting_result.new()
82
+ result.success = @handler.greeting(args.english)
83
+ write_result(result, oprot, 'greeting', seqid)
84
+ end
85
+
86
+ def process_block(seqid, iprot, oprot)
87
+ args = read_args(iprot, Block_args)
88
+ result = Block_result.new()
89
+ result.success = @handler.block()
90
+ write_result(result, oprot, 'block', seqid)
91
+ end
92
+
93
+ def process_unblock(seqid, iprot, oprot)
94
+ args = read_args(iprot, Unblock_args)
95
+ @handler.unblock(args.n)
96
+ return
97
+ end
98
+
99
+ def process_shutdown(seqid, iprot, oprot)
100
+ args = read_args(iprot, Shutdown_args)
101
+ @handler.shutdown()
102
+ return
103
+ end
104
+
105
+ def process_sleep(seqid, iprot, oprot)
106
+ args = read_args(iprot, Sleep_args)
107
+ result = Sleep_result.new()
108
+ @handler.sleep(args.seconds)
109
+ write_result(result, oprot, 'sleep', seqid)
110
+ end
111
+
112
+ end
113
+
114
+ # HELPER FUNCTIONS AND STRUCTURES
115
+
116
+ class Greeting_args
117
+ include ::Thrift::Struct
118
+ ENGLISH = 1
119
+
120
+ Thrift::Struct.field_accessor self, :english
121
+ FIELDS = {
122
+ ENGLISH => {:type => Thrift::Types::BOOL, :name => 'english'}
123
+ }
124
+
125
+ def struct_fields; FIELDS; end
126
+
127
+ def validate
128
+ end
129
+
130
+ end
131
+
132
+ class Greeting_result
133
+ include ::Thrift::Struct
134
+ SUCCESS = 0
135
+
136
+ Thrift::Struct.field_accessor self, :success
137
+ FIELDS = {
138
+ SUCCESS => {:type => Thrift::Types::STRUCT, :name => 'success', :class => SpecNamespace::Hello}
139
+ }
140
+
141
+ def struct_fields; FIELDS; end
142
+
143
+ def validate
144
+ end
145
+
146
+ end
147
+
148
+ class Block_args
149
+ include ::Thrift::Struct
150
+
151
+ FIELDS = {
152
+
153
+ }
154
+
155
+ def struct_fields; FIELDS; end
156
+
157
+ def validate
158
+ end
159
+
160
+ end
161
+
162
+ class Block_result
163
+ include ::Thrift::Struct
164
+ SUCCESS = 0
165
+
166
+ Thrift::Struct.field_accessor self, :success
167
+ FIELDS = {
168
+ SUCCESS => {:type => Thrift::Types::BOOL, :name => 'success'}
169
+ }
170
+
171
+ def struct_fields; FIELDS; end
172
+
173
+ def validate
174
+ end
175
+
176
+ end
177
+
178
+ class Unblock_args
179
+ include ::Thrift::Struct
180
+ N = 1
181
+
182
+ Thrift::Struct.field_accessor self, :n
183
+ FIELDS = {
184
+ N => {:type => Thrift::Types::I32, :name => 'n'}
185
+ }
186
+
187
+ def struct_fields; FIELDS; end
188
+
189
+ def validate
190
+ end
191
+
192
+ end
193
+
194
+ class Unblock_result
195
+ include ::Thrift::Struct
196
+
197
+ FIELDS = {
198
+
199
+ }
200
+
201
+ def struct_fields; FIELDS; end
202
+
203
+ def validate
204
+ end
205
+
206
+ end
207
+
208
+ class Shutdown_args
209
+ include ::Thrift::Struct
210
+
211
+ FIELDS = {
212
+
213
+ }
214
+
215
+ def struct_fields; FIELDS; end
216
+
217
+ def validate
218
+ end
219
+
220
+ end
221
+
222
+ class Shutdown_result
223
+ include ::Thrift::Struct
224
+
225
+ FIELDS = {
226
+
227
+ }
228
+
229
+ def struct_fields; FIELDS; end
230
+
231
+ def validate
232
+ end
233
+
234
+ end
235
+
236
+ class Sleep_args
237
+ include ::Thrift::Struct
238
+ SECONDS = 1
239
+
240
+ Thrift::Struct.field_accessor self, :seconds
241
+ FIELDS = {
242
+ SECONDS => {:type => Thrift::Types::DOUBLE, :name => 'seconds'}
243
+ }
244
+
245
+ def struct_fields; FIELDS; end
246
+
247
+ def validate
248
+ end
249
+
250
+ end
251
+
252
+ class Sleep_result
253
+ include ::Thrift::Struct
254
+
255
+ FIELDS = {
256
+
257
+ }
258
+
259
+ def struct_fields; FIELDS; end
260
+
261
+ def validate
262
+ end
263
+
264
+ end
265
+
266
+ end
267
+
268
+ end