fsdb 0.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (176) hide show
  1. data/{RELEASE-NOTES → History.txt} +6 -0
  2. data/{README → README.txt} +26 -17
  3. data/examples/flat.rb +146 -0
  4. data/examples/fsdb-example.rb +28 -0
  5. data/examples/rbformat.rb +17 -0
  6. data/examples/yaml2.rb +29 -0
  7. data/junk/OLDRakefile +98 -0
  8. data/junk/OLDRakefile2 +55 -0
  9. data/junk/check-cache.rb +18 -0
  10. data/junk/create-lock.rb +25 -0
  11. data/junk/doc/old-api/classes/FSDB.html +139 -0
  12. data/junk/doc/old-api/classes/FSDB/Database.html +953 -0
  13. data/junk/doc/old-api/classes/FSDB/Database.src/M000029.html +16 -0
  14. data/junk/doc/old-api/classes/FSDB/Database.src/M000030.html +16 -0
  15. data/junk/doc/old-api/classes/FSDB/Database.src/M000031.html +16 -0
  16. data/junk/doc/old-api/classes/FSDB/Database.src/M000032.html +16 -0
  17. data/junk/doc/old-api/classes/FSDB/Database.src/M000033.html +33 -0
  18. data/junk/doc/old-api/classes/FSDB/Database.src/M000034.html +18 -0
  19. data/junk/doc/old-api/classes/FSDB/Database.src/M000035.html +22 -0
  20. data/junk/doc/old-api/classes/FSDB/Database.src/M000036.html +16 -0
  21. data/junk/doc/old-api/classes/FSDB/Database.src/M000037.html +22 -0
  22. data/junk/doc/old-api/classes/FSDB/Database.src/M000038.html +43 -0
  23. data/junk/doc/old-api/classes/FSDB/Database.src/M000039.html +25 -0
  24. data/junk/doc/old-api/classes/FSDB/Database.src/M000040.html +43 -0
  25. data/junk/doc/old-api/classes/FSDB/Database.src/M000041.html +23 -0
  26. data/junk/doc/old-api/classes/FSDB/Database.src/M000042.html +22 -0
  27. data/junk/doc/old-api/classes/FSDB/Database.src/M000043.html +16 -0
  28. data/junk/doc/old-api/classes/FSDB/Database.src/M000044.html +16 -0
  29. data/junk/doc/old-api/classes/FSDB/Database.src/M000045.html +18 -0
  30. data/junk/doc/old-api/classes/FSDB/Database.src/M000046.html +18 -0
  31. data/junk/doc/old-api/classes/FSDB/Database.src/M000047.html +18 -0
  32. data/junk/doc/old-api/classes/FSDB/Database.src/M000048.html +16 -0
  33. data/junk/doc/old-api/classes/FSDB/Database.src/M000049.html +71 -0
  34. data/junk/doc/old-api/classes/FSDB/Database.src/M000050.html +43 -0
  35. data/junk/doc/old-api/classes/FSDB/Database.src/M000051.html +53 -0
  36. data/junk/doc/old-api/classes/FSDB/Database.src/M000052.html +44 -0
  37. data/junk/doc/old-api/classes/FSDB/Database.src/M000053.html +39 -0
  38. data/junk/doc/old-api/classes/FSDB/Database.src/M000054.html +72 -0
  39. data/junk/doc/old-api/classes/FSDB/Database.src/M000055.html +39 -0
  40. data/junk/doc/old-api/classes/FSDB/Database.src/M000056.html +18 -0
  41. data/junk/doc/old-api/classes/FSDB/Database.src/M000057.html +18 -0
  42. data/junk/doc/old-api/classes/FSDB/Database.src/M000058.html +18 -0
  43. data/junk/doc/old-api/classes/FSDB/Database.src/M000059.html +18 -0
  44. data/junk/doc/old-api/classes/FSDB/Database.src/M000060.html +18 -0
  45. data/junk/doc/old-api/classes/FSDB/Database.src/M000061.html +23 -0
  46. data/junk/doc/old-api/classes/FSDB/Database.src/M000062.html +23 -0
  47. data/junk/doc/old-api/classes/FSDB/Database.src/M000063.html +18 -0
  48. data/junk/doc/old-api/classes/FSDB/Database.src/M000064.html +18 -0
  49. data/junk/doc/old-api/classes/FSDB/Database/AbortedTransaction.html +118 -0
  50. data/junk/doc/old-api/classes/FSDB/Database/CreateFileError.html +120 -0
  51. data/junk/doc/old-api/classes/FSDB/Database/DirIsImmutableError.html +117 -0
  52. data/junk/doc/old-api/classes/FSDB/Database/DirNotEmptyError.html +117 -0
  53. data/junk/doc/old-api/classes/FSDB/Database/FormatError.html +117 -0
  54. data/junk/doc/old-api/classes/FSDB/Database/MissingFileError.html +117 -0
  55. data/junk/doc/old-api/classes/FSDB/Database/MissingObjectError.html +117 -0
  56. data/junk/doc/old-api/classes/FSDB/Database/NotDirError.html +118 -0
  57. data/junk/doc/old-api/classes/FSDB/Database/PathComponentError.html +120 -0
  58. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.html +148 -0
  59. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000005.html +21 -0
  60. data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000007.html +21 -0
  61. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.html +210 -0
  62. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000006.html +22 -0
  63. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000007.html +22 -0
  64. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000008.html +22 -0
  65. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000009.html +22 -0
  66. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000010.html +22 -0
  67. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000011.html +22 -0
  68. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000012.html +22 -0
  69. data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000013.html +22 -0
  70. data/junk/doc/old-api/classes/FSDB/ForkSafely.html +126 -0
  71. data/junk/doc/old-api/classes/FSDB/Modex.html +237 -0
  72. data/junk/doc/old-api/classes/FSDB/Modex.src/M000024.html +21 -0
  73. data/junk/doc/old-api/classes/FSDB/Modex.src/M000025.html +30 -0
  74. data/junk/doc/old-api/classes/FSDB/Modex.src/M000026.html +21 -0
  75. data/junk/doc/old-api/classes/FSDB/Modex.src/M000027.html +30 -0
  76. data/junk/doc/old-api/classes/FSDB/Modex.src/M000028.html +44 -0
  77. data/junk/doc/old-api/classes/FSDB/Modex.src/M000029.html +26 -0
  78. data/junk/doc/old-api/classes/FSDB/Modex.src/M000030.html +48 -0
  79. data/junk/doc/old-api/classes/FSDB/Modex/ForkSafely.html +105 -0
  80. data/junk/doc/old-api/classes/FSDB/Mutex.html +244 -0
  81. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000018.html +19 -0
  82. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000019.html +18 -0
  83. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000020.html +19 -0
  84. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000021.html +18 -0
  85. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000022.html +23 -0
  86. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000023.html +30 -0
  87. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000024.html +26 -0
  88. data/junk/doc/old-api/classes/FSDB/Mutex.src/M000025.html +21 -0
  89. data/junk/doc/old-api/classes/FSDB/Mutex/ForkSafely.html +105 -0
  90. data/junk/doc/old-api/classes/FSDB/PathUtilities.html +257 -0
  91. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000012.html +23 -0
  92. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000013.html +18 -0
  93. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000014.html +23 -0
  94. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000015.html +18 -0
  95. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000016.html +18 -0
  96. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000017.html +22 -0
  97. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000018.html +23 -0
  98. data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000019.html +18 -0
  99. data/junk/doc/old-api/classes/FSDB/PathUtilities/InvalidPathError.html +111 -0
  100. data/junk/doc/old-api/classes/File.html +272 -0
  101. data/junk/doc/old-api/classes/File.src/M000001.html +17 -0
  102. data/junk/doc/old-api/classes/File.src/M000002.html +17 -0
  103. data/junk/doc/old-api/classes/File.src/M000003.html +20 -0
  104. data/junk/doc/old-api/classes/File.src/M000004.html +20 -0
  105. data/junk/doc/old-api/classes/File.src/M000005.html +32 -0
  106. data/junk/doc/old-api/classes/File.src/M000006.html +32 -0
  107. data/junk/doc/old-api/created.rid +1 -0
  108. data/junk/doc/old-api/files/README.html +112 -0
  109. data/junk/doc/old-api/files/RELEASE-NOTES.html +233 -0
  110. data/junk/doc/old-api/files/fsdb_txt.html +888 -0
  111. data/junk/doc/old-api/files/lib/fsdb/database_rb.html +115 -0
  112. data/junk/doc/old-api/files/lib/fsdb/file-lock_rb.html +109 -0
  113. data/junk/doc/old-api/files/lib/fsdb/modex_rb.html +121 -0
  114. data/junk/doc/old-api/files/lib/fsdb/mutex_rb.html +108 -0
  115. data/junk/doc/old-api/files/lib/fsdb/util_rb.html +108 -0
  116. data/junk/doc/old-api/fr_class_index.html +47 -0
  117. data/junk/doc/old-api/fr_file_index.html +34 -0
  118. data/junk/doc/old-api/fr_method_index.html +90 -0
  119. data/junk/doc/old-api/index.html +24 -0
  120. data/junk/doc/old-api/rdoc-style.css +208 -0
  121. data/junk/file-lock-nb.rb +15 -0
  122. data/junk/fl.rb +144 -0
  123. data/junk/flock-test.rb +39 -0
  124. data/junk/fsdb.kateproject +47 -0
  125. data/junk/fsdb.prj +196 -0
  126. data/junk/fsdb.sf +46 -0
  127. data/junk/insert-dir.rb +48 -0
  128. data/junk/lock-test-bug.rb +150 -0
  129. data/junk/lock-test-too-simple.rb +136 -0
  130. data/junk/lock-test.rb +151 -0
  131. data/{script → junk}/mkrdoc +0 -0
  132. data/junk/restore-fsdb.rb +37 -0
  133. data/junk/rf.txt +5 -0
  134. data/junk/solaris-bug-fixed.rb +184 -0
  135. data/junk/solaris-bug.rb +182 -0
  136. data/junk/solaris-bug.txt +43 -0
  137. data/junk/sync.rb +327 -0
  138. data/junk/test-file-lock.html +86 -0
  139. data/junk/test-file-lock.rb +84 -0
  140. data/junk/test-processes.rb +131 -0
  141. data/junk/test-threads.rb +113 -0
  142. data/junk/wiki-mutex.rb +108 -0
  143. data/lib/fsdb/database.rb +5 -3
  144. data/lib/fsdb/delegatable.rb +21 -0
  145. data/lib/fsdb/faster-modex.rb +223 -0
  146. data/lib/fsdb/faster-mutex.rb +138 -0
  147. data/lib/fsdb/mutex.rb +4 -1
  148. data/lib/fsdb/persistent.rb +91 -0
  149. data/lib/fsdb/read-write-object.rb +36 -0
  150. data/lib/fsdb/server.rb +44 -0
  151. data/misc/fsdb-blorubu.txt +47 -0
  152. data/misc/mtime-and-file-id.txt +23 -0
  153. data/misc/posixlock.txt +148 -0
  154. data/rakefile +39 -0
  155. data/tasks/ann.rake +80 -0
  156. data/tasks/bones.rake +20 -0
  157. data/tasks/gem.rake +201 -0
  158. data/tasks/git.rake +40 -0
  159. data/tasks/notes.rake +27 -0
  160. data/tasks/post_load.rake +34 -0
  161. data/tasks/rdoc.rake +51 -0
  162. data/tasks/rubyforge.rake +55 -0
  163. data/tasks/setup.rb +292 -0
  164. data/tasks/spec.rake +54 -0
  165. data/tasks/svn.rake +47 -0
  166. data/tasks/test.rake +40 -0
  167. data/tasks/zentest.rake +36 -0
  168. data/test/err.txt +31 -0
  169. data/test/runs.rb +8 -0
  170. data/test/test-file-lock.rb +78 -0
  171. data/test/test-util.rb +1 -0
  172. data/test/trap.rb +31 -0
  173. metadata +198 -35
  174. data/Manifest +0 -36
  175. data/Rakefile +0 -10
  176. data/fsdb.gemspec +0 -113
@@ -0,0 +1,48 @@
1
+ def insert(path, object)
2
+ abs_path = absolute(path)
3
+ file_id = make_file_id(abs_path)
4
+ object_exclusive file_id do |cache_entry|
5
+ open_write_lock(path) do |f|
6
+ dump(object, f)
7
+ cache_entry.update(f.mtime, inc_version_of(f, cache_entry), object)
8
+ object
9
+ end
10
+ end
11
+ rescue DirIsImmutableError
12
+ begin
13
+ ary = object.to_a
14
+ # when default to_a becomes obsolete, this will be reported as
15
+ # an error, which is ok--it will be caught and reported below.
16
+ raise unless ary.all?{|x|x.size == 2}
17
+ rescue StandardError
18
+ raise DirIsImmutableError,
19
+ "Cannot insert given object at dir #{path} in #{inspect}"
20
+ end
21
+ ary.each do |k,v|
22
+ insert(File.join(path, k), v)
23
+ end
24
+
25
+
26
+ def test_insert_dir
27
+ @db['insert_dir/'] = {
28
+ 'path1' => 1,
29
+ 'path2' => 2
30
+ }
31
+ assert_equal(1, @db['insert_dir/path1'])
32
+ assert_equal(2, @db['insert_dir/path2'])
33
+ end
34
+
35
+
36
+ __END__
37
+
38
+ - FSDB::Database#insert now allows
39
+
40
+ db['dir/'] = {
41
+ 'path1' => obj1,
42
+ 'path2' => obj2
43
+ }
44
+
45
+ as a shorthand for
46
+
47
+ db['dir/path1'] = obj1
48
+ db['dir/path2'] = obj2
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thread'
4
+ require 'sync'
5
+
6
+ @path = '/tmp/foo'
7
+
8
+ @cache_mutex = Mutex.new
9
+
10
+ def make_entry
11
+ @cache_mutex.synchronize do
12
+ @cache = Mutex.new
13
+ @sync = Synchronizer.new
14
+ @object = nil
15
+ end
16
+ end
17
+
18
+ # This is just for housekeeping, so that stale entries don't result
19
+ # in unused, but uncollectable, CacheEntry objects.
20
+ def clear_entry
21
+ @cache_mutex.synchronize do
22
+ @cache = nil
23
+ @sync = nil
24
+ @object = nil
25
+ end
26
+ end
27
+
28
+ def object(f)
29
+ @cache.synchronize do
30
+ @object ||= f.read
31
+ end
32
+ end
33
+
34
+ def sync_object_shared
35
+ @sync.synchronize(Synchronizer::SH) {yield}
36
+ end
37
+
38
+ def sync_object_exclusive
39
+ @sync.synchronize(Synchronizer::EX) {yield}
40
+ end
41
+
42
+ def browse
43
+ make_entry
44
+ sync_object_shared do
45
+ open_read_lock(@path) do |f|
46
+ yield object(f)
47
+ end
48
+ end
49
+ end
50
+
51
+ def change
52
+ make_entry
53
+ sync_object_exclusive do
54
+ open_write_lock(@path) do |f|
55
+ object = object(f)
56
+ yield object
57
+ f.write(object)
58
+ end
59
+ end
60
+ end
61
+
62
+ def open_read_lock(path)
63
+ File.open(path, "r") do |f|
64
+ begin
65
+ f.flock(File::LOCK_SH)
66
+ yield f
67
+ ensure
68
+ f.flock(File::LOCK_UN)
69
+ end
70
+ end
71
+ end
72
+
73
+ def open_write_lock(path)
74
+ File.open(path, "r+") do |f|
75
+ f.rewind
76
+ begin
77
+ f.flock(File::LOCK_EX) ## need to use fcntl for Linux NFS
78
+ yield f
79
+ ensure
80
+ f.flush ## is this necessary? [ruby-talk:16721]
81
+ f.flock(File::LOCK_UN)
82
+ end
83
+ end
84
+ end
85
+
86
+ def do_test
87
+ $stdout.sync = true
88
+ Thread.abort_on_exception = true
89
+
90
+ srand(3765)
91
+
92
+ thread_count = 2
93
+ rep_count = 100
94
+ max_sleep = 0.01
95
+
96
+ File.open(@path, "w") do |f|
97
+ f.write("0")
98
+ end
99
+
100
+ threads = (0..thread_count).map do |thread_index|
101
+ Thread.new(thread_index) do |ti|
102
+ Thread.current[:number] = ti
103
+ rep_count.times do |iter|
104
+ Thread.current[:iter] = iter
105
+
106
+ case rand(3)
107
+
108
+ when 0
109
+ browse do |tester|
110
+ old_tester = tester
111
+ sleep(rand*max_sleep)
112
+ unless old_tester == tester
113
+ fail "browse: #{old_tester} != #{tester}"
114
+ end
115
+ end
116
+
117
+ when 1
118
+ change do |tester|
119
+ tester.replace (tester.to_i + 1).to_s
120
+ old_tester = tester
121
+ sleep(rand*max_sleep)
122
+ unless old_tester == tester
123
+ fail "browse: #{old_tester} != #{tester}"
124
+ end
125
+ end
126
+
127
+ else
128
+ sleep(rand*max_sleep)
129
+ if rand(20) == 0
130
+ clear_entry
131
+ end
132
+
133
+ end
134
+
135
+ end
136
+ end
137
+ end
138
+
139
+ sleep(rep_count * max_sleep * 2)
140
+ if threads.any? {|t|t.alive?}
141
+ $stderr.puts "Deadlock!"
142
+ exit!
143
+ end
144
+
145
+ threads.each do |thread|
146
+ thread.join
147
+ end
148
+ end
149
+
150
+ do_test
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thread'
4
+ require 'sync'
5
+ #require 'ftools'
6
+
7
+ @path = '/tmp/foo'
8
+
9
+ @cache_mutex = Mutex.new
10
+ @sync = Synchronizer.new
11
+ @object = nil
12
+
13
+ def object(f)
14
+ @cache_mutex.synchronize do
15
+ @object ||= f.read
16
+ end
17
+ end
18
+
19
+ def clear_cache
20
+ @object = nil
21
+ end
22
+
23
+ def sync_object_shared
24
+ @sync.synchronize(Synchronizer::SH) {yield}
25
+ end
26
+ def sync_object_exclusive
27
+ @sync.synchronize(Synchronizer::EX) {yield}
28
+ end
29
+
30
+ def browse
31
+ sync_object_shared do
32
+ open_read_lock(@path) do |f|
33
+ yield object(f)
34
+ end
35
+ end
36
+ end
37
+
38
+ def change
39
+ sync_object_exclusive do
40
+ open_write_lock(@path) do |f|
41
+ object = object(f)
42
+ yield object
43
+ f.write(object)
44
+ end
45
+ end
46
+ end
47
+
48
+ def open_read_lock(path)
49
+ File.open(path, "r") do |f|
50
+ begin
51
+ f.flock(File::LOCK_SH)
52
+ yield f
53
+ ensure
54
+ f.flock(File::LOCK_UN)
55
+ end
56
+ end
57
+ end
58
+
59
+ def open_write_lock(path)
60
+ File.open(path, "r+") do |f|
61
+ f.rewind
62
+ begin
63
+ f.flock(File::LOCK_EX) ## need to use fcntl for Linux NFS
64
+ yield f
65
+ ensure
66
+ f.flush ## is this necessary? [ruby-talk:16721]
67
+ f.flock(File::LOCK_UN)
68
+ end
69
+ end
70
+ end
71
+
72
+ def do_test
73
+ $stdout.sync = true
74
+ Thread.abort_on_exception = true
75
+
76
+ srand(3765)
77
+
78
+ thread_count = 2
79
+ rep_count = 100
80
+ max_sleep = 0.01
81
+
82
+ File.open(@path, "w") do |f|
83
+ f.write "0"
84
+ end
85
+
86
+ threads = (0..thread_count).map do |thread_index|
87
+ Thread.new(thread_index) do |ti|
88
+ Thread.current[:number] = ti
89
+ rep_count.times do |iter|
90
+ Thread.current[:iter] = iter
91
+
92
+ case rand(3)
93
+
94
+ when 0
95
+ browse do |tester|
96
+ old_tester = tester
97
+ sleep(rand*max_sleep)
98
+ unless old_tester == tester
99
+ fail "browse: #{old_tester} != #{tester}"
100
+ end
101
+ end
102
+
103
+ when 1
104
+ change do |tester|
105
+ tester.replace (tester.to_i + 1).to_s
106
+ old_tester = tester
107
+ sleep(rand*max_sleep)
108
+ unless old_tester == tester
109
+ fail "browse: #{old_tester} != #{tester}"
110
+ end
111
+ end
112
+
113
+ else
114
+ sleep(rand*max_sleep)
115
+ if rand(20) == 0
116
+ clear_cache
117
+ end
118
+
119
+ end
120
+
121
+ end
122
+ end
123
+ end
124
+
125
+ sleep(rep_count * max_sleep * 2)
126
+ if threads.any? {|t|t.alive?}
127
+ $stderr.puts "Deadlock!"
128
+ exit!
129
+ end
130
+
131
+ threads.each do |thread|
132
+ thread.join
133
+ end
134
+ end
135
+
136
+ do_test
data/junk/lock-test.rb ADDED
@@ -0,0 +1,151 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thread'
4
+ require 'sync'
5
+
6
+ @path = '/tmp/foo'
7
+
8
+ @cache_mutex = Mutex.new
9
+
10
+ def make_entry
11
+ @cache_mutex.synchronize do
12
+ unless @mutex
13
+ @mutex ||= Mutex.new
14
+ @sync ||= Synchronizer.new
15
+ @object = nil
16
+ end
17
+ end
18
+ end
19
+
20
+ # This is just for housekeeping, so that stale entries don't result
21
+ # in unused, but uncollectable, CacheEntry objects.
22
+ def clear_entry
23
+ @cache_mutex.synchronize do
24
+ @mutex = nil
25
+ @sync = nil
26
+ @object = nil
27
+ end
28
+ end
29
+
30
+ def object(f)
31
+ @mutex.synchronize do
32
+ @object ||= f.read
33
+ end
34
+ end
35
+
36
+ def sync_object_shared
37
+ @sync.synchronize(Synchronizer::SH) {yield}
38
+ end
39
+
40
+ def sync_object_exclusive
41
+ @sync.synchronize(Synchronizer::EX) {yield}
42
+ end
43
+
44
+ def browse
45
+ make_entry
46
+ sync_object_shared do
47
+ open_read_lock(@path) do |f|
48
+ yield object(f)
49
+ end
50
+ end
51
+ end
52
+
53
+ def change
54
+ make_entry
55
+ sync_object_exclusive do
56
+ open_write_lock(@path) do |f|
57
+ object = object(f)
58
+ yield object
59
+ f.write(object)
60
+ end
61
+ end
62
+ end
63
+
64
+ def open_read_lock(path)
65
+ File.open(path, "r") do |f|
66
+ begin
67
+ f.flock(File::LOCK_SH)
68
+ yield f
69
+ ensure
70
+ f.flock(File::LOCK_UN)
71
+ end
72
+ end
73
+ end
74
+
75
+ def open_write_lock(path)
76
+ File.open(path, "r+") do |f|
77
+ begin
78
+ f.flock(File::LOCK_EX) ## need to use fcntl for Linux NFS
79
+ yield f
80
+ ensure
81
+ f.flush ## is this necessary? [ruby-talk:16721]
82
+ f.flock(File::LOCK_UN)
83
+ end
84
+ end
85
+ end
86
+
87
+ def do_test
88
+ $stdout.sync = true
89
+ Thread.abort_on_exception = true
90
+
91
+ srand(3765)
92
+
93
+ thread_count = 2
94
+ rep_count = 100
95
+ max_sleep = 0.01
96
+
97
+ File.open(@path, "w") do |f|
98
+ f.write("0")
99
+ end
100
+
101
+ threads = (0..thread_count).map do |thread_index|
102
+ Thread.new(thread_index) do |ti|
103
+ Thread.current[:number] = ti
104
+ rep_count.times do |iter|
105
+ Thread.current[:iter] = iter
106
+
107
+ case rand(3)
108
+
109
+ when 0
110
+ browse do |tester|
111
+ old_tester = tester
112
+ sleep(rand*max_sleep)
113
+ unless old_tester == tester
114
+ fail "browse: #{old_tester} != #{tester}"
115
+ end
116
+ end
117
+
118
+ when 1
119
+ change do |tester|
120
+ tester.replace (tester.to_i + 1).to_s
121
+ old_tester = tester
122
+ sleep(rand*max_sleep)
123
+ unless old_tester == tester
124
+ fail "browse: #{old_tester} != #{tester}"
125
+ end
126
+ end
127
+
128
+ else
129
+ sleep(rand*max_sleep)
130
+ if rand(20) == 0
131
+ clear_entry
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+ end
138
+ end
139
+
140
+ sleep(rep_count * max_sleep * 2)
141
+ if threads.any? {|t|t.alive?}
142
+ $stderr.puts "Deadlock!"
143
+ exit!
144
+ end
145
+
146
+ threads.each do |thread|
147
+ thread.join
148
+ end
149
+ end
150
+
151
+ do_test