thrift 0.2.0 → 0.2.0.2

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,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