cod 0.5.0 → 0.6.0

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