ruby_channel 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in ruby_channel.gemspec
4
+ gemspec
data/README ADDED
@@ -0,0 +1,7 @@
1
+ This is a simple library aimed to use channels in Ruby the same concept that channels in Google's Go.
2
+
3
+ Missing tests and decent README? Read the very simple code!
4
+
5
+ Shame on me!
6
+
7
+ MIT license... bla, bla, bla
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,29 @@
1
+ require 'monitor'
2
+ require 'thread'
3
+
4
+ require 'ruby_channel/channel.rb'
5
+ require 'ruby_channel/selector.rb'
6
+ require 'ruby_channel/version.rb'
7
+
8
+ module RubyChannel
9
+ CHANNEL_DEBUG = false
10
+ end
11
+
12
+ module Kernel
13
+ def go(*args)
14
+ Thread.new do
15
+ begin
16
+ yield(*args)
17
+ rescue Exception => e
18
+ p e
19
+ p e.backtrace
20
+ end
21
+ end
22
+ end
23
+
24
+ def select_channel
25
+ s = RubyChannel::Selector.new
26
+ yield s
27
+ s.select
28
+ end
29
+ end
@@ -0,0 +1,140 @@
1
+ module RubyChannel
2
+ class Channel
3
+ attr_reader :mutex, :queue
4
+
5
+ #
6
+ # Creates a new channel.
7
+ #
8
+ def initialize
9
+ @queue = []
10
+ @waiting = []
11
+ @mutex = Mutex.new
12
+ end
13
+
14
+ #
15
+ # Pushes +obj+ to the queue.
16
+ #
17
+ def push(obj)
18
+ @mutex.synchronize do
19
+ @queue.push obj
20
+ begin
21
+ t = @waiting.shift
22
+ t.wakeup if t
23
+ rescue ThreadError
24
+ retry
25
+ end
26
+ end
27
+ end
28
+
29
+ #
30
+ # Alias of push
31
+ #
32
+ alias << push
33
+
34
+ #
35
+ # Retrieves data from channel. If the channel is empty, the calling thread is
36
+ # suspended until data is pushed onto channel.
37
+ #
38
+ def pop
39
+ @mutex.synchronize do
40
+ loop do
41
+ if @queue.empty?
42
+ @waiting.push Thread.current
43
+ @mutex.sleep
44
+ else
45
+ return @queue.shift
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ #
52
+ # Retrieves data from channel like method +pop+, but if +non_block+ is true, the
53
+ # thread isn't suspended, and an exception is raised.
54
+ #
55
+ def pop!
56
+ @mutex.synchronize do
57
+ loop do
58
+ if @queue.empty?
59
+ raise ThreadError, "Empty Channel"
60
+ @waiting.push Thread.current
61
+ @mutex.sleep
62
+ else
63
+ return @queue.shift
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ #
70
+ # Redirect signal to other channel
71
+ #
72
+ def redirect_to(channel, callback_method=nil, *args)
73
+ value = self.pop
74
+ value.send(callback_method, *args) if callback_method
75
+ yield value if block_given?
76
+ channel << value
77
+ end
78
+ alias >> redirect_to
79
+
80
+ #
81
+ # Returns +true+ if the channel is empty.
82
+ #
83
+ def empty?
84
+ @queue.empty?
85
+ end
86
+
87
+ #
88
+ # Removes all objects from the channel.
89
+ #
90
+ def clear
91
+ @queue.clear
92
+ end
93
+
94
+ #
95
+ # Returns the length of the channel.
96
+ #
97
+ def length
98
+ @queue.length
99
+ end
100
+
101
+ #
102
+ # Alias of length.
103
+ #
104
+ alias size length
105
+
106
+ #
107
+ # Returns the number of threads waiting on the queue.
108
+ #
109
+ def waiting_size
110
+ @waiting.size
111
+ end
112
+
113
+ #
114
+ # Method called only by selector to subscribe listeners, dont
115
+ # use it, unless you understand exactly what you're doing!
116
+ #
117
+ def subscribe(selector)
118
+ channel = self
119
+ @mutex.synchronize do
120
+ loop do
121
+ return selector.result unless selector.waiting?
122
+ if @queue.empty?
123
+ @waiting.push Thread.current
124
+ @mutex.sleep
125
+ else
126
+ selector.mutex.synchronize do
127
+ if selector.waiting?
128
+ result = selector.update_result(channel, @queue.shift)
129
+ yield result
130
+ end
131
+ end
132
+ selector.release_result
133
+ return selector.result
134
+ end
135
+ end
136
+ end
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,63 @@
1
+ module RubyChannel
2
+ class Selector
3
+ attr_reader :waiting, :result, :mutex
4
+ alias waiting? waiting
5
+
6
+ def initialize
7
+ @threads = []
8
+ @waiting = true
9
+ @mutex = Mutex.new
10
+ @return_thread = nil
11
+ end
12
+
13
+ def listen(channel, &block)
14
+ selector = self
15
+ @threads << Thread.new do
16
+ Thread.current[:name] = "Listener" if RubyChannel::CHANNEL_DEBUG
17
+ channel.subscribe(selector, &block)
18
+ end
19
+ end
20
+
21
+ def timeout(value, &block)
22
+ timeout_channel = Channel.new
23
+ Thread.new do
24
+ Thread.current[:name] = "Timeout Listener" if RubyChannel::CHANNEL_DEBUG
25
+ sleep value
26
+ timeout_channel << :timeout
27
+ end
28
+ listen timeout_channel, &block
29
+ end
30
+
31
+ def default(&block)
32
+ default_channel = Channel.new
33
+ Thread.new do
34
+ Thread.current[:name] = "Default Listener" if RubyChannel::CHANNEL_DEBUG
35
+ default_channel << :default
36
+ end
37
+ listen default_channel, &block
38
+ end
39
+
40
+ def select
41
+ Thread.current[:name] = "select" if RubyChannel::CHANNEL_DEBUG
42
+ @mutex.synchronize do
43
+ if waiting?
44
+ @return_thread = Thread.current
45
+ Thread.list.each{|t| puts "#{t.inspect}: #{t[:name]}"} if RubyChannel::CHANNEL_DEBUG
46
+ @mutex.sleep
47
+ end
48
+ end
49
+ @threads.each{ |thread| thread.wakeup }
50
+ @threads.clear
51
+ return @result
52
+ end
53
+
54
+ def update_result(channel, value)
55
+ @waiting = false
56
+ @result = value
57
+ end
58
+
59
+ def release_result
60
+ @return_thread.run if @return_thread
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,3 @@
1
+ module RubyChannel
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/ruby_channel/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "ruby_channel"
6
+ s.version = RubyChannel::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Dalton Pinto']
9
+ s.email = ['dalthon@aluno.ita.br']
10
+ s.homepage = "http://rubygems.org/gems/ruby_channel"
11
+ s.summary = "A way to use channels in Ruby, inspired by Google's Go"
12
+ s.description = "A way to use channels in Ruby, inspired by Google's Go and Ilya Grigorik's gem 'agent'"
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "ruby_channel"
16
+
17
+ s.add_development_dependency "bundler", ">= 1.0.0"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
21
+ s.require_path = 'lib'
22
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_channel
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Dalton Pinto
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-02-09 00:00:00 -02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: bundler
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 0
31
+ - 0
32
+ version: 1.0.0
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: A way to use channels in Ruby, inspired by Google's Go and Ilya Grigorik's gem 'agent'
36
+ email:
37
+ - dalthon@aluno.ita.br
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - .gitignore
46
+ - Gemfile
47
+ - README
48
+ - Rakefile
49
+ - lib/ruby_channel.rb
50
+ - lib/ruby_channel/channel.rb
51
+ - lib/ruby_channel/selector.rb
52
+ - lib/ruby_channel/version.rb
53
+ - ruby_channel.gemspec
54
+ has_rdoc: true
55
+ homepage: http://rubygems.org/gems/ruby_channel
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options: []
60
+
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 1
78
+ - 3
79
+ - 6
80
+ version: 1.3.6
81
+ requirements: []
82
+
83
+ rubyforge_project: ruby_channel
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: A way to use channels in Ruby, inspired by Google's Go
88
+ test_files: []
89
+