methodmissing-channel 1.0.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.
data/README ADDED
@@ -0,0 +1,51 @@
1
+ Simple native Channel object for Ruby MRI
2
+ (c) 2009 Lourens Naudé (methodmissing), James Tucker (raggi)
3
+
4
+ http://github.com/methodmissing/channel
5
+
6
+ This library works with Ruby 1.8 and 1.9 and is a more efficient
7
+ implementation of the following Ruby code :
8
+
9
+ class RubyChannel
10
+ def initialize
11
+ @subscribers = []
12
+ end
13
+ def subscribe(&b)
14
+ @subscribers << b
15
+ self
16
+ end
17
+ def <<(o)
18
+ @subscribers.each { |s| s.call(o) }
19
+ end
20
+ end
21
+
22
+ Use cases :
23
+
24
+ * In process message dispatch
25
+ * Parsers and protocols with a message, error and warning channel
26
+ * Facilitates decoupled notification and callback patterns
27
+
28
+ Caveats :
29
+
30
+ * Fixed width channel size for very thin, and fast, subscriber management
31
+
32
+ Examples :
33
+
34
+ counter = 0
35
+ ch = Channel.new.subscribe{|obj| @counter += obj }
36
+ ch << 3
37
+ ch << -2
38
+ counter #=> 1
39
+
40
+ Todo :
41
+
42
+ * A deferrable mode that notifies on a background thread
43
+ * More flexible notification mechanism through integration with http://github.com/methodmissing/callback/tree/master
44
+
45
+ To run the test suite:
46
+
47
+ rake
48
+
49
+ To run the benchmarks:
50
+
51
+ rake bench
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+
5
+ CH_ROOT = 'ext/channel'
6
+
7
+ desc 'Default: test'
8
+ task :default => :test
9
+
10
+ desc 'Run channel tests.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs = [CH_ROOT]
13
+ t.pattern = 'test/test_*.rb'
14
+ t.verbose = true
15
+ end
16
+ task :test => :build
17
+
18
+ namespace :build do
19
+ file "#{CH_ROOT}/channel.c"
20
+ file "#{CH_ROOT}/extconf.rb"
21
+ file "#{CH_ROOT}/Makefile" => %W(#{CH_ROOT}/channel.c #{CH_ROOT}/extconf.rb) do
22
+ Dir.chdir(CH_ROOT) do
23
+ ruby 'extconf.rb'
24
+ end
25
+ end
26
+
27
+ desc "generate makefile"
28
+ task :makefile => %W(#{CH_ROOT}/Makefile #{CH_ROOT}/channel.c)
29
+
30
+ dlext = Config::CONFIG['DLEXT']
31
+ file "#{CH_ROOT}/channel.#{dlext}" => %W(#{CH_ROOT}/Makefile #{CH_ROOT}/channel.c) do
32
+ Dir.chdir(CH_ROOT) do
33
+ sh 'make' # TODO - is there a config for which make somewhere?
34
+ end
35
+ end
36
+
37
+ desc "compile channel extension"
38
+ task :compile => "#{CH_ROOT}/channel.#{dlext}"
39
+
40
+ task :clean do
41
+ Dir.chdir(CH_ROOT) do
42
+ sh 'make clean'
43
+ end if File.exists?("#{CH_ROOT}/Makefile")
44
+ end
45
+
46
+ CLEAN.include("#{CH_ROOT}/Makefile")
47
+ CLEAN.include("#{CH_ROOT}/channel.#{dlext}")
48
+ end
49
+
50
+ task :clean => %w(build:clean)
51
+
52
+ desc "compile"
53
+ task :build => %w(build:compile)
54
+
55
+ task :install do |t|
56
+ Dir.chdir(CH_ROOT) do
57
+ sh 'sudo make install'
58
+ end
59
+ end
60
+
61
+ desc "clean build install"
62
+ task :setup => %w(clean build install)
63
+
64
+ desc "run benchmarks"
65
+ task :bench do |t|
66
+ ruby "bench/bench.rb"
67
+ end
68
+ task :bench => :build
data/bench/bench.rb ADDED
@@ -0,0 +1,15 @@
1
+ require "benchmark"
2
+ $:.unshift "."
3
+ require File.dirname(__FILE__) + "/../ext/channel/channel"
4
+ require File.dirname(__FILE__) + "/ruby_channel"
5
+
6
+ channel = Channel.new(16)
7
+ 16.times{ channel.subscribe{|o| o } }
8
+ rb_channel = RubyChannel.new
9
+ 16.times{ rb_channel.subscribe{|o| o } }
10
+
11
+ TESTS = 10_000
12
+ Benchmark.bmbm do |results|
13
+ results.report("Channel#<<") { TESTS.times { channel << :obj } }
14
+ results.report("RubyChannel#<<") { TESTS.times { rb_channel << :obj } }
15
+ end
@@ -0,0 +1,12 @@
1
+ class RubyChannel
2
+ def initialize
3
+ @subscribers = []
4
+ end
5
+ def subscribe(&b)
6
+ @subscribers << b
7
+ self
8
+ end
9
+ def <<(o)
10
+ @subscribers.each { |s| s.call(o) }
11
+ end
12
+ end
data/channel.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "channel"
3
+ s.version = "1.0.0"
4
+ s.date = "2009-08-28"
5
+ s.summary = "Native MRI channel"
6
+ s.email = "lourens@methodmissing.com"
7
+ s.homepage = "http://github.com/methodmissing/channel"
8
+ s.description = "Simple native Channel object for Ruby MRI (1.8.{6,7} and 1.9.2)"
9
+ s.has_rdoc = true
10
+ s.authors = ["Lourens Naudé (methodmissing)","James Tucker (raggi)"]
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = %w[
13
+ README
14
+ Rakefile
15
+ bench/bench.rb
16
+ bench/ruby_channel.rb
17
+ ext/channel/extconf.rb
18
+ ext/channel/channel.c
19
+ channel.gemspec
20
+ ] + Dir.glob('test/*')
21
+ s.rdoc_options = ["--main", "README"]
22
+ s.extra_rdoc_files = ["README"]
23
+ s.extensions << "ext/channel/extconf.rb"
24
+ end
@@ -0,0 +1,115 @@
1
+ #include "ruby.h"
2
+
3
+ typedef struct {
4
+ int size;
5
+ int sbs;
6
+ VALUE *subscribers;
7
+ } RChannel;
8
+
9
+ #define MAX_CHANNEL_SIZE 32
10
+ #define GetChannelStruct(obj) (Check_Type(obj, T_DATA), (RChannel*)DATA_PTR(obj))
11
+
12
+ VALUE rb_cChannel;
13
+ static ID id_call;
14
+
15
+ static void mark_channel(RChannel* ch)
16
+ {
17
+ int i;
18
+ for (i=0; i < ch->sbs; i++) {
19
+ rb_gc_mark(ch->subscribers[i]);
20
+ }
21
+ }
22
+
23
+ static void free_channel(RChannel* ch)
24
+ {
25
+ xfree(ch);
26
+ }
27
+
28
+ static VALUE channel_alloc _((VALUE));
29
+ static VALUE
30
+ channel_alloc( VALUE klass )
31
+ {
32
+ VALUE ch;
33
+ RChannel* chs;
34
+ ch = Data_Make_Struct(klass, RChannel, mark_channel, free_channel, chs);
35
+ chs->size = 0;
36
+ chs->sbs = 0;
37
+ chs->subscribers = 0;
38
+
39
+ return ch;
40
+ }
41
+
42
+ static VALUE
43
+ rb_channel_new(VALUE ch, int size)
44
+ {
45
+ RChannel* chs = GetChannelStruct(ch);
46
+ if (size < 0 || size > MAX_CHANNEL_SIZE){
47
+ rb_raise(rb_eArgError, "Invalid channel size!");
48
+ }
49
+ if (size == 0) size++;
50
+ chs->size = size;
51
+ chs->subscribers = ALLOC_N(VALUE, size);
52
+ return ch;
53
+ }
54
+
55
+ static VALUE
56
+ rb_channel_initialize( int argc, VALUE *argv, VALUE ch )
57
+ {
58
+ VALUE size;
59
+ int channel_size;
60
+ rb_scan_args(argc, argv, "01", &size);
61
+ channel_size = NIL_P(size) ? 0 : FIX2INT(size);
62
+ return rb_channel_new(ch, channel_size);
63
+ }
64
+
65
+ static VALUE
66
+ rb_channel_size( VALUE ch )
67
+ {
68
+ RChannel* chs = GetChannelStruct(ch);
69
+ return INT2FIX(chs->size);
70
+ }
71
+
72
+ static VALUE
73
+ rb_channel_subscribers( VALUE ch )
74
+ {
75
+ RChannel* chs = GetChannelStruct(ch);
76
+ return INT2FIX(chs->sbs);
77
+ }
78
+
79
+ static VALUE
80
+ rb_channel_subscribe( int argc, VALUE *argv, VALUE ch )
81
+ {
82
+ VALUE cb;
83
+ RChannel* chs = GetChannelStruct(ch);
84
+ if (!rb_block_given_p()) rb_raise(rb_eArgError, "Block callback required!");
85
+ cb = rb_block_proc();
86
+ chs->subscribers[chs->sbs] = cb;
87
+ chs->sbs++;
88
+ return ch;
89
+ }
90
+
91
+ static VALUE
92
+ rb_channel_push( VALUE ch, VALUE obj )
93
+ {
94
+ int i;
95
+ RChannel* chs = GetChannelStruct(ch);
96
+ for (i=0; i < chs->sbs; i++) {
97
+ rb_funcall(chs->subscribers[i],id_call,1,obj);
98
+ }
99
+ return ch;
100
+ }
101
+
102
+ void
103
+ Init_channel()
104
+ {
105
+ id_call = rb_intern("call");
106
+
107
+ rb_cChannel = rb_define_class("Channel", rb_cObject);
108
+ rb_define_alloc_func(rb_cChannel, channel_alloc);
109
+
110
+ rb_define_method(rb_cChannel,"initialize", rb_channel_initialize, -1);
111
+ rb_define_method(rb_cChannel,"size", rb_channel_size, 0);
112
+ rb_define_method(rb_cChannel,"subscribers", rb_channel_subscribers, 0);
113
+ rb_define_method(rb_cChannel,"subscribe", rb_channel_subscribe, -1);
114
+ rb_define_method(rb_cChannel,"<<", rb_channel_push, 1);
115
+ }
@@ -0,0 +1,12 @@
1
+ require 'mkmf'
2
+
3
+ def add_define(name)
4
+ $defs.push("-D#{name}")
5
+ end
6
+
7
+ dir_config('channel')
8
+
9
+ add_define 'RUBY19' if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h')
10
+ add_define 'RUBY18' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
11
+
12
+ create_makefile('channel')
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: methodmissing-channel
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - "Lourens Naud\xC3\xA9 (methodmissing)"
8
+ - James Tucker (raggi)
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-08-28 00:00:00 -07:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Simple native Channel object for Ruby MRI (1.8.{6,7} and 1.9.2)
18
+ email: lourens@methodmissing.com
19
+ executables: []
20
+
21
+ extensions:
22
+ - ext/channel/extconf.rb
23
+ extra_rdoc_files:
24
+ - README
25
+ files:
26
+ - README
27
+ - Rakefile
28
+ - bench/bench.rb
29
+ - bench/ruby_channel.rb
30
+ - ext/channel/extconf.rb
31
+ - ext/channel/channel.c
32
+ - channel.gemspec
33
+ has_rdoc: true
34
+ homepage: http://github.com/methodmissing/channel
35
+ post_install_message:
36
+ rdoc_options:
37
+ - --main
38
+ - README
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project:
56
+ rubygems_version: 1.2.0
57
+ signing_key:
58
+ specification_version: 2
59
+ summary: Native MRI channel
60
+ test_files: []
61
+