higgs 0.1.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.
Files changed (64) hide show
  1. data/ChangeLog +208 -0
  2. data/LICENSE +26 -0
  3. data/README +2 -0
  4. data/Rakefile +75 -0
  5. data/bin/higgs_backup +67 -0
  6. data/bin/higgs_dump_index +43 -0
  7. data/bin/higgs_dump_jlog +42 -0
  8. data/bin/higgs_verify +37 -0
  9. data/lib/cgi/session/higgs.rb +72 -0
  10. data/lib/higgs/block.rb +192 -0
  11. data/lib/higgs/cache.rb +117 -0
  12. data/lib/higgs/dbm.rb +55 -0
  13. data/lib/higgs/exceptions.rb +31 -0
  14. data/lib/higgs/flock.rb +77 -0
  15. data/lib/higgs/index.rb +164 -0
  16. data/lib/higgs/jlog.rb +159 -0
  17. data/lib/higgs/lock.rb +189 -0
  18. data/lib/higgs/storage.rb +1086 -0
  19. data/lib/higgs/store.rb +228 -0
  20. data/lib/higgs/tar.rb +390 -0
  21. data/lib/higgs/thread.rb +370 -0
  22. data/lib/higgs/tman.rb +513 -0
  23. data/lib/higgs/utils/bman.rb +285 -0
  24. data/lib/higgs/utils.rb +22 -0
  25. data/lib/higgs/version.rb +21 -0
  26. data/lib/higgs.rb +59 -0
  27. data/misc/cache_bench/cache_bench.rb +43 -0
  28. data/misc/dbm_bench/.strc +8 -0
  29. data/misc/dbm_bench/Rakefile +78 -0
  30. data/misc/dbm_bench/dbm_multi_thread.rb +199 -0
  31. data/misc/dbm_bench/dbm_rnd_delete.rb +43 -0
  32. data/misc/dbm_bench/dbm_rnd_read.rb +44 -0
  33. data/misc/dbm_bench/dbm_rnd_update.rb +44 -0
  34. data/misc/dbm_bench/dbm_seq_read.rb +45 -0
  35. data/misc/dbm_bench/dbm_seq_write.rb +44 -0
  36. data/misc/dbm_bench/st_verify.rb +28 -0
  37. data/misc/io_bench/cksum_bench.rb +48 -0
  38. data/misc/io_bench/jlog_bench.rb +71 -0
  39. data/misc/io_bench/write_bench.rb +128 -0
  40. data/misc/thread_bench/lock_bench.rb +132 -0
  41. data/mkrdoc.rb +8 -0
  42. data/rdoc.yml +13 -0
  43. data/sample/count.rb +60 -0
  44. data/sample/dbmtest.rb +38 -0
  45. data/test/Rakefile +45 -0
  46. data/test/run.rb +32 -0
  47. data/test/test_block.rb +163 -0
  48. data/test/test_cache.rb +214 -0
  49. data/test/test_cgi_session.rb +142 -0
  50. data/test/test_flock.rb +162 -0
  51. data/test/test_index.rb +258 -0
  52. data/test/test_jlog.rb +180 -0
  53. data/test/test_lock.rb +320 -0
  54. data/test/test_online_backup.rb +169 -0
  55. data/test/test_storage.rb +439 -0
  56. data/test/test_storage_conf.rb +202 -0
  57. data/test/test_storage_init_opts.rb +89 -0
  58. data/test/test_store.rb +211 -0
  59. data/test/test_tar.rb +432 -0
  60. data/test/test_thread.rb +541 -0
  61. data/test/test_tman.rb +875 -0
  62. data/test/test_tman_init_opts.rb +56 -0
  63. data/test/test_utils_bman.rb +234 -0
  64. metadata +115 -0
@@ -0,0 +1,132 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ # for ident(1)
4
+ CVS_ID = '$Id: lock_bench.rb 559 2007-09-25 15:20:20Z toki $'
5
+
6
+ $: << File.join(File.dirname($0), '..', '..', 'lib')
7
+
8
+ require 'benchmark'
9
+ require 'higgs/thread'
10
+ require 'monitor'
11
+ require 'sync'
12
+ require 'thread'
13
+
14
+ loop_count = (ARGV.shift || '1000').to_i
15
+ thread_count = (ARGV.shift || '10').to_i
16
+ puts "#{$0}: LOOP:#{loop_count}, THREAD:#{thread_count}"
17
+
18
+ Benchmark.bm(30) do |x|
19
+ [ [ Mutex.new, 'Mutex' ],
20
+ [ Monitor.new, 'Monitor' ],
21
+ [ Higgs::ReadWriteLock.new.read_lock, 'ReadWriteLock (read:M)' ],
22
+ [ Higgs::ReadWriteLock.new.write_lock, 'ReadWriteLock (write:M)' ]
23
+ ].each do |m, name|
24
+ barrier = Higgs::Barrier.new(thread_count + 1)
25
+ th_grp = ThreadGroup.new
26
+ thread_count.times do
27
+ th_grp.add Thread.new{
28
+ barrier.wait
29
+ loop_count.times do
30
+ m.synchronize{
31
+ # nothing to do.
32
+ }
33
+ end
34
+ }
35
+ end
36
+
37
+ x.report(name) {
38
+ barrier.wait
39
+ for t in th_grp.list
40
+ t.join
41
+ end
42
+ }
43
+ end
44
+
45
+ r_lock, w_lock = Higgs::ReadWriteLock.new.to_a
46
+ barrier = Higgs::Barrier.new(thread_count + 1)
47
+ th_grp = ThreadGroup.new
48
+ (thread_count - 1).times do
49
+ th_grp.add Thread.new{
50
+ barrier.wait
51
+ loop_count.times do
52
+ r_lock.synchronize{
53
+ # nothing to do.
54
+ }
55
+ end
56
+ }
57
+ end
58
+ th_grp.add Thread.new{
59
+ barrier.wait
60
+ loop_count.times do
61
+ w_lock.synchronize{
62
+ # nothing to do.
63
+ }
64
+ end
65
+ }
66
+
67
+ x.report('ReadWriteLock (read:M/write:1)') {
68
+ barrier.wait
69
+ for t in th_grp.list
70
+ t.join
71
+ end
72
+ }
73
+
74
+ [ [ Sync::SH, 'Sync (read:M)' ],
75
+ [ Sync::EX, 'Sync (write:M)' ]
76
+ ].each do |mode, name|
77
+ s = Sync.new
78
+ barrier = Higgs::Barrier.new(thread_count + 1)
79
+ th_grp = ThreadGroup.new
80
+ thread_count.times do
81
+ th_grp.add Thread.new{
82
+ barrier.wait
83
+ loop_count.times do
84
+ s.synchronize(mode) {
85
+ # nothing to do.
86
+ }
87
+ end
88
+ }
89
+ end
90
+
91
+ x.report(name) {
92
+ barrier.wait
93
+ for t in th_grp.list
94
+ t.join
95
+ end
96
+ }
97
+ end
98
+
99
+ s = Sync.new
100
+ barrier = Higgs::Barrier.new(thread_count + 1)
101
+ th_grp = ThreadGroup.new
102
+ (thread_count - 1).times do
103
+ th_grp.add Thread.new{
104
+ barrier.wait
105
+ loop_count.times do
106
+ s.synchronize(Sync::SH) {
107
+ # nothing to do.
108
+ }
109
+ end
110
+ }
111
+ end
112
+ th_grp.add Thread.new{
113
+ barrier.wait
114
+ loop_count.times do
115
+ s.synchronize(Sync::EX) {
116
+ # nothing to do.
117
+ }
118
+ end
119
+ }
120
+
121
+ x.report('Sync (read:M/write:1)') {
122
+ barrier.wait
123
+ for t in th_grp.list
124
+ t.join
125
+ end
126
+ }
127
+ end
128
+
129
+ # Local Variables:
130
+ # mode: Ruby
131
+ # indent-tabs-mode: nil
132
+ # End:
data/mkrdoc.rb ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ CVS_ID = '$Id: mkrdoc.rb 564 2007-09-27 16:10:25Z toki $' # for ident(1)
4
+
5
+ require 'yaml'
6
+
7
+ rdoc_opts = YAML.load(IO.read('rdoc.yml'))
8
+ system 'rdoc', *(rdoc_opts['CommonOptions'] + rdoc_opts['CommandLineOptions']).flatten
data/rdoc.yml ADDED
@@ -0,0 +1,13 @@
1
+ CommonOptions:
2
+ - -SNa
3
+ - [ -i, . ]
4
+ - [ -m, Higgs ]
5
+
6
+ CommandLineOptions:
7
+ - [ -i, lib ]
8
+ - [ -x, misc ]
9
+ - [ -x, mkrdoc ]
10
+ - [ -x, pkg ]
11
+ - [ -x, sample ]
12
+ - [ -x, test ]
13
+ - [ -o, api ]
data/sample/count.rb ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'higgs'
4
+ require 'higgs/thread' # for Higgs::Barrier
5
+
6
+ num_of_write_threads = (ARGV.shift || '10').to_i
7
+ num_of_count = (ARGV.shift || '100').to_i
8
+
9
+ Higgs::Store.open('count') {|st|
10
+ st.transaction(true) {|tx|
11
+ puts "start - #{Time.now}"
12
+ p tx[:count]
13
+ puts ''
14
+ }
15
+
16
+ th_grp = ThreadGroup.new
17
+ barrier = Higgs::Barrier.new(num_of_write_threads + 2)
18
+ is_print = true
19
+
20
+ num_of_write_threads.times do
21
+ th_grp.add Thread.new{
22
+ barrier.wait
23
+ num_of_count.times do
24
+ st.transaction{|tx|
25
+ tx[:count] = 0 unless (tx.key? :count)
26
+ tx[:count] += 1
27
+ }
28
+ end
29
+ }
30
+ end
31
+
32
+ th_read = Thread.new{
33
+ barrier.wait
34
+ while (is_print)
35
+ st.transaction(true) {|tx|
36
+ p tx[:count]
37
+ }
38
+ sleep(0.1)
39
+ end
40
+ }
41
+
42
+ barrier.wait
43
+ for t in th_grp.list
44
+ t.join
45
+ end
46
+
47
+ is_print = false
48
+ th_read.join
49
+
50
+ st.transaction(true) {|tx|
51
+ puts ''
52
+ puts "last - #{Time.now}"
53
+ p tx[:count]
54
+ }
55
+ }
56
+
57
+ # Local Variables:
58
+ # mode: Ruby
59
+ # indent-tabs-mode: nil
60
+ # End:
data/sample/dbmtest.rb ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'higgs'
4
+
5
+ Higgs::DBM.open('test') {|dbm|
6
+ dbm.transaction{|tx|
7
+ keys = tx.keys
8
+ if (keys.length > 0) then
9
+ for k in keys
10
+ puts "-"
11
+ puts "key: #{k}"
12
+ puts "value: #{tx[k]}"
13
+ tx.each_property(k) do |name, value|
14
+ case (name)
15
+ when Symbol
16
+ puts "system_property[#{name}]: #{value}"
17
+ when String
18
+ puts "custom_property[#{name}]: #{value}"
19
+ else
20
+ raise "unexpected property name: #{name}"
21
+ end
22
+ end
23
+ end
24
+ else
25
+ tx['foobar'] = 'FB'
26
+ tx.set_property('foobar', 'number', 0)
27
+ tx['baz'] = 'BZ'
28
+ tx.set_property('baz', 'number', 1)
29
+ tx['quux'] = 'QX'
30
+ tx.set_property('quux', 'number', 2)
31
+ end
32
+ }
33
+ }
34
+
35
+ # Local Variables:
36
+ # mode: Ruby
37
+ # indent-tabs-mode: nil
38
+ # End:
data/test/Rakefile ADDED
@@ -0,0 +1,45 @@
1
+ # for ident(1)
2
+ CVS_ID = '$Id: Rakefile 561 2007-09-26 15:46:49Z toki $'
3
+
4
+ LIB_DIR = File.join(File.dirname(__FILE__), '..', 'lib')
5
+ RCOV_DIR = File.join(File.dirname(__FILE__), 'coverage')
6
+
7
+ task :default => [ :test ]
8
+
9
+ task :test do
10
+ ruby '-I', LIB_DIR, 'run.rb'
11
+ end
12
+
13
+ task :rcov do
14
+ rm_rf RCOV_DIR
15
+ sh 'rcov', '-I', LIB_DIR, '-x', 'rcov\.rb,run\.rb', 'run.rb'
16
+ end
17
+
18
+ task :optional do
19
+ for opt_rb in Dir['optional/*.rb']
20
+ symlnk_rb = File.basename(opt_rb)
21
+ unless (File.symlink? symlnk_rb) then
22
+ ln_s opt_rb, symlnk_rb, :verbose => true
23
+ end
24
+ end
25
+ end
26
+
27
+ task :clean do
28
+ rm_rf RCOV_DIR
29
+ end
30
+
31
+ task :clean_optional do
32
+ for opt_rb in Dir['optional/*.rb']
33
+ symlnk_rb = File.basename(opt_rb)
34
+ rm_f symlnk_rb
35
+ end
36
+ end
37
+
38
+ rule '.test' => [ '.rb' ] do |t|
39
+ ruby '-I', LIB_DIR, 'run.rb', '^' + Regexp.quote(t.source) + '$'
40
+ end
41
+
42
+ # Local Variables:
43
+ # mode: Ruby
44
+ # indent-tabs-mode: nil
45
+ # End:
data/test/run.rb ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ $: << File.dirname(__FILE__)
4
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
5
+
6
+ # for ident(1)
7
+ CVS_ID = '$Id: run.rb 575 2007-09-29 08:13:51Z toki $'
8
+
9
+ mask = // # any match
10
+ if ($0 == __FILE__) then
11
+ if (ARGV.length > 0 && ARGV[0] !~ /^-/) then
12
+ mask = Regexp.compile(ARGV.shift)
13
+ end
14
+ end
15
+
16
+ test_dir, this_name = File.split(__FILE__)
17
+ for test_rb in Dir.entries(test_dir).sort
18
+ case (test_rb)
19
+ when this_name
20
+ # skip
21
+ when /^test_.*\.rb$/
22
+ if (test_rb =~ mask) then
23
+ puts "load #{test_rb}"
24
+ require File.join(test_dir, test_rb)
25
+ end
26
+ end
27
+ end
28
+
29
+ # Local Variables:
30
+ # mode: Ruby
31
+ # indent-tabs-mode: nil
32
+ # End:
@@ -0,0 +1,163 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'digest/sha2'
4
+ require 'fileutils'
5
+ require 'higgs/block'
6
+ require 'test/unit'
7
+
8
+ module Higgs::Test
9
+ class BlockTest < Test::Unit::TestCase
10
+ include Higgs::Block
11
+
12
+ # for ident(1)
13
+ CVS_ID = '$Id: test_block.rb 559 2007-09-25 15:20:20Z toki $'
14
+
15
+ def setup
16
+ @io = File.open('block.test_io', 'w+')
17
+ @io.binmode
18
+ end
19
+
20
+ def teardown
21
+ @io.close unless @io.closed?
22
+ FileUtils.rm_f('block.test_io')
23
+ end
24
+
25
+ def test_single_head_write_read
26
+ body = 'foo'
27
+ body_cksum_bin = Digest::SHA512.digest(body)
28
+ head_write(@io, 'FOO', body.length, 'SHA512', body_cksum_bin)
29
+
30
+ @io.seek(0)
31
+ r = head_read(@io, 'FOO')
32
+ assert_equal(body.length, r[0])
33
+ assert_equal('SHA512', r[1])
34
+ assert_equal(body_cksum_bin, r[2])
35
+ end
36
+
37
+ def test_single_block_write_read
38
+ body = 'foo'
39
+ block_write(@io, 'FOO', body)
40
+
41
+ @io.seek(0)
42
+ assert_equal(body, block_read(@io, 'FOO'))
43
+ end
44
+
45
+ def test_many_block_write_read
46
+ data_list = (0..12).map{|i| 'Z' * 2**i }.map{|s| [ s[0..-2], s, s + 'Z' ] }.flatten
47
+ for s in data_list
48
+ block_write(@io, 'FOO', s)
49
+ end
50
+
51
+ i = 0
52
+ @io.seek(0)
53
+ while (s = block_read(@io, 'FOO'))
54
+ assert(! data_list.empty?, "nth:#{i}")
55
+ assert_equal(data_list.shift, s)
56
+ end
57
+ assert(data_list.empty?)
58
+ end
59
+
60
+ def test_empty_read
61
+ assert_nil(head_read(@io, 'FOO'))
62
+ assert_nil(block_read(@io, 'FOO'))
63
+ end
64
+
65
+ def test_head_read_BrokenError_short_read
66
+ @io.write("\x00")
67
+ @io.seek(0)
68
+ assert_raise(BrokenError) {
69
+ head_read(@io, 'FOO')
70
+ }
71
+ end
72
+
73
+ def test_head_read_BrokenError_broken_head_block
74
+ body = 'foo'
75
+ body_cksum_bin = Digest::SHA512.digest(body)
76
+ head_write(@io, 'FOO', body.length, 'SHA512', body_cksum_bin)
77
+
78
+ @io.seek(511)
79
+ @io.write("\xFF")
80
+ @io.seek(0)
81
+
82
+ assert_raise(BrokenError) {
83
+ head_read(@io, 'FOO')
84
+ }
85
+ end
86
+
87
+ def test_head_read_BrokenError_unknown_magic_symbol
88
+ body = 'foo'
89
+ body_cksum_bin = Digest::SHA512.digest(body)
90
+ head_write(@io, 'BAR', body.length, 'SHA512', body_cksum_bin)
91
+ @io.seek(0)
92
+
93
+ assert_raise(BrokenError) {
94
+ head_read(@io, 'FOO')
95
+ }
96
+ end
97
+
98
+ def test_block_read_BrokenError_short_unexpected_EOF_1
99
+ block_write(@io, 'FOO', 'foo')
100
+ @io.truncate(512)
101
+ @io.seek(0)
102
+ assert_raise(BrokenError) {
103
+ block_read(@io, 'FOO')
104
+ }
105
+ end
106
+
107
+ def test_block_read_BrokenError_short_read_1
108
+ block_write(@io, 'FOO', 'foo')
109
+ @io.truncate(513)
110
+ @io.seek(0)
111
+ assert_raise(BrokenError) {
112
+ block_read(@io, 'FOO')
113
+ }
114
+ end
115
+
116
+ def test_block_read_BrokenError_short_unexpected_EOF_2
117
+ block_write(@io, 'FOO', 'foo')
118
+ @io.truncate(515)
119
+ @io.seek(0)
120
+ assert_raise(BrokenError) {
121
+ block_read(@io, 'FOO')
122
+ }
123
+ end
124
+
125
+ def test_block_read_BrokenError_short_read_2
126
+ block_write(@io, 'FOO', 'foo')
127
+ @io.truncate(1023)
128
+ @io.seek(0)
129
+ assert_raise(BrokenError) {
130
+ block_read(@io, 'FOO')
131
+ }
132
+ end
133
+
134
+ def test_block_read_BrokenError_unknown_body_cksum_type
135
+ body = 'foo'
136
+ block_write(@io, 'FOO', body)
137
+ @io.seek(0)
138
+ head_write(@io, 'FOO', body.length, 'UNKNOWN', Digest::SHA512.digest(body))
139
+ @io.seek(0)
140
+
141
+ assert_raise(BrokenError) {
142
+ block_read(@io, 'FOO')
143
+ }
144
+ end
145
+
146
+ def test_block_read_BrokenError_unknown_body_cksum_error
147
+ body = 'foo'
148
+ block_write(@io, 'FOO', body)
149
+ @io.seek(0)
150
+ head_write(@io, 'FOO', body.length, 'SHA512', '')
151
+ @io.seek(0)
152
+
153
+ assert_raise(BrokenError) {
154
+ block_read(@io, 'FOO')
155
+ }
156
+ end
157
+ end
158
+ end
159
+
160
+ # Local Variables:
161
+ # mode: Ruby
162
+ # indent-tabs-mode: nil
163
+ # End:
@@ -0,0 +1,214 @@
1
+ #!/usr/local/bin/ruby
2
+
3
+ require 'higgs/cache'
4
+ require 'higgs/thread'
5
+ require 'test/unit'
6
+ require 'timeout'
7
+
8
+ module Higgs::Test
9
+ class LRUCacheTest < Test::Unit::TestCase
10
+ include Higgs
11
+
12
+ # for ident(1)
13
+ CVS_ID = '$Id: test_cache.rb 559 2007-09-25 15:20:20Z toki $'
14
+
15
+ CACHE_LIMIT_SIZE = 10
16
+
17
+ def setup
18
+ @cache = LRUCache.new(CACHE_LIMIT_SIZE)
19
+ end
20
+
21
+ def test_store_and_fetch
22
+ @cache[:foo] = 'apple'
23
+ @cache[:bar] = 'banana'
24
+ @cache[:baz] = 'orange'
25
+ assert_equal('apple', @cache[:foo])
26
+ assert_equal('banana', @cache[:bar])
27
+ assert_equal('orange', @cache[:baz])
28
+ end
29
+
30
+ def test_fetch_not_defined_value
31
+ assert_nil(@cache[:foo])
32
+ assert_nil(@cache[:bar])
33
+ assert_nil(@cache[:baz])
34
+ end
35
+
36
+ def test_delete
37
+ @cache[:foo] = 'apple'
38
+ @cache[:bar] = 'banana'
39
+ @cache[:baz] = 'orange'
40
+ @cache.delete(:bar)
41
+ assert_equal('apple', @cache[:foo])
42
+ assert_equal(nil, @cache[:bar])
43
+ assert_equal('orange', @cache[:baz])
44
+ end
45
+
46
+ def test_LRU_read
47
+ CACHE_LIMIT_SIZE.times do |i|
48
+ @cache[i] = i.to_s
49
+ end
50
+ CACHE_LIMIT_SIZE.times do |i|
51
+ assert_equal(i.to_s, @cache[i], "#{i}")
52
+ end
53
+
54
+ old_key = 0
55
+ last_key = CACHE_LIMIT_SIZE - 1
56
+ new_key = CACHE_LIMIT_SIZE
57
+
58
+ @cache[new_key] = new_key.to_s
59
+ assert_equal(nil, @cache[old_key])
60
+ assert_equal(last_key.to_s, @cache[last_key])
61
+ assert_equal(new_key.to_s, @cache[new_key])
62
+ end
63
+
64
+ def test_LRU_write
65
+ CACHE_LIMIT_SIZE.times do |i|
66
+ @cache[i] = i.to_s
67
+ end
68
+ (0...CACHE_LIMIT_SIZE).to_a.reverse_each do |i|
69
+ @cache[i] = i.to_s
70
+ end
71
+
72
+ old_key = CACHE_LIMIT_SIZE - 1
73
+ last_key = 0
74
+ new_key = CACHE_LIMIT_SIZE
75
+
76
+ @cache[new_key] = new_key.to_s
77
+ assert_equal(nil, @cache[old_key])
78
+ assert_equal(last_key.to_s, @cache[last_key])
79
+ assert_equal(new_key.to_s, @cache[new_key])
80
+ end
81
+ end
82
+
83
+ class SharedWorkCacheTest < Test::Unit::TestCase
84
+ include Higgs
85
+
86
+ # for ident(1)
87
+ CVS_ID = '$Id: test_cache.rb 559 2007-09-25 15:20:20Z toki $'
88
+
89
+ def setup
90
+ @calc_calls = 0
91
+ @cache = SharedWorkCache.new{|key| calc(key) }
92
+ end
93
+
94
+ def calc(n)
95
+ @calc_calls += 1
96
+ @s = 0 # @s's scope is over multi-threading
97
+ for i in 1..n
98
+ @s += i
99
+ end
100
+ @s
101
+ end
102
+
103
+ def test_calc
104
+ assert_equal(1, calc(1))
105
+ assert_equal(1, @calc_calls)
106
+
107
+ assert_equal(3, calc(2))
108
+ assert_equal(2, @calc_calls)
109
+
110
+ assert_equal(6, calc(3))
111
+ assert_equal(3, @calc_calls)
112
+
113
+ assert_equal(10, calc(4))
114
+ assert_equal(4, @calc_calls)
115
+
116
+ assert_equal(15, calc(5))
117
+ assert_equal(5, @calc_calls)
118
+ end
119
+
120
+ def test_fetch
121
+ 100.times do |i|
122
+ assert_equal(1, @cache[1], "loop(#{i})")
123
+ assert_equal(3, @cache[2], "loop(#{i})")
124
+ assert_equal(6, @cache[3], "loop(#{i})")
125
+ assert_equal(10, @cache[4], "loop(#{i})")
126
+ assert_equal(15, @cache[5], "loop(#{i})")
127
+ assert_equal(5, @calc_calls)
128
+ end
129
+ end
130
+
131
+ def test_delete
132
+ assert_equal(false, @cache.delete(5))
133
+ assert_equal(15, @cache[5])
134
+ assert_equal(1, @calc_calls)
135
+
136
+ assert_equal(true, @cache.delete(5))
137
+ assert_equal(15, @cache[5])
138
+ assert_equal(2, @calc_calls, 'reload')
139
+ end
140
+
141
+ NUM_OF_THREADS = 10
142
+ WORK_COUNT = 10000
143
+
144
+ def calc_race_condition
145
+ barrier = Higgs::Barrier.new(NUM_OF_THREADS + 1)
146
+ th_grp = ThreadGroup.new
147
+
148
+ result_list = [ nil ] * NUM_OF_THREADS
149
+ NUM_OF_THREADS.times{|i| # `i' should be local scope of thread block
150
+ th_grp.add Thread.new{
151
+ barrier.wait
152
+ result_list[i] = calc(WORK_COUNT)
153
+ }
154
+ }
155
+
156
+ barrier.wait
157
+ for t in th_grp.list
158
+ t.join
159
+ end
160
+
161
+ expected_value = calc(WORK_COUNT)
162
+ result_list.find{|v| v != expected_value }
163
+ end
164
+ private :calc_race_condition
165
+
166
+ def test_multi_thread_fetch
167
+ count = 0
168
+ timeout(10) {
169
+ begin
170
+ count += 1
171
+ end until (calc_race_condition)
172
+ }
173
+
174
+ @calc_calls = 0
175
+ expected_result = calc(WORK_COUNT)
176
+ assert_equal(1, @calc_calls)
177
+
178
+ count.times do |n|
179
+ barrier = Higgs::Barrier.new(NUM_OF_THREADS + 1)
180
+ th_grp = ThreadGroup.new
181
+ NUM_OF_THREADS.times{|i| # `i' should be local scope of thread block
182
+ th_grp.add Thread.new{
183
+ barrier.wait
184
+ assert_equal(expected_result, @cache[WORK_COUNT], "#{n}th: th#{i}")
185
+ }
186
+ }
187
+
188
+ barrier.wait
189
+ for t in th_grp.list
190
+ t.join
191
+ end
192
+ assert_equal(2, @calc_calls, "#{n}th")
193
+ end
194
+ end
195
+ end
196
+
197
+ class SharedWorkCacheNoWorkBlockTest < Test::Unit::TestCase
198
+ include Higgs
199
+
200
+ # for ident(1)
201
+ CVS_ID = '$Id: test_cache.rb 559 2007-09-25 15:20:20Z toki $'
202
+
203
+ def test_no_work_block
204
+ assert_raise(RuntimeError) {
205
+ SharedWorkCache.new
206
+ }
207
+ end
208
+ end
209
+ end
210
+
211
+ # Local Variables:
212
+ # mode: Ruby
213
+ # indent-tabs-mode: nil
214
+ # End: