wires 0.5.6 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51987a7590eb20419d2b26717efc9085ea19043f
4
- data.tar.gz: ed10fe66856e3517d7e9779188957e022232d4d2
3
+ metadata.gz: e9f2487653b2bc09e49a835f4945890973744d1a
4
+ data.tar.gz: 273bf1a229f890c1a1bae0e7002cfa8c99c56e2d
5
5
  SHA512:
6
- metadata.gz: 75b868f5b060e88b5fc8f8024946cd6c862af613f8afbccfea85247e11a33bb87498f38677f941869cbd6ee7b8b81122f32249bbd61c82d95b7876244fa9350a
7
- data.tar.gz: 9f70b430842b38fbec44c558720e6a846ce88d738a7e742777a873f46c9d129aa4ee519a1663ed0194f9befcd99c05aa6e62b3eb5f1590cf1df91221351ee110
6
+ metadata.gz: a087b9a5bc3c6cc604d1272401e24d0e428b9868b6c3a615384bd35af3862b5ef896832bbb6d6c671b683871f8993ca6630ad37b53eff1f1ce418cb075ca888b
7
+ data.tar.gz: f6fd9105f3d1cbc90489e2e933065214657463a261e94de1fe85faac8f2c609689b303b6384bd6f50e388ccfd0b5689418c7caf6533406324700baf22ee89c62
@@ -291,6 +291,123 @@ module Wires.current_network::Namespace
291
291
  fire(*args, **kwargs)
292
292
  end
293
293
 
294
+ # Synchronize execution of this thread to an incoming event.
295
+ # The user block will be executed, and will not return until a matching
296
+ # incoming event has been fired
297
+ # (see the {SyncHelper})
298
+ #
299
+ # @note In order to use this method correctly, the "action" that causes the
300
+ # incoming event to be fired should happen within the user block. If
301
+ # it happens before the user block, one risks a race condition; if after,
302
+ # one risks deadlock (or timeout) due to the feedback event being missed.
303
+ # As long as the feedback event happens after execution of the user block
304
+ # has begun, the event is guaranteed to be caught and processed.
305
+ #
306
+ # @param *events [<Symbol, Event>] the event pattern(s) filter
307
+ # to listen with. (see {#register}).
308
+ # @param timeout [Fixnum] the timeout, in seconds.
309
+ # (see {SyncHelper#wait}).
310
+ # @param &block [Proc] the user block. This block will be executed inline
311
+ # and passed an instance of {SyncHelper}. Within this block, the helper
312
+ # should be configured using its methods. Optionally, {SyncHelper#wait}
313
+ # can be called to wait in a specific location in the block. Otherwise,
314
+ # it will be called implicitly at the end of the block.
315
+ #
316
+ def sync_on(*events, timeout:nil, &block)
317
+ SyncHelper.new(events, self, timeout:timeout, &block)
318
+ nil
319
+ end
320
+
321
+ # Helper class passed to user block in {Channel#sync} method.
322
+ # Read here for how to use the helper, but never instantiate it yourself.
323
+ class SyncHelper
324
+
325
+ # Don't instantiate this class directly, use {Channel#sync}
326
+ # @api private
327
+ def initialize(events, channel, timeout:nil)
328
+ @timeout = timeout
329
+ @lock, @cond = Mutex.new, ConditionVariable.new
330
+ @conditions = []
331
+ @executions = []
332
+ @received = []
333
+
334
+ # Create the temporary event handler to capture incoming matches
335
+ proc = Proc.new { |e,c| @lock.synchronize { snag e,c } }
336
+
337
+ # Run the user block within the lock and wait afterward if they didn't
338
+ @lock.synchronize {
339
+ channel.register events, &proc
340
+ yield self
341
+ wait unless @waited
342
+ channel.unregister &proc
343
+ }
344
+ end
345
+
346
+ # Add a condition which must be fulfilled for {#wait} to find a match.
347
+ #
348
+ # @param &block [Proc] the block specifiying the condition to be met.
349
+ # It will be passed the event and channel, and the truthiness of its
350
+ # return value will be evaluated to determine if the condition is met.
351
+ # It will only be executed if the +[event,channel]+ pair fits the
352
+ # filter and meets all of the other evaluated conditions so far.
353
+ #
354
+ def condition(&block)
355
+ @conditions << block if block
356
+ nil
357
+ end
358
+
359
+ # Add a execution to run on the matching event for each {#wait}.
360
+ #
361
+ # @param &block [Proc] the block to be executed.
362
+ # It will only be executed if the +[event,channel]+ pair fits the
363
+ # filter and met all of the conditions to fulfill the {#wait}.
364
+ # The block will not be run if the {#wait} times out.
365
+ #
366
+ def execute(&block)
367
+ @executions << block if block
368
+ nil
369
+ end
370
+
371
+ # Wait for exactly one matching event meeting all {#conditions} to come.
372
+ #
373
+ # @note This will be called once implicitly at the end of the user block
374
+ # unless it gets called explicitly somewhere within the user block.
375
+ # It can be called multiple times within the user block to require
376
+ # one matching event each time within the block.
377
+ #
378
+ # @param timeout [Fixnum] The maximum time to wait for a match,
379
+ # specified in seconds. By default, it will be the number used at
380
+ # instantiation (passed from {Channel#sync_on}).
381
+ #
382
+ # @return the matching {Event} object, or nil if timed out.
383
+ #
384
+ def wait(timeout=@timeout)
385
+ @waited = true
386
+ result = nil
387
+
388
+ # Loop through each result, making sure it matches the conditions,
389
+ # returning nil if the wait timed out and didn't push into @received
390
+ loop do
391
+ @cond.wait @lock, timeout
392
+ result = @received.pop
393
+ return nil unless result
394
+ break if !@conditions.detect { |blk| !blk.call *result }
395
+ end
396
+
397
+ # Run all the execute blocks on the result
398
+ @executions.each { |blk| blk.call *result }
399
+ result.first #=> return event
400
+ end
401
+
402
+ private
403
+
404
+ # Snag the given event and channel to try it out in the blocking thread
405
+ def snag(*args)
406
+ @received << args
407
+ @cond.signal # Pass execution back to blocking thread and block this one
408
+ end
409
+ end
410
+
294
411
  # Determine if one channel matches another.
295
412
  #
296
413
  # In this context, a match indicates a receiver relationship.
@@ -13,6 +13,11 @@ module Wires.current_network::Namespace
13
13
  codeblock
14
14
  end
15
15
 
16
+ def sync_on(event, channel=self, **kwargs, &codeblock)
17
+ channel = Channel.new(channel) unless channel.is_a? Channel
18
+ channel.sync_on(event, **kwargs, &codeblock)
19
+ end
20
+
16
21
  def fire(event, channel=self, **kwargs)
17
22
  channel = Channel.new(channel) unless channel.is_a? Channel
18
23
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wires
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe McIlvain
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-19 00:00:00.000000000 Z
11
+ date: 2014-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: threadlock
@@ -162,7 +162,7 @@ files:
162
162
  - lib/wires/core_ext/time.rb
163
163
  homepage: https://github.com/jemc/wires/
164
164
  licenses:
165
- - Copyright 2013 Joe McIlvain. All rights reserved.
165
+ - Copyright 2013-2014 Joe McIlvain. All rights reserved.
166
166
  metadata: {}
167
167
  post_install_message:
168
168
  rdoc_options: []
@@ -185,3 +185,4 @@ signing_key:
185
185
  specification_version: 4
186
186
  summary: wires
187
187
  test_files: []
188
+ has_rdoc: