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.
- data/{RELEASE-NOTES → History.txt} +6 -0
- data/{README → README.txt} +26 -17
- data/examples/flat.rb +146 -0
- data/examples/fsdb-example.rb +28 -0
- data/examples/rbformat.rb +17 -0
- data/examples/yaml2.rb +29 -0
- data/junk/OLDRakefile +98 -0
- data/junk/OLDRakefile2 +55 -0
- data/junk/check-cache.rb +18 -0
- data/junk/create-lock.rb +25 -0
- data/junk/doc/old-api/classes/FSDB.html +139 -0
- data/junk/doc/old-api/classes/FSDB/Database.html +953 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000029.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000030.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000031.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000032.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000033.html +33 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000034.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000035.html +22 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000036.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000037.html +22 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000038.html +43 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000039.html +25 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000040.html +43 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000041.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000042.html +22 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000043.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000044.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000045.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000046.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000047.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000048.html +16 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000049.html +71 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000050.html +43 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000051.html +53 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000052.html +44 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000053.html +39 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000054.html +72 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000055.html +39 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000056.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000057.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000058.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000059.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000060.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000061.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000062.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000063.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database.src/M000064.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Database/AbortedTransaction.html +118 -0
- data/junk/doc/old-api/classes/FSDB/Database/CreateFileError.html +120 -0
- data/junk/doc/old-api/classes/FSDB/Database/DirIsImmutableError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/DirNotEmptyError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/FormatError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/MissingFileError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/MissingObjectError.html +117 -0
- data/junk/doc/old-api/classes/FSDB/Database/NotDirError.html +118 -0
- data/junk/doc/old-api/classes/FSDB/Database/PathComponentError.html +120 -0
- data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.html +148 -0
- data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000005.html +21 -0
- data/junk/doc/old-api/classes/FSDB/DatabaseDebuggable.src/M000007.html +21 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.html +210 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000006.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000007.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000008.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000009.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000010.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000011.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000012.html +22 -0
- data/junk/doc/old-api/classes/FSDB/DirectoryIterators.src/M000013.html +22 -0
- data/junk/doc/old-api/classes/FSDB/ForkSafely.html +126 -0
- data/junk/doc/old-api/classes/FSDB/Modex.html +237 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000024.html +21 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000025.html +30 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000026.html +21 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000027.html +30 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000028.html +44 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000029.html +26 -0
- data/junk/doc/old-api/classes/FSDB/Modex.src/M000030.html +48 -0
- data/junk/doc/old-api/classes/FSDB/Modex/ForkSafely.html +105 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.html +244 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000018.html +19 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000019.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000020.html +19 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000021.html +18 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000022.html +23 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000023.html +30 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000024.html +26 -0
- data/junk/doc/old-api/classes/FSDB/Mutex.src/M000025.html +21 -0
- data/junk/doc/old-api/classes/FSDB/Mutex/ForkSafely.html +105 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.html +257 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000012.html +23 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000013.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000014.html +23 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000015.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000016.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000017.html +22 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000018.html +23 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities.src/M000019.html +18 -0
- data/junk/doc/old-api/classes/FSDB/PathUtilities/InvalidPathError.html +111 -0
- data/junk/doc/old-api/classes/File.html +272 -0
- data/junk/doc/old-api/classes/File.src/M000001.html +17 -0
- data/junk/doc/old-api/classes/File.src/M000002.html +17 -0
- data/junk/doc/old-api/classes/File.src/M000003.html +20 -0
- data/junk/doc/old-api/classes/File.src/M000004.html +20 -0
- data/junk/doc/old-api/classes/File.src/M000005.html +32 -0
- data/junk/doc/old-api/classes/File.src/M000006.html +32 -0
- data/junk/doc/old-api/created.rid +1 -0
- data/junk/doc/old-api/files/README.html +112 -0
- data/junk/doc/old-api/files/RELEASE-NOTES.html +233 -0
- data/junk/doc/old-api/files/fsdb_txt.html +888 -0
- data/junk/doc/old-api/files/lib/fsdb/database_rb.html +115 -0
- data/junk/doc/old-api/files/lib/fsdb/file-lock_rb.html +109 -0
- data/junk/doc/old-api/files/lib/fsdb/modex_rb.html +121 -0
- data/junk/doc/old-api/files/lib/fsdb/mutex_rb.html +108 -0
- data/junk/doc/old-api/files/lib/fsdb/util_rb.html +108 -0
- data/junk/doc/old-api/fr_class_index.html +47 -0
- data/junk/doc/old-api/fr_file_index.html +34 -0
- data/junk/doc/old-api/fr_method_index.html +90 -0
- data/junk/doc/old-api/index.html +24 -0
- data/junk/doc/old-api/rdoc-style.css +208 -0
- data/junk/file-lock-nb.rb +15 -0
- data/junk/fl.rb +144 -0
- data/junk/flock-test.rb +39 -0
- data/junk/fsdb.kateproject +47 -0
- data/junk/fsdb.prj +196 -0
- data/junk/fsdb.sf +46 -0
- data/junk/insert-dir.rb +48 -0
- data/junk/lock-test-bug.rb +150 -0
- data/junk/lock-test-too-simple.rb +136 -0
- data/junk/lock-test.rb +151 -0
- data/{script → junk}/mkrdoc +0 -0
- data/junk/restore-fsdb.rb +37 -0
- data/junk/rf.txt +5 -0
- data/junk/solaris-bug-fixed.rb +184 -0
- data/junk/solaris-bug.rb +182 -0
- data/junk/solaris-bug.txt +43 -0
- data/junk/sync.rb +327 -0
- data/junk/test-file-lock.html +86 -0
- data/junk/test-file-lock.rb +84 -0
- data/junk/test-processes.rb +131 -0
- data/junk/test-threads.rb +113 -0
- data/junk/wiki-mutex.rb +108 -0
- data/lib/fsdb/database.rb +5 -3
- data/lib/fsdb/delegatable.rb +21 -0
- data/lib/fsdb/faster-modex.rb +223 -0
- data/lib/fsdb/faster-mutex.rb +138 -0
- data/lib/fsdb/mutex.rb +4 -1
- data/lib/fsdb/persistent.rb +91 -0
- data/lib/fsdb/read-write-object.rb +36 -0
- data/lib/fsdb/server.rb +44 -0
- data/misc/fsdb-blorubu.txt +47 -0
- data/misc/mtime-and-file-id.txt +23 -0
- data/misc/posixlock.txt +148 -0
- data/rakefile +39 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/err.txt +31 -0
- data/test/runs.rb +8 -0
- data/test/test-file-lock.rb +78 -0
- data/test/test-util.rb +1 -0
- data/test/trap.rb +31 -0
- metadata +198 -35
- data/Manifest +0 -36
- data/Rakefile +0 -10
- data/fsdb.gemspec +0 -113
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
### only use this if you can ensure Thread.critical is not already set!
|
|
4
|
+
### it mught be worth implementing a granular atomic-add-based mutex in
|
|
5
|
+
### place of Thread.critical
|
|
6
|
+
|
|
7
|
+
# Make sure we use the fast definition, not the thread.rb one!
|
|
8
|
+
class Thread # :nodoc:
|
|
9
|
+
def self.exclusive
|
|
10
|
+
old = critical
|
|
11
|
+
self.critical = true
|
|
12
|
+
yield
|
|
13
|
+
ensure
|
|
14
|
+
self.critical = old
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
module FSDB
|
|
19
|
+
|
|
20
|
+
# Mutex class based on standard thread.rb Mutex, which has some problems:
|
|
21
|
+
#
|
|
22
|
+
# - waiters are not a strict queue (try_lock can jump the queue, after
|
|
23
|
+
# which the queue gets *rotated*). Race condition.
|
|
24
|
+
#
|
|
25
|
+
# - doesn't use Thread.exclusive in enough places
|
|
26
|
+
#
|
|
27
|
+
# - no way to make dead threads give up the mutex, which is crucial in a fork
|
|
28
|
+
#
|
|
29
|
+
# Note: neither this Mutex nor the one in thread.rb is nestable.
|
|
30
|
+
#
|
|
31
|
+
class Mutex
|
|
32
|
+
def initialize
|
|
33
|
+
@waiting = []
|
|
34
|
+
@locked = nil
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def locked?
|
|
38
|
+
@locked
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def try_lock
|
|
42
|
+
Thread.critical = true
|
|
43
|
+
if not @locked
|
|
44
|
+
@locked = Thread.current
|
|
45
|
+
rslt = true
|
|
46
|
+
end
|
|
47
|
+
Thread.critical = false
|
|
48
|
+
rslt
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def lock
|
|
52
|
+
thread = Thread.current
|
|
53
|
+
Thread.critical = true
|
|
54
|
+
if @locked
|
|
55
|
+
@waiting.push thread
|
|
56
|
+
Thread.stop
|
|
57
|
+
unless @locked == thread
|
|
58
|
+
raise ThreadError, "queue was jumped"
|
|
59
|
+
end
|
|
60
|
+
else
|
|
61
|
+
@locked = thread
|
|
62
|
+
end
|
|
63
|
+
Thread.critical = false
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def unlock
|
|
68
|
+
return unless @locked
|
|
69
|
+
|
|
70
|
+
Thread.critical = true; t = wake_next_waiter; Thread.critical = false
|
|
71
|
+
|
|
72
|
+
begin
|
|
73
|
+
t.run if t
|
|
74
|
+
rescue ThreadError
|
|
75
|
+
end
|
|
76
|
+
self
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def synchronize
|
|
80
|
+
lock
|
|
81
|
+
yield
|
|
82
|
+
ensure
|
|
83
|
+
unlock
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def remove_dead # :nodoc:
|
|
87
|
+
Thread.critical = true
|
|
88
|
+
@waiting = @waiting.select {|t| t.alive?}
|
|
89
|
+
wake_next_waiter if @locked and not @locked.alive?
|
|
90
|
+
Thread.critical = false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
def wake_next_waiter
|
|
95
|
+
t = @waiting.shift
|
|
96
|
+
t.wakeup if t
|
|
97
|
+
@locked = t
|
|
98
|
+
rescue ThreadError
|
|
99
|
+
retry
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
module ForkSafely
|
|
103
|
+
def fork # :nodoc:
|
|
104
|
+
super do
|
|
105
|
+
ObjectSpace.each_object(Mutex) { |m| m.remove_dead }
|
|
106
|
+
yield
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# FSDB users who fork should include ForkSafely or FSDB itself. If you use
|
|
113
|
+
# mutexes (outside of those in FSDB), they should be FSDB::Mutexes.
|
|
114
|
+
module ForkSafely
|
|
115
|
+
include Mutex::ForkSafely
|
|
116
|
+
end
|
|
117
|
+
include ForkSafely
|
|
118
|
+
|
|
119
|
+
end # module FSDB
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
if __FILE__ == $0
|
|
123
|
+
# Stress test is in fsdb/test/test-mutex.rb. This is just to show fork usage.
|
|
124
|
+
|
|
125
|
+
include FSDB::ForkSafely
|
|
126
|
+
|
|
127
|
+
m = FSDB::Mutex.new
|
|
128
|
+
|
|
129
|
+
Thread.new { m.synchronize { sleep 1 } }
|
|
130
|
+
|
|
131
|
+
fork do
|
|
132
|
+
m.synchronize { puts "Didn't get here if you used standard mutex or fork." }
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
m.synchronize { puts "Got here." }
|
|
136
|
+
|
|
137
|
+
Process.wait
|
|
138
|
+
end
|
data/lib/fsdb/mutex.rb
CHANGED
|
@@ -29,6 +29,8 @@ class Mutex
|
|
|
29
29
|
def initialize
|
|
30
30
|
@waiting = []
|
|
31
31
|
@locked = nil
|
|
32
|
+
@waiting.taint # enable tainted comunication
|
|
33
|
+
self.taint
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
def locked?
|
|
@@ -37,7 +39,7 @@ class Mutex
|
|
|
37
39
|
|
|
38
40
|
def try_lock
|
|
39
41
|
Thread.exclusive do
|
|
40
|
-
if not @locked
|
|
42
|
+
if not @locked # and @waiting.empty? # not needed
|
|
41
43
|
@locked = Thread.current
|
|
42
44
|
true
|
|
43
45
|
end
|
|
@@ -83,6 +85,7 @@ class Mutex
|
|
|
83
85
|
Thread.exclusive do
|
|
84
86
|
@waiting = @waiting.select {|t| t.alive?}
|
|
85
87
|
wake_next_waiter if @locked and not @locked.alive?
|
|
88
|
+
## what if @locked thread left the resource in an inconsistent state?
|
|
86
89
|
end
|
|
87
90
|
end
|
|
88
91
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require 'thread'
|
|
4
|
+
require 'ftools'
|
|
5
|
+
require 'fsdb/file-lock'
|
|
6
|
+
|
|
7
|
+
# Mixin for an object that persists in a file by itself.
|
|
8
|
+
#
|
|
9
|
+
# References from the object to objects which need to persist separately
|
|
10
|
+
# should be through nonpersistent_attr_accessors. Otherwise, objects
|
|
11
|
+
# referred to in attrs will persist in the same file.
|
|
12
|
+
|
|
13
|
+
module Persistent
|
|
14
|
+
|
|
15
|
+
def persistent_mutex
|
|
16
|
+
@persistent_mutex ||
|
|
17
|
+
Thread.exclusive do
|
|
18
|
+
@persistent_mutex ||= Mutex.new # ||= to prevent race condition
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Save the persistent object (and all of its persistent references) to
|
|
23
|
+
# its file. If a block is given, call it (with self) in the context of
|
|
24
|
+
# locks that protect the file from other threads and processes. This can
|
|
25
|
+
# be used to atomically save related objects in separate files.
|
|
26
|
+
def save
|
|
27
|
+
persistent_mutex.synchronize do
|
|
28
|
+
File.makedirs(File.dirname(persistent_file))
|
|
29
|
+
File.open(persistent_file, "wb") do |f|
|
|
30
|
+
f.lock_exclusive do
|
|
31
|
+
dump(f)
|
|
32
|
+
yield self if block_given?
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def dump(*args)
|
|
39
|
+
Marshal.dump(self, *args)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def persistent_file
|
|
43
|
+
raise "#{self.class} must define the #persistent_file method to" +
|
|
44
|
+
" return the path of the file in which object persists."
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class << self
|
|
48
|
+
|
|
49
|
+
# Need to take care that only one thread in the process is restoring
|
|
50
|
+
# a particular object, or there will be multiple copies.
|
|
51
|
+
def restore file
|
|
52
|
+
object = File.open(file, "rb") do |f|
|
|
53
|
+
f.lock_shared do
|
|
54
|
+
load(f)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
object.restore file
|
|
58
|
+
object
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def load(*args)
|
|
62
|
+
Marshal.load(*args)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Called when the object is loaded, allowing the object to restore some
|
|
68
|
+
# state from the path at which it was saved.
|
|
69
|
+
def restore file; end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if __FILE__ == $0
|
|
74
|
+
|
|
75
|
+
class Foo
|
|
76
|
+
include Persistent
|
|
77
|
+
attr_accessor :x
|
|
78
|
+
attr_accessor :dir
|
|
79
|
+
def persistent_file; "/tmp/foo"; end
|
|
80
|
+
def restore file; @dir = File.dirname(file); end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
foo = Foo.new
|
|
84
|
+
foo.x = 1
|
|
85
|
+
foo.save
|
|
86
|
+
foo.x = 2
|
|
87
|
+
foo = Persistent.restore("/tmp/foo")
|
|
88
|
+
p foo.x
|
|
89
|
+
p foo.dir
|
|
90
|
+
|
|
91
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
|
|
3
|
+
# based on drb.rb
|
|
4
|
+
|
|
5
|
+
class IO
|
|
6
|
+
def write_object(obj)
|
|
7
|
+
str = Marshal.dump(obj)
|
|
8
|
+
packet = [str.size].pack('N') + str
|
|
9
|
+
write(packet)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# returns nil at eof
|
|
13
|
+
def read_object
|
|
14
|
+
sz = read(4) # sizeof (N)
|
|
15
|
+
return nil if sz.nil?
|
|
16
|
+
raise TypeError, "incomplete header, size == #{sz.size}" if sz.size < 4
|
|
17
|
+
sz = sz.unpack('N')[0]
|
|
18
|
+
str = read(sz)
|
|
19
|
+
raise TypeError, 'incomplete object' if str.nil? || str.size < sz
|
|
20
|
+
Marshal::load(str)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
if __FILE__ == $0
|
|
25
|
+
|
|
26
|
+
f = File.open('/tmp/foo', 'w')
|
|
27
|
+
|
|
28
|
+
f.write_object([1,2,3])
|
|
29
|
+
|
|
30
|
+
f.close
|
|
31
|
+
|
|
32
|
+
f = File.open('/tmp/foo', 'r')
|
|
33
|
+
|
|
34
|
+
p f.read_object
|
|
35
|
+
|
|
36
|
+
end
|
data/lib/fsdb/server.rb
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'drb'
|
|
2
|
+
require 'fsdb'
|
|
3
|
+
|
|
4
|
+
## can we use ssl?
|
|
5
|
+
|
|
6
|
+
module FSDB
|
|
7
|
+
class Server
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
attr_reader :name, :dir, :transactions
|
|
11
|
+
|
|
12
|
+
def initialize name = nil, dir = nil, db_class = nil, opts = {}
|
|
13
|
+
@name = name || "fsdb-server"
|
|
14
|
+
@dir = dir || name
|
|
15
|
+
@db = (db_class || Database).new(@dir, opts)
|
|
16
|
+
|
|
17
|
+
@listening = true
|
|
18
|
+
@transactions = 0
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# counts transactions running in the server process
|
|
22
|
+
def transaction
|
|
23
|
+
Thread.exclusive {@transactions += 1}
|
|
24
|
+
yield
|
|
25
|
+
ensure
|
|
26
|
+
Thread.exclusive {@transactions -= 1}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def enter_admin_runlevel
|
|
30
|
+
@listening = false
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def leave_admin_runlevel
|
|
34
|
+
@listening = true
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def check_listening
|
|
38
|
+
unless @listening or user.name == 'admin'
|
|
39
|
+
raise "Server not listening."
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
Like pstore, fsdb is:
|
|
2
|
+
|
|
3
|
+
- pure ruby, cross-platform
|
|
4
|
+
|
|
5
|
+
- ruby license
|
|
6
|
+
|
|
7
|
+
- simple API
|
|
8
|
+
|
|
9
|
+
- simple implementation (though less so)
|
|
10
|
+
|
|
11
|
+
- transaction based, with abort capability
|
|
12
|
+
|
|
13
|
+
However, pstore has some drawbacks:
|
|
14
|
+
|
|
15
|
+
- not thread-safe (though it is process-safe)
|
|
16
|
+
|
|
17
|
+
- coarse granularity (transaction applies to whole pstore), does not scale well
|
|
18
|
+
|
|
19
|
+
- uses marshalling, even if your data is is some other format that doesn't require the overhead of marshalling (e.g., strings, binary data)
|
|
20
|
+
|
|
21
|
+
Other advantages of fsdb:
|
|
22
|
+
|
|
23
|
+
- can use YAML (or other formats) where you want to (e.g., in user settings, but perhaps not for large ruby objects or binary data). It's easy to specify a mapping from path patterns to data formats.
|
|
24
|
+
|
|
25
|
+
- thread-safe, so you can easily wire it up to a multithreaded server, like drb (see examples/server.rb)
|
|
26
|
+
|
|
27
|
+
- objects in the database are just files, and so can be manipulated with standard command line tools (including rsync, versioning tools, etc.).
|
|
28
|
+
|
|
29
|
+
- granularity is up to you: you can have lots of little objects, if you want, (there are tradeoffs, of course)
|
|
30
|
+
|
|
31
|
+
- scales as well as the fs does
|
|
32
|
+
|
|
33
|
+
- thoroughly stress-tested, partially unit-tested. (But still not quite stable on Windows.)
|
|
34
|
+
|
|
35
|
+
- Can select fcntl-based locking (if platform supports it), which should allow safe use with NFS mounts on linux
|
|
36
|
+
|
|
37
|
+
Disadvantages of fsdb:
|
|
38
|
+
|
|
39
|
+
- If you want indexes, you need to implement them on top of fsdb. You could do:
|
|
40
|
+
|
|
41
|
+
db['name-index'] = RBTree.new
|
|
42
|
+
|
|
43
|
+
and define your own methods for adding/removing objects that also updates the name-index. The name-index maps names (or other keys) to paths in the db.
|
|
44
|
+
|
|
45
|
+
- Likewise, no SQL, out of the box.
|
|
46
|
+
|
|
47
|
+
- Efficiency and reliability depends on the fs. (My benchmarks get on the order of 1000 transactions per sec on a 3 yr old linux box.)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
mtime granularity on win?
|
|
2
|
+
|
|
3
|
+
see:
|
|
4
|
+
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/by_handle_file_information_str.asp
|
|
5
|
+
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/sysinfo/base/file_times.asp
|
|
6
|
+
|
|
7
|
+
http://www.google.com/search?hl=en&ie=ISO-8859-1&q=unique+file+id+on+windows&btnG=Google+Search
|
|
8
|
+
|
|
9
|
+
http://discuss.fogcreek.com/joelonsoftware/default.asp?cmd=show&ixPost=42340
|
|
10
|
+
|
|
11
|
+
Windows can give a unique file indentifier using the function GetFileInformationByHandle() (See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/fileio/base/getfileinformationbyhandle.asp)
|
|
12
|
+
|
|
13
|
+
Example: (hFile is a handle to a file)
|
|
14
|
+
|
|
15
|
+
BY_HANDLE_FILE_INFORMATION FileInfo;
|
|
16
|
+
|
|
17
|
+
GetFileInformationByHandle(hFile, &FileInfo);
|
|
18
|
+
inode= FileInfo.nFileIndexLow | (FileInfo.nFileIndexHigh << 32);
|
|
19
|
+
volume=FileInfo.dwVolumeSerialNumber;
|
|
20
|
+
|
|
21
|
+
According to Micorsoft : "The identifier (low and high parts) and the volume serial number uniquely identify a file on a single computer. To determine whether two open handles represent the same file, combine this identifier and the volume serial number for each file and compare them."
|
|
22
|
+
|
|
23
|
+
|
data/misc/posixlock.txt
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Subject:
|
|
2
|
+
[ANN] posixlock
|
|
3
|
+
From:
|
|
4
|
+
"Ara.T.Howard" <ahoward@ngdc.noaa.gov>
|
|
5
|
+
Date:
|
|
6
|
+
Sat, 6 Dec 2003 05:22:07 +0900
|
|
7
|
+
To:
|
|
8
|
+
ruby-talk@ruby-lang.org (ruby-talk ML)
|
|
9
|
+
Newsgroups:
|
|
10
|
+
comp.lang.ruby
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
SYNOPSIS:
|
|
14
|
+
posixlock
|
|
15
|
+
use fcntl (vs. flock) based locking for File#flock
|
|
16
|
+
|
|
17
|
+
USAGE
|
|
18
|
+
require 'posixlock' # replaces File#flock!
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
i currently do not have a place to post software - so here's a little
|
|
22
|
+
extension that replaces flock with an fcntl based impl for posix compliant
|
|
23
|
+
locking (safe on nfs) - posted to c.l.r for posterity:
|
|
24
|
+
|
|
25
|
+
extconf.rb
|
|
26
|
+
================
|
|
27
|
+
require 'mkmf'
|
|
28
|
+
create_makefile 'posixlock'
|
|
29
|
+
================
|
|
30
|
+
|
|
31
|
+
posixlock.c
|
|
32
|
+
================
|
|
33
|
+
#ifdef _WIN32
|
|
34
|
+
#include "missing/file.h"
|
|
35
|
+
#endif
|
|
36
|
+
|
|
37
|
+
#include "ruby.h"
|
|
38
|
+
#include "rubyio.h"
|
|
39
|
+
#include "rubysig.h"
|
|
40
|
+
|
|
41
|
+
#ifdef HAVE_UNISTD_H
|
|
42
|
+
#include <unistd.h>
|
|
43
|
+
#endif
|
|
44
|
+
|
|
45
|
+
#ifdef HAVE_FCNTL_H
|
|
46
|
+
#include <fcntl.h>
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
#include <errno.h>
|
|
50
|
+
|
|
51
|
+
extern VALUE rb_cFile;
|
|
52
|
+
|
|
53
|
+
# ifndef LOCK_SH
|
|
54
|
+
# define LOCK_SH 1
|
|
55
|
+
# endif
|
|
56
|
+
# ifndef LOCK_EX
|
|
57
|
+
# define LOCK_EX 2
|
|
58
|
+
# endif
|
|
59
|
+
# ifndef LOCK_NB
|
|
60
|
+
# define LOCK_NB 4
|
|
61
|
+
# endif
|
|
62
|
+
# ifndef LOCK_UN
|
|
63
|
+
# define LOCK_UN 8
|
|
64
|
+
# endif
|
|
65
|
+
|
|
66
|
+
static int
|
|
67
|
+
posixlock (fd, operation)
|
|
68
|
+
int fd;
|
|
69
|
+
int operation;
|
|
70
|
+
{
|
|
71
|
+
struct flock lock;
|
|
72
|
+
|
|
73
|
+
switch (operation & ~LOCK_NB)
|
|
74
|
+
{
|
|
75
|
+
case LOCK_SH:
|
|
76
|
+
lock.l_type = F_RDLCK;
|
|
77
|
+
break;
|
|
78
|
+
case LOCK_EX:
|
|
79
|
+
lock.l_type = F_WRLCK;
|
|
80
|
+
break;
|
|
81
|
+
case LOCK_UN:
|
|
82
|
+
lock.l_type = F_UNLCK;
|
|
83
|
+
break;
|
|
84
|
+
default:
|
|
85
|
+
errno = EINVAL;
|
|
86
|
+
return -1;
|
|
87
|
+
}
|
|
88
|
+
lock.l_whence = SEEK_SET;
|
|
89
|
+
lock.l_start = lock.l_len = 0L;
|
|
90
|
+
return fcntl (fd, (operation & LOCK_NB) ? F_SETLK : F_SETLKW, &lock);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
static VALUE
|
|
95
|
+
rb_file_posixlock (obj, operation)
|
|
96
|
+
VALUE obj;
|
|
97
|
+
VALUE operation;
|
|
98
|
+
{
|
|
99
|
+
#ifndef __CHECKER__
|
|
100
|
+
OpenFile *fptr;
|
|
101
|
+
int ret;
|
|
102
|
+
|
|
103
|
+
rb_secure (2);
|
|
104
|
+
GetOpenFile (obj, fptr);
|
|
105
|
+
|
|
106
|
+
if (fptr->mode & FMODE_WRITABLE)
|
|
107
|
+
{
|
|
108
|
+
fflush (GetWriteFile (fptr));
|
|
109
|
+
}
|
|
110
|
+
retry:
|
|
111
|
+
TRAP_BEG;
|
|
112
|
+
ret = posixlock (fileno (fptr->f), NUM2INT (operation));
|
|
113
|
+
TRAP_END;
|
|
114
|
+
if (ret < 0)
|
|
115
|
+
{
|
|
116
|
+
switch (errno)
|
|
117
|
+
{
|
|
118
|
+
case EAGAIN:
|
|
119
|
+
case EACCES:
|
|
120
|
+
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
|
|
121
|
+
case EWOULDBLOCK:
|
|
122
|
+
#endif
|
|
123
|
+
return Qfalse;
|
|
124
|
+
case EINTR:
|
|
125
|
+
#if defined(ERESTART)
|
|
126
|
+
case ERESTART:
|
|
127
|
+
#endif
|
|
128
|
+
goto retry;
|
|
129
|
+
}
|
|
130
|
+
rb_sys_fail (fptr->path);
|
|
131
|
+
}
|
|
132
|
+
#endif
|
|
133
|
+
|
|
134
|
+
return INT2FIX (0);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
void
|
|
139
|
+
Init_posixlock()
|
|
140
|
+
{
|
|
141
|
+
rb_define_method(rb_cFile, "flock", rb_file_posixlock, 1);
|
|
142
|
+
}
|
|
143
|
+
================
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
-a
|
|
148
|
+
-- ATTN: please update your address books with address below! =============================================================================== | EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov | PHONE :: 303.497.6469 | ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328 | STP :: http://www.ngdc.noaa.gov/stp/ | NGDC :: http://www.ngdc.noaa.gov/ | NESDIS :: http://www.nesdis.noaa.gov/ | NOAA :: http://www.noaa.gov/ | US DOC :: http://www.commerce.gov/ | | The difference between art and science is that science is what we | understand well enough to explain to a computer. | Art is everything else. | -- Donald Knuth, "Discover" | | /bin/sh -c 'for l in ruby perl;do $l -e "print \"\x3a\x2d\x29\x0a\"";done' ===============================================================================
|