fsdb 0.5 → 0.6.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 (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