cod 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 849f13cc13d03934bdf098bc46697880be2380e7
4
+ data.tar.gz: e31877e11c7b73a9e725b3083b4e8508df3cecda
5
+ SHA512:
6
+ metadata.gz: d37a103c94ccb8ce3f092a6b208809eac7b84ceaf560a4bd99f6958860f4f67c47991c33e9315d5d0197671fead58b92cfe949809aa821936786ba251a72bd72
7
+ data.tar.gz: d34fc64a7c8162855083775b99914987b6292d75db704f66b140e1c502b08839a714fd1a4b992663f305e5e479f0a5b5e2b2f688a2145ddf7c97c52c37daf1fd
@@ -1,3 +1,15 @@
1
+ = 0.6.0 / ???
2
+
3
+ + Cod.bidir is now based on UNIXSocket.pair (socketpair) and is called just
4
+ #bidir. Thanks to Yves Senn (senny) for the idea and the pairing sessions!
5
+ ! Eliminated some warnings for exceptions that were reraised anyway. Thanks
6
+ to Chris Schmich (schmich).
7
+ ! Updates project to work with modern rubies.
8
+
9
+ = 0.5.1 / 30Aug2012
10
+
11
+ + Cod.bidir_pipe is now selectable.
12
+
1
13
  = 0.5.0 / 12Jun2012
2
14
 
3
15
  + Process#wait will not throw Errno::ECHILD anymore.
@@ -0,0 +1,20 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + "/../lib")
2
+ require 'cod'
3
+
4
+ pipe = Cod.bidir
5
+
6
+ child_pid = fork do
7
+ pipe.swap!
8
+ pipe.put 'test'
9
+ pipe.put Process.pid
10
+ command = pipe.get
11
+ p command
12
+ end
13
+
14
+ begin
15
+ p pipe.get
16
+ p pipe.get
17
+ pipe.put :foobar
18
+ ensure
19
+ Process.wait(child_pid)
20
+ end
data/lib/cod.rb CHANGED
@@ -65,18 +65,32 @@ module Cod
65
65
  end
66
66
  module_function :pipe
67
67
 
68
- # Creates two channels based on {Cod.pipe} (unidirectional IO.pipe) and links
69
- # things up so that you communication is bidirectional. Writes go to
70
- # #out and reads come from #in.
68
+ # Creates a channel based on socketpair (UNIXSocket.pair). This is a
69
+ # IPC-kind of channel that can be used to exchange messages both ways.
71
70
  #
72
- # @overload bidir_pipe(serializer=nil)
71
+ # @overload bidir(serializer=nil)
73
72
  # @param serializer [#en,#de] optional serializer to use
74
- # @return [Cod::BidirPipe]
73
+ # @return [Cod::Bidir]
75
74
  #
76
- def bidir_pipe(serializer=nil, pipe_pair=nil)
77
- Cod::BidirPipe.new(serializer, pipe_pair)
75
+ def bidir(serializer=nil)
76
+ Cod::Bidir.pair(serializer)
78
77
  end
79
- module_function :bidir_pipe
78
+ module_function :bidir
79
+
80
+ # Creates a named bidirectional channel. Only one end of this will be
81
+ # connected to the unix socket in the file system identified by path.
82
+ #
83
+ def bidir_named(name, serializer=nil)
84
+ Cod::Bidir.named(name, serializer)
85
+ end
86
+ module_function :bidir_named
87
+
88
+ # Creates the server side for a named unix socket connection.
89
+ #
90
+ def bidir_server(name, serializer=nil)
91
+ Cod::BidirServer.new(name, serializer)
92
+ end
93
+ module_function :bidir_server
80
94
 
81
95
  # Creates a tcp connection to the destination and returns a channel for it.
82
96
  #
@@ -169,9 +183,13 @@ module Cod
169
183
  end
170
184
  end
171
185
 
186
+ require 'cod/callbacks'
187
+
172
188
  require 'cod/select_group'
173
189
  require 'cod/select'
174
190
 
191
+ require 'cod/socket_server'
192
+
175
193
  require 'cod/iopair'
176
194
 
177
195
  require 'cod/channel'
@@ -180,7 +198,9 @@ require 'cod/simple_serializer'
180
198
  require 'cod/line_serializer'
181
199
 
182
200
  require 'cod/pipe'
183
- require 'cod/bidir_pipe'
201
+
202
+ require 'cod/bidir'
203
+ require 'cod/bidir_server'
184
204
 
185
205
  require 'cod/process'
186
206
 
@@ -0,0 +1,102 @@
1
+ module Cod
2
+
3
+ # A bidirectional pipe, also called a socket pair.
4
+ #
5
+ class Bidir < Channel
6
+
7
+ include Callbacks
8
+
9
+ # Serializer to use for messages on this transport.
10
+ attr_reader :serializer
11
+
12
+ # This process' end of the pipe, can be used for both reading and writing.
13
+ attr_accessor :socket
14
+
15
+ # The other side of the pipe.
16
+ attr_reader :other
17
+
18
+ # Path of the socket (.named), if based on a file system FIFO.
19
+ attr_reader :path
20
+
21
+ def self.named(name, serializer=nil)
22
+ new(serializer || SimpleSerializer.new,
23
+ name,
24
+ UNIXSocket.new(name), nil)
25
+ end
26
+ def self.pair(serializer=nil)
27
+ new(serializer || SimpleSerializer.new,
28
+ nil,
29
+ *UNIXSocket.pair)
30
+ end
31
+
32
+ # Initializes a Bidir channel given two or alternatively one end of a
33
+ # bidirectional pipe. (socketpair)
34
+ #
35
+ # socket ---- other
36
+ #
37
+ def initialize(serializer, path, socket, other)
38
+ @serializer = serializer
39
+ @socket, @other = socket, other
40
+ @path = path
41
+ end
42
+
43
+ def put(obj)
44
+ using_callbacks(socket) do
45
+ socket.write(
46
+ serializer.en(obj))
47
+ end
48
+ end
49
+
50
+ def get
51
+ serializer.de(socket)
52
+ rescue EOFError, IOError
53
+ raise Cod::ConnectionLost,
54
+ "All pipe ends seem to be closed. Reading from this pipe will not "+
55
+ "return any data."
56
+ end
57
+
58
+ def close
59
+ socket.close;
60
+ other.close if other
61
+ rescue IOError
62
+ # One code path through Cod::Process will close other prematurely. This
63
+ # is to avoid an error.
64
+ end
65
+
66
+ # Swaps the end of this pipe around.
67
+ #
68
+ def swap!
69
+ @socket, @other = @other, @socket
70
+ end
71
+
72
+ # ---------------------------------------------------------- serialization
73
+
74
+ # A small structure that is constructed for a serialized tcp client on
75
+ # the other end (the deserializing end). What the deserializing code does
76
+ # with this is his problem.
77
+ #
78
+ # @private
79
+ #
80
+ OtherEnd = Struct.new(:path) # :nodoc:
81
+
82
+ def _dump(level) # :nodoc:
83
+ if !path && callbacks_enabled?
84
+ register_callback { |conn| conn.send_io(other) }
85
+ end
86
+
87
+ Marshal.dump(path)
88
+ end
89
+ def self._load(params) # :nodoc:
90
+ # Instead of a tcp client (no way to construct one at this point), we'll
91
+ # insert a kind of marker in the object stream that will be replaced
92
+ # with a valid client later on. (hopefully)
93
+ OtherEnd.new(Marshal.load(params))
94
+ end
95
+
96
+ # @private
97
+ #
98
+ def to_read_fds
99
+ [socket]
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,34 @@
1
+ module Cod
2
+ class BidirServer < SocketServer
3
+ include Callbacks
4
+
5
+ attr_reader :path
6
+
7
+ def initialize(path, serializer)
8
+ super serializer || SimpleSerializer.new,
9
+ UNIXServer.new(path)
10
+
11
+ @path = path
12
+ end
13
+
14
+ def deserialize_special(socket, obj)
15
+ case obj
16
+ when Bidir::OtherEnd
17
+ if obj.path == self.path
18
+ return back_channel(socket)
19
+ end
20
+
21
+ channel = Bidir.new(serializer, nil, nil, nil)
22
+ register_callback { |conn|
23
+ channel.socket= conn.recv_io(UNIXSocket) }
24
+ return channel
25
+ end
26
+
27
+ obj
28
+ end
29
+
30
+ def back_channel(socket)
31
+ Bidir.new(serializer, nil, socket, nil)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,24 @@
1
+ module Cod
2
+ module Callbacks
3
+ def using_callbacks(*args)
4
+ Thread.current[:callbacks] = []
5
+
6
+ result = yield
7
+
8
+ Thread.current[:callbacks].each do |cb|
9
+ cb.call(*args)
10
+ end
11
+
12
+ return result
13
+ ensure
14
+ Thread.current[:callbacks] = nil
15
+ end
16
+
17
+ def callbacks_enabled?
18
+ Thread.current[:callbacks]
19
+ end
20
+ def register_callback(&block)
21
+ Thread.current[:callbacks] << block
22
+ end
23
+ end
24
+ end
@@ -15,12 +15,10 @@ module Cod
15
15
  self.w = other.w.dup if other.w
16
16
  end
17
17
  def write(buf)
18
- close_r
19
18
  raise Cod::ReadOnlyChannel unless w
20
19
  w.write(buf)
21
20
  end
22
21
  def read(serializer)
23
- close_w
24
22
  raise Cod::WriteOnlyChannel unless r
25
23
  serializer.de(r)
26
24
  end
@@ -104,6 +104,7 @@ module Cod
104
104
  #
105
105
  def put(obj)
106
106
  raise Cod::ReadOnlyChannel unless can_write?
107
+ pipe.close_r
107
108
 
108
109
  pipe.write(
109
110
  serializer.en(obj))
@@ -145,7 +146,7 @@ module Cod
145
146
  # @private
146
147
  #
147
148
  def to_read_fds
148
- r
149
+ [r]
149
150
  end
150
151
 
151
152
  # Returns true if you can read from this pipe.
@@ -29,19 +29,24 @@ module Cod
29
29
 
30
30
  # @private
31
31
  def run(command)
32
- @pipe = Cod.bidir_pipe(@serializer)
32
+ @pipe = Cod.bidir(@serializer)
33
33
 
34
34
  @pid = ::Process.spawn(command,
35
- :in => @pipe.w.r,
36
- :out => @pipe.r.w)
35
+ :in => @pipe.other,
36
+ :out => @pipe.other)
37
+
38
+ # Close the end we just dedicated to the process we've spawned.
39
+ # As a consequence, when the process exists, we'll get a read error
40
+ # on our side of the pipe (@pipe.socket, #put, #get)
41
+ @pipe.other.close
37
42
  end
38
43
 
39
44
  # Returns the cod channel associated with this process. The channel will
40
45
  # have the process' standard output bound to its #get (input), and the
41
46
  # process' standard input will be bound to #put (output).
42
47
  #
43
- # Note that when the process exits and all communication has been read from
44
- # the channel, it will probably raise a Cod::ConnectionLost error.
48
+ # Note that when the process exits and all communication has been read
49
+ # from the channel, it will probably raise a Cod::ConnectionLost error.
45
50
  #
46
51
  # @example
47
52
  # process = Cod.process('uname', LineSerializer.new)
@@ -62,13 +67,13 @@ module Cod
62
67
  ::Process.kill :TERM, @pid
63
68
  end
64
69
 
65
- # Asks the process to terminate by closing its stanard input. This normally
66
- # closes down the process, but no guarantees are made.
70
+ # Asks the process to terminate by closing its stanard input. This
71
+ # normally closes down the process, but no guarantees are made.
67
72
  #
68
73
  # @return [void]
69
74
  #
70
75
  def terminate
71
- @pipe.w.close
76
+ @pipe.socket.close_write
72
77
  end
73
78
 
74
79
  # Waits for the process to terminate and returns its exit value. May
@@ -35,7 +35,7 @@ module Cod
35
35
  # @return [Hash,Array,Cod::Channel,IO]
36
36
  #
37
37
  def do
38
- fds = groups.values { |e| to_read_fd(e) }.compact
38
+ fds = groups.values { |e| to_read_fds(e) }.compact
39
39
 
40
40
  # Perform select
41
41
  r,w,e = IO.select(fds, nil, nil, timeout)
@@ -45,13 +45,14 @@ module Cod
45
45
 
46
46
  # Prepare a return value: The original hash, where the fds are ready.
47
47
  groups.
48
- keep_if { |e| r.include?(to_read_fd(e)) }.
48
+ keep_if { |e| to_read_fds(e).
49
+ any? { |fd| r.include?(fd) } }.
49
50
  unpack
50
51
  end
51
52
  private
52
- def to_read_fd(single)
53
- return single.to_read_fds if single.respond_to?(:to_read_fds)
54
- return single
53
+ def to_read_fds(channel)
54
+ return channel.to_read_fds if channel.respond_to?(:to_read_fds)
55
+ return [channel]
55
56
  end
56
57
  end
57
58
  end
@@ -0,0 +1,172 @@
1
+ require 'socket'
2
+
3
+ module Cod
4
+
5
+ # Abstract base class for all kinds of socket based servers. Useful for all
6
+ # types of channels that know an #accept.
7
+ #
8
+ class SocketServer
9
+ include Callbacks
10
+
11
+ attr_reader :socket
12
+ attr_reader :serializer
13
+
14
+ def initialize(serializer, socket)
15
+ @socket = socket
16
+ @client_sockets = []
17
+ @round_robin_index = 0
18
+ @messages = Array.new
19
+ @serializer = serializer
20
+ end
21
+
22
+ # Receives one object from the channel. This will receive one message from
23
+ # one of the connected clients in a round-robin fashion.
24
+ #
25
+ # @example
26
+ # channel.get # => object
27
+ #
28
+ # @param opts [Hash]
29
+ # @return [Object] message sent by one of the clients
30
+ #
31
+ def get(opts={})
32
+ msg, socket = _get(opts)
33
+ return msg
34
+ end
35
+
36
+ # Receives one object from the channel. Returns a tuple of
37
+ # <message,channel> where channel is a tcp channel that links back to the
38
+ # client that sent message.
39
+ #
40
+ # Using this method, the server can communicate back to its clients
41
+ # individually instead of collectively.
42
+ #
43
+ # @example Answering to the client that sent a specific message
44
+ # msg, chan = server.get_ext
45
+ # chan.put :answer
46
+ #
47
+ # @param opts [Hash]
48
+ # @return [Array<Object, TcpClient>] tuple of the message that was sent
49
+ # a channel back to the client that sent the message
50
+ def get_ext(opts={})
51
+ msg, socket = _get(opts)
52
+ return [
53
+ msg,
54
+ back_channel(socket)]
55
+ end
56
+
57
+ # Closes the channel.
58
+ #
59
+ def close
60
+ @socket.close
61
+ @client_sockets.each { |io| io.close }
62
+ end
63
+
64
+ # Returns an array of IOs that Cod.select should select on.
65
+ #
66
+ def to_read_fds
67
+ @client_sockets + [@socket]
68
+ end
69
+
70
+ # Returns the number of clients that are connected to this server
71
+ # currently.
72
+ #
73
+ def connections
74
+ @client_sockets.size
75
+ end
76
+
77
+ # ------------------------------------------------------- connection owner
78
+
79
+ # Notifies the TcpServer that one of its connections needs to be closed.
80
+ # This can be triggered by using #get_ext to obtain a handle to
81
+ # connections and then calling #close on that connection.
82
+ #
83
+ # @param socket [TCPSocket] the socket that needs to be closed @return
84
+ # [void]
85
+ #
86
+ def request_close(socket)
87
+ @client_sockets.delete(socket)
88
+ socket.close
89
+ end
90
+
91
+ private
92
+ def _get(opts)
93
+ loop do
94
+ # Return a buffered message if there is one left.
95
+ return @messages.shift unless @messages.empty?
96
+
97
+ # Shuffle the socket list around, so we don't always read from the
98
+ # same client first.
99
+ socket_list = round_robin(@client_sockets)
100
+
101
+ # Append the server socket to be able to react to new connections
102
+ # that are made.
103
+ socket_list << socket
104
+
105
+ # Sleep until either a new connection is made or data is available on
106
+ # one of the old connections.
107
+ rr, _, _ = IO.select(socket_list, nil, nil)
108
+ next unless rr
109
+
110
+ # Accept new connections
111
+ if rr.include?(@socket)
112
+ accept_new_connections
113
+ rr.delete(@socket)
114
+ end
115
+
116
+ handle_socket_events(rr, opts)
117
+ end
118
+ end
119
+
120
+ def handle_socket_events(sockets, opts)
121
+ sockets.each do |io|
122
+ if io.eof?
123
+ @client_sockets.delete(io)
124
+ io.close
125
+ else
126
+ consume_pending io, opts
127
+ end
128
+ end
129
+ end
130
+
131
+ def consume_pending(io, opts)
132
+ until io.eof?
133
+ @messages << [
134
+ deserialize(io),
135
+ io]
136
+
137
+ # More messages from this socket?
138
+ return unless IO.select([io], nil, nil, 0.0001)
139
+ end
140
+ end
141
+
142
+ # Hooks deserialisation (if the programmer provided for this) and calls
143
+ # #deserialize_special for each object in the object stream. This allows
144
+ # construction of back channels from replacement tokens, for example.
145
+ #
146
+ def deserialize(io)
147
+ using_callbacks(io) do
148
+ @serializer.de(io) { |obj| deserialize_special(io, obj) }
149
+ end
150
+ end
151
+
152
+ def round_robin(list)
153
+ @round_robin_index += 1
154
+ if @round_robin_index >= list.size
155
+ @round_robin_index = 0
156
+ end
157
+
158
+ # Create a duplicate of list that has its elements rotated by
159
+ # @round_robin_index
160
+ list = list.dup
161
+ list = list + list.shift(@round_robin_index)
162
+ end
163
+
164
+ def accept_new_connections
165
+ loop do
166
+ @client_sockets << @socket.accept_nonblock
167
+ end
168
+ rescue Errno::EAGAIN
169
+ # This means that there are no sockets to accept. Continue.
170
+ end
171
+ end
172
+ end
@@ -135,7 +135,11 @@ module Cod
135
135
  # @private
136
136
  #
137
137
  def to_read_fds
138
- @connection.socket if @connection
138
+ if @connection
139
+ [@connection.socket]
140
+ else
141
+ []
142
+ end
139
143
  end
140
144
  private
141
145
  # Checks to see in which of the three connection phases we're in. If we're
@@ -169,7 +173,6 @@ module Cod
169
173
  @connection.write(
170
174
  @serializer.en(msg))
171
175
  rescue Exception => e
172
- warn e
173
176
  raise
174
177
  end
175
178
 
@@ -270,4 +273,4 @@ module Cod
270
273
  end
271
274
  end
272
275
  end
273
- end
276
+ end
@@ -26,70 +26,24 @@ module Cod
26
26
  # This means that you can transmit the client channel through the connection
27
27
  # as part of the message you send.
28
28
  #
29
- class TcpServer
29
+ class TcpServer < SocketServer
30
30
  def initialize(bind_to, serializer)
31
- @socket = TCPServer.new(*bind_to.split(':'))
32
- @client_sockets = []
33
- @round_robin_index = 0
34
- @messages = Array.new
35
- @serializer = serializer
31
+ super(serializer, TCPServer.new(*bind_to.split(':')))
36
32
  end
37
33
 
38
- # Receives one object from the channel. This will receive one message from
39
- # one of the connected clients in a round-robin fashion.
40
- #
41
- # @example
42
- # channel.get # => object
43
- #
44
- # @param opts [Hash]
45
- # @return [Object] message sent by one of the clients
46
- #
47
- def get(opts={})
48
- msg, socket = _get(opts)
49
- return msg
50
- end
51
-
52
- # Receives one object from the channel. Returns a tuple of
53
- # <message,channel> where channel is a tcp channel that links back to the
54
- # client that sent message.
55
- #
56
- # Using this method, the server can communicate back to its clients
57
- # individually instead of collectively.
58
- #
59
- # @example Answering to the client that sent a specific message
60
- # msg, chan = server.get_ext
61
- # chan.put :answer
62
- #
63
- # @param opts [Hash]
64
- # @return [Array<Object, TcpClient>] tuple of the message that was sent
65
- # a channel back to the client that sent the message
66
- def get_ext(opts={})
67
- msg, socket = _get(opts)
68
- return [
69
- msg,
70
- produce_back_channel(socket)]
34
+ def deserialize_special(socket, obj)
35
+ if obj.kind_of?(TcpClient::OtherEnd)
36
+ return back_channel(socket)
37
+ end
38
+ return obj
71
39
  end
72
40
 
73
- # Closes the channel.
74
- #
75
- def close
76
- @socket.close
77
- @client_sockets.each { |io| io.close }
78
- end
79
-
80
- # Returns an array of IOs that Cod.select should select on.
81
- #
82
- def to_read_fds
83
- @client_sockets
41
+ def back_channel(socket)
42
+ TcpClient.new(
43
+ TcpClient::Connection.new(socket, self),
44
+ serializer)
84
45
  end
85
46
 
86
- # Returns the number of clients that are connected to this server
87
- # currently.
88
- #
89
- def connections
90
- @client_sockets.size
91
- end
92
-
93
47
  # --------------------------------------------------------- service/client
94
48
 
95
49
  def service
@@ -102,104 +56,5 @@ module Cod
102
56
  def client(answers_to)
103
57
  Service::Client.new(answers_to, answers_to)
104
58
  end
105
-
106
- # ------------------------------------------------------- connection owner
107
-
108
- # Notifies the TcpServer that one of its connections needs to be closed.
109
- # This can be triggered by using #get_ext to obtain a handle to connections
110
- # and then calling #close on that connection.
111
- #
112
- # @param socket [TCPSocket] the socket that needs to be closed
113
- # @return [void]
114
- #
115
- def request_close(socket)
116
- @client_sockets.delete(socket)
117
- socket.close
118
- end
119
-
120
- private
121
- def _get(opts)
122
- loop do
123
- # Return a buffered message if there is one left.
124
- return @messages.shift unless @messages.empty?
125
-
126
- # Shuffle the socket list around, so we don't always read from the
127
- # same client first.
128
- socket_list = round_robin(@client_sockets)
129
-
130
- # Append the server socket to be able to react to new connections
131
- # that are made.
132
- socket_list << @socket
133
-
134
- # Sleep until either a new connection is made or data is available on
135
- # one of the old connections.
136
- rr, _, _ = IO.select(socket_list, nil, nil)
137
- next unless rr
138
-
139
- # Accept new connections
140
- if rr.include?(@socket)
141
- accept_new_connections
142
- rr.delete(@socket)
143
- end
144
-
145
- handle_socket_events(rr, opts)
146
- end
147
- end
148
-
149
- def handle_socket_events(sockets, opts)
150
- sockets.each do |io|
151
- if io.eof?
152
- @client_sockets.delete(io)
153
- io.close
154
- else
155
- consume_pending io, opts
156
- end
157
- end
158
- end
159
-
160
- def consume_pending(io, opts)
161
- until io.eof?
162
- @messages << [
163
- deserialize(io),
164
- io]
165
-
166
- # More messages from this socket?
167
- return unless IO.select([io], nil, nil, 0.0001)
168
- end
169
- end
170
-
171
- def deserialize(io)
172
- @serializer.de(io) { |obj|
173
- obj.kind_of?(TcpClient::OtherEnd) ?
174
- produce_back_channel(io) :
175
- obj
176
- }
177
- end
178
-
179
- def round_robin(list)
180
- @round_robin_index += 1
181
- if @round_robin_index >= list.size
182
- @round_robin_index = 0
183
- end
184
-
185
- # Create a duplicate of list that has its elements rotated by
186
- # @round_robin_index
187
- list = list.dup
188
- list = list + list.shift(@round_robin_index)
189
- end
190
-
191
- def accept_new_connections
192
- loop do
193
- @client_sockets << @socket.accept_nonblock
194
- end
195
- rescue Errno::EAGAIN
196
- # This means that there are no sockets to accept. Continue.
197
- end
198
-
199
- def produce_back_channel(socket)
200
- TcpClient.new(
201
- TcpClient::Connection.new(socket, self),
202
- @serializer)
203
- end
204
59
  end
205
60
  end
@@ -35,7 +35,7 @@ module Cod
35
35
  attr_reader :thread
36
36
 
37
37
  def try_work
38
- @try_work_exclusive_section.enter {
38
+ exclusive {
39
39
  # NOTE if predicate is nil or not set, no work will be accomplished.
40
40
  # This is the way I need it.
41
41
  while !@queue.empty? && predicate?
@@ -73,7 +73,9 @@ module Cod
73
73
  # work_queue.schedule { a_piece_of_work }
74
74
  #
75
75
  def schedule(&work)
76
- @queue << work
76
+ exclusive {
77
+ @queue << work
78
+ }
77
79
  end
78
80
 
79
81
  # Shuts down the queue properly, without waiting for work to be completed.
@@ -89,7 +91,9 @@ module Cod
89
91
  # Returns the size of the queue.
90
92
  #
91
93
  def size
92
- @queue.size
94
+ exclusive {
95
+ @queue.size
96
+ }
93
97
  end
94
98
 
95
99
  def clear_thread_semaphore
metadata CHANGED
@@ -1,110 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cod
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
5
- prerelease:
4
+ version: 0.6.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Kaspar Schiess
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-06-12 00:00:00.000000000 Z
11
+ date: 2014-09-23 00:00:00.000000000 Z
13
12
  dependencies:
14
- - !ruby/object:Gem::Dependency
15
- name: rake
16
- requirement: !ruby/object:Gem::Requirement
17
- none: false
18
- requirements:
19
- - - ! '>='
20
- - !ruby/object:Gem::Version
21
- version: '0'
22
- type: :development
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ! '>='
28
- - !ruby/object:Gem::Version
29
- version: '0'
30
13
  - !ruby/object:Gem::Dependency
31
14
  name: rspec
32
15
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
16
  requirements:
35
- - - ! '>='
17
+ - - ">="
36
18
  - !ruby/object:Gem::Version
37
19
  version: '0'
38
20
  type: :development
39
21
  prerelease: false
40
22
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
23
  requirements:
43
- - - ! '>='
24
+ - - ">="
44
25
  - !ruby/object:Gem::Version
45
26
  version: '0'
46
27
  - !ruby/object:Gem::Dependency
47
28
  name: flexmock
48
29
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
30
  requirements:
51
- - - ! '>='
31
+ - - ">="
52
32
  - !ruby/object:Gem::Version
53
33
  version: '0'
54
34
  type: :development
55
35
  prerelease: false
56
36
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
37
  requirements:
59
- - - ! '>='
38
+ - - ">="
60
39
  - !ruby/object:Gem::Version
61
40
  version: '0'
62
41
  - !ruby/object:Gem::Dependency
63
42
  name: yard
64
43
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
44
  requirements:
67
- - - ! '>='
45
+ - - ">="
68
46
  - !ruby/object:Gem::Version
69
47
  version: '0'
70
48
  type: :development
71
49
  prerelease: false
72
50
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
51
  requirements:
75
- - - ! '>='
52
+ - - ">="
76
53
  - !ruby/object:Gem::Version
77
54
  version: '0'
78
55
  - !ruby/object:Gem::Dependency
79
56
  name: guard
80
57
  requirement: !ruby/object:Gem::Requirement
81
- none: false
82
58
  requirements:
83
- - - ! '>='
59
+ - - ">="
84
60
  - !ruby/object:Gem::Version
85
61
  version: '0'
86
62
  type: :development
87
63
  prerelease: false
88
64
  version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
65
  requirements:
91
- - - ! '>='
66
+ - - ">="
92
67
  - !ruby/object:Gem::Version
93
68
  version: '0'
94
69
  - !ruby/object:Gem::Dependency
95
70
  name: growl
96
71
  requirement: !ruby/object:Gem::Requirement
97
- none: false
98
72
  requirements:
99
- - - ! '>='
73
+ - - ">="
100
74
  - !ruby/object:Gem::Version
101
75
  version: '0'
102
76
  type: :development
103
77
  prerelease: false
104
78
  version_requirements: !ruby/object:Gem::Requirement
105
- none: false
106
79
  requirements:
107
- - - ! '>='
80
+ - - ">="
108
81
  - !ruby/object:Gem::Version
109
82
  version: '0'
110
83
  description:
@@ -116,13 +89,29 @@ extra_rdoc_files:
116
89
  files:
117
90
  - HISTORY.txt
118
91
  - LICENSE
119
- - Rakefile
120
92
  - README
93
+ - examples/bs_ping_pong/ping.rb
94
+ - examples/bs_ping_pong/pong.rb
95
+ - examples/example_scaffold.rb
96
+ - examples/forked_server/README
97
+ - examples/forked_server/forked.rb
98
+ - examples/master_child.rb
99
+ - examples/master_child_bidir.rb
100
+ - examples/netcat/README
101
+ - examples/netcat/server.rb
102
+ - examples/protocol-buffers/README
103
+ - examples/protocol-buffers/master_child.rb
104
+ - examples/service.rb
105
+ - examples/tcp.rb
106
+ - examples/tcp_get_ext.rb
107
+ - lib/cod.rb
108
+ - lib/cod/beanstalk.rb
121
109
  - lib/cod/beanstalk/channel.rb
122
110
  - lib/cod/beanstalk/serializer.rb
123
111
  - lib/cod/beanstalk/service.rb
124
- - lib/cod/beanstalk.rb
125
- - lib/cod/bidir_pipe.rb
112
+ - lib/cod/bidir.rb
113
+ - lib/cod/bidir_server.rb
114
+ - lib/cod/callbacks.rb
126
115
  - lib/cod/channel.rb
127
116
  - lib/cod/iopair.rb
128
117
  - lib/cod/line_serializer.rb
@@ -133,54 +122,34 @@ files:
133
122
  - lib/cod/select_group.rb
134
123
  - lib/cod/service.rb
135
124
  - lib/cod/simple_serializer.rb
125
+ - lib/cod/socket_server.rb
136
126
  - lib/cod/tcp_client.rb
137
127
  - lib/cod/tcp_server.rb
138
128
  - lib/cod/work_queue.rb
139
- - lib/cod.rb
140
129
  - lib/tcp_proxy.rb
141
- - examples/bs_ping_pong/ping.rb
142
- - examples/bs_ping_pong/pong.rb
143
- - examples/example_scaffold.rb
144
- - examples/forked_server/forked.rb
145
- - examples/forked_server/README
146
- - examples/master_child.rb
147
- - examples/netcat/README
148
- - examples/netcat/server.rb
149
- - examples/protocol-buffers/master_child.rb
150
- - examples/protocol-buffers/README
151
- - examples/service.rb
152
- - examples/tcp.rb
153
- - examples/tcp_get_ext.rb
154
130
  homepage: http://kschiess.github.com/cod
155
131
  licenses: []
132
+ metadata: {}
156
133
  post_install_message:
157
134
  rdoc_options:
158
- - --main
135
+ - "--main"
159
136
  - README
160
137
  require_paths:
161
138
  - lib
162
139
  required_ruby_version: !ruby/object:Gem::Requirement
163
- none: false
164
140
  requirements:
165
- - - ! '>='
141
+ - - ">="
166
142
  - !ruby/object:Gem::Version
167
143
  version: '0'
168
- segments:
169
- - 0
170
- hash: 4413858275826022328
171
144
  required_rubygems_version: !ruby/object:Gem::Requirement
172
- none: false
173
145
  requirements:
174
- - - ! '>='
146
+ - - ">="
175
147
  - !ruby/object:Gem::Version
176
148
  version: '0'
177
- segments:
178
- - 0
179
- hash: 4413858275826022328
180
149
  requirements: []
181
150
  rubyforge_project:
182
- rubygems_version: 1.8.24
151
+ rubygems_version: 2.2.2
183
152
  signing_key:
184
- specification_version: 3
153
+ specification_version: 4
185
154
  summary: Really simple IPC. Pipes, TCP sockets, beanstalkd, ...
186
155
  test_files: []
data/Rakefile DELETED
@@ -1,35 +0,0 @@
1
- require "rubygems"
2
- require "rdoc/task"
3
- require 'rspec/core/rake_task'
4
- require 'rubygems/package_task'
5
- require 'rake/clean'
6
-
7
- desc "Run all tests: Exhaustive."
8
- RSpec::Core::RakeTask.new
9
-
10
- task :default => :spec
11
-
12
- task :stats do
13
- %w(lib spec).each do |path|
14
- printf "%10s:", path
15
- system %Q(find #{path} -name "*.rb" | xargs wc -l | grep total)
16
- end
17
- end
18
-
19
- require 'yard'
20
- YARD::Rake::YardocTask.new do |t|
21
- # t.files = ['lib/**/*.rb']
22
- # t.options = ['--any', '--extra', '--opts'] # optional
23
- end
24
-
25
- # This task actually builds the gem.
26
- task :gem => :spec
27
- spec = eval(File.read('cod.gemspec'))
28
-
29
- desc "Generate the gem package."
30
- Gem::PackageTask.new(spec) do |pkg|
31
- # pkg.need_tar = true
32
- end
33
-
34
- CLEAN << 'pkg'
35
- CLEAN << 'doc'
@@ -1,32 +0,0 @@
1
- module Cod
2
- class BidirPipe < Channel
3
- # The Cod::Pipe instance we're currently #put'ting to.
4
- attr_reader :w
5
- # The Cod::Pipe instance we're currently #get'ting from.
6
- attr_reader :r
7
-
8
- def initialize(serializer=nil, pipe_pair=nil)
9
- @serializer = serializer || SimpleSerializer.new
10
- @r, @w = pipe_pair || [Cod.pipe(@serializer), Cod.pipe(@serializer)]
11
- end
12
-
13
- def put(msg)
14
- w.put(msg)
15
- end
16
-
17
- def get
18
- r.get
19
- end
20
-
21
- def close
22
- r.close
23
- w.close
24
- end
25
-
26
- # Swaps the end of this pipe around.
27
- #
28
- def swap!
29
- @r, @w = w, r
30
- end
31
- end
32
- end