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.
- data/ChangeLog +208 -0
- data/LICENSE +26 -0
- data/README +2 -0
- data/Rakefile +75 -0
- data/bin/higgs_backup +67 -0
- data/bin/higgs_dump_index +43 -0
- data/bin/higgs_dump_jlog +42 -0
- data/bin/higgs_verify +37 -0
- data/lib/cgi/session/higgs.rb +72 -0
- data/lib/higgs/block.rb +192 -0
- data/lib/higgs/cache.rb +117 -0
- data/lib/higgs/dbm.rb +55 -0
- data/lib/higgs/exceptions.rb +31 -0
- data/lib/higgs/flock.rb +77 -0
- data/lib/higgs/index.rb +164 -0
- data/lib/higgs/jlog.rb +159 -0
- data/lib/higgs/lock.rb +189 -0
- data/lib/higgs/storage.rb +1086 -0
- data/lib/higgs/store.rb +228 -0
- data/lib/higgs/tar.rb +390 -0
- data/lib/higgs/thread.rb +370 -0
- data/lib/higgs/tman.rb +513 -0
- data/lib/higgs/utils/bman.rb +285 -0
- data/lib/higgs/utils.rb +22 -0
- data/lib/higgs/version.rb +21 -0
- data/lib/higgs.rb +59 -0
- data/misc/cache_bench/cache_bench.rb +43 -0
- data/misc/dbm_bench/.strc +8 -0
- data/misc/dbm_bench/Rakefile +78 -0
- data/misc/dbm_bench/dbm_multi_thread.rb +199 -0
- data/misc/dbm_bench/dbm_rnd_delete.rb +43 -0
- data/misc/dbm_bench/dbm_rnd_read.rb +44 -0
- data/misc/dbm_bench/dbm_rnd_update.rb +44 -0
- data/misc/dbm_bench/dbm_seq_read.rb +45 -0
- data/misc/dbm_bench/dbm_seq_write.rb +44 -0
- data/misc/dbm_bench/st_verify.rb +28 -0
- data/misc/io_bench/cksum_bench.rb +48 -0
- data/misc/io_bench/jlog_bench.rb +71 -0
- data/misc/io_bench/write_bench.rb +128 -0
- data/misc/thread_bench/lock_bench.rb +132 -0
- data/mkrdoc.rb +8 -0
- data/rdoc.yml +13 -0
- data/sample/count.rb +60 -0
- data/sample/dbmtest.rb +38 -0
- data/test/Rakefile +45 -0
- data/test/run.rb +32 -0
- data/test/test_block.rb +163 -0
- data/test/test_cache.rb +214 -0
- data/test/test_cgi_session.rb +142 -0
- data/test/test_flock.rb +162 -0
- data/test/test_index.rb +258 -0
- data/test/test_jlog.rb +180 -0
- data/test/test_lock.rb +320 -0
- data/test/test_online_backup.rb +169 -0
- data/test/test_storage.rb +439 -0
- data/test/test_storage_conf.rb +202 -0
- data/test/test_storage_init_opts.rb +89 -0
- data/test/test_store.rb +211 -0
- data/test/test_tar.rb +432 -0
- data/test/test_thread.rb +541 -0
- data/test/test_tman.rb +875 -0
- data/test/test_tman_init_opts.rb +56 -0
- data/test/test_utils_bman.rb +234 -0
- 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
data/rdoc.yml
ADDED
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:
|
data/test/test_block.rb
ADDED
@@ -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:
|
data/test/test_cache.rb
ADDED
@@ -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:
|