virtfs 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +154 -0
- data/Rakefile +5 -0
- data/lib/virtfs-nativefs-thick.rb +1 -0
- data/lib/virtfs-nativefs-thin.rb +1 -0
- data/lib/virtfs.rb +38 -0
- data/lib/virtfs/activation.rb +97 -0
- data/lib/virtfs/block_io.rb +140 -0
- data/lib/virtfs/byte_range.rb +71 -0
- data/lib/virtfs/context.rb +300 -0
- data/lib/virtfs/context_manager.rb +175 -0
- data/lib/virtfs/context_switch_class_methods.rb +96 -0
- data/lib/virtfs/delegate_module.rb +40 -0
- data/lib/virtfs/dir_instance_delegate.rb +3 -0
- data/lib/virtfs/exception.rb +13 -0
- data/lib/virtfs/file_instance_delegate.rb +3 -0
- data/lib/virtfs/file_modes_and_options.rb +293 -0
- data/lib/virtfs/find_class_methods.rb +106 -0
- data/lib/virtfs/io_buffer.rb +133 -0
- data/lib/virtfs/io_instance_delegate.rb +3 -0
- data/lib/virtfs/kernel.rb +146 -0
- data/lib/virtfs/nativefs/thick.rb +30 -0
- data/lib/virtfs/nativefs/thick/dir_class_methods.rb +38 -0
- data/lib/virtfs/nativefs/thick/file_class_methods.rb +178 -0
- data/lib/virtfs/nativefs/thin.rb +32 -0
- data/lib/virtfs/nativefs/thin/dir.rb +30 -0
- data/lib/virtfs/nativefs/thin/dir_class_methods.rb +41 -0
- data/lib/virtfs/nativefs/thin/file.rb +112 -0
- data/lib/virtfs/nativefs/thin/file_class_methods.rb +181 -0
- data/lib/virtfs/protofs/protofs.rb +7 -0
- data/lib/virtfs/protofs/protofs_base.rb +12 -0
- data/lib/virtfs/protofs/protofs_dir.rb +13 -0
- data/lib/virtfs/protofs/protofs_dir_class.rb +31 -0
- data/lib/virtfs/protofs/protofs_file.rb +27 -0
- data/lib/virtfs/protofs/protofs_file_class.rb +136 -0
- data/lib/virtfs/stat.rb +100 -0
- data/lib/virtfs/thin_dir_delegator.rb +79 -0
- data/lib/virtfs/thin_file_delegator.rb +77 -0
- data/lib/virtfs/thin_io_delegator_methods.rb +301 -0
- data/lib/virtfs/thin_io_delegator_methods_bufferio.rb +337 -0
- data/lib/virtfs/v_dir.rb +238 -0
- data/lib/virtfs/v_file.rb +480 -0
- data/lib/virtfs/v_io.rb +243 -0
- data/lib/virtfs/v_pathname.rb +128 -0
- data/lib/virtfs/version.rb +3 -0
- data/spec/activate_spec.rb +202 -0
- data/spec/chroot_spec.rb +120 -0
- data/spec/context_manager_class_spec.rb +246 -0
- data/spec/context_manager_instance_spec.rb +255 -0
- data/spec/context_spec.rb +335 -0
- data/spec/data/UTF-16LE-data.txt +0 -0
- data/spec/data/UTF-8-data.txt +212 -0
- data/spec/dir_class_spec.rb +506 -0
- data/spec/dir_instance_spec.rb +208 -0
- data/spec/file_class_spec.rb +2106 -0
- data/spec/file_instance_spec.rb +154 -0
- data/spec/file_modes_and_options_spec.rb +1556 -0
- data/spec/find_spec.rb +142 -0
- data/spec/io_bufferio_size_shared_examples.rb +371 -0
- data/spec/io_bufferio_size_spec.rb +861 -0
- data/spec/io_bufferio_spec.rb +801 -0
- data/spec/io_class_spec.rb +145 -0
- data/spec/io_instance_spec.rb +516 -0
- data/spec/kernel_spec.rb +285 -0
- data/spec/mount_spec.rb +186 -0
- data/spec/nativefs_local_root_spec.rb +132 -0
- data/spec/path_spec.rb +39 -0
- data/spec/spec_helper.rb +126 -0
- data/tasks/rspec.rake +3 -0
- data/tasks/yard.rake +7 -0
- data/test/UTF-8-demo.txt +212 -0
- data/test/bench.rb +18 -0
- data/test/bio_internal_test.rb +45 -0
- data/test/delegate_io.rb +31 -0
- data/test/delegate_module.rb +62 -0
- data/test/encode_test.rb +42 -0
- data/test/enoent_test.rb +30 -0
- data/test/namespace_test.rb +42 -0
- data/test/read_block_valid_encoding.rb +44 -0
- data/test/read_test.rb +78 -0
- data/test/stream_readers.rb +46 -0
- data/test/utf-16-demo.txt +0 -0
- data/test/utf8_to_utf16.rb +77 -0
- data/test/wrapper_test.rb +34 -0
- data/virtfs.gemspec +29 -0
- metadata +230 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
module VirtFS
|
2
|
+
# ByteRange utility class, encapsulate a range of bytes as given
|
3
|
+
# by their first / last offsets
|
4
|
+
class ByteRange
|
5
|
+
attr_accessor :first, :last
|
6
|
+
|
7
|
+
def initialize(first = nil, last = nil)
|
8
|
+
set(first, last)
|
9
|
+
end
|
10
|
+
|
11
|
+
def empty?
|
12
|
+
@first.nil? || @last.nil?
|
13
|
+
end
|
14
|
+
|
15
|
+
def length
|
16
|
+
return 0 if empty?
|
17
|
+
@last - @first + 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def include?(obj)
|
21
|
+
return false if empty?
|
22
|
+
return (obj.first >= @first && obj.last <= @last) if obj.is_a?(self.class)
|
23
|
+
obj >= @first && obj <= @last
|
24
|
+
end
|
25
|
+
|
26
|
+
def adjacent?(*args)
|
27
|
+
return false if empty?
|
28
|
+
nrange = range_arg(args)
|
29
|
+
nrange.first == @last + 1 || nrange.last == @first - 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def overlap?(*args)
|
33
|
+
return false if empty?
|
34
|
+
nrange = range_arg(args)
|
35
|
+
include?(nrange.first) || include?(nrange.last) || nrange.include?(@first) || nrange.include?(@last)
|
36
|
+
end
|
37
|
+
|
38
|
+
def contiguous?(*args)
|
39
|
+
nrange = range_arg(args)
|
40
|
+
adjacent?(nrange) || overlap?(nrange)
|
41
|
+
end
|
42
|
+
|
43
|
+
def expand(*args)
|
44
|
+
nrange = range_arg(args)
|
45
|
+
@first = nrange.first if empty? || nrange.first < @first
|
46
|
+
@last = nrange.last if empty? || nrange.last > @last
|
47
|
+
end
|
48
|
+
|
49
|
+
def clear
|
50
|
+
set(nil, nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
def set(first, last)
|
54
|
+
@first = first
|
55
|
+
@last = last
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def range_arg(args)
|
61
|
+
case args.length
|
62
|
+
when 1
|
63
|
+
return args[0]
|
64
|
+
when 2
|
65
|
+
return self.class.new(args[0], args[1])
|
66
|
+
else
|
67
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for 1..2)"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
module VirtFS
|
2
|
+
# FS-specific state under which FS calls occur.
|
3
|
+
#
|
4
|
+
# VirtFS maps an independent context instance to each
|
5
|
+
# Ruby thread group, and internally switches to it before
|
6
|
+
# dispatching target FS calls from that thread.
|
7
|
+
# This class implements the core functionality behind the FS context
|
8
|
+
class Context
|
9
|
+
attr_reader :key
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@mount_points = []
|
13
|
+
@fs_lookup = {}
|
14
|
+
@mount_mutex = Mutex.new
|
15
|
+
@dir_mutex = Mutex.new
|
16
|
+
@saved_root = nil
|
17
|
+
@saved_cwd = nil
|
18
|
+
@root = VfsRealFile::SEPARATOR
|
19
|
+
@cwd = @root
|
20
|
+
@key = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# Set key used to uniquely identify the context
|
24
|
+
#
|
25
|
+
# @param val [String] context identifier
|
26
|
+
# @raise [RuntimeError] if key already assigned
|
27
|
+
def key=(val)
|
28
|
+
@dir_mutex.synchronize do
|
29
|
+
raise "Context already assigned to key: #{@key}" if !@key.nil? && !val.nil?
|
30
|
+
@key = val
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Mount the specified FS instance at the specified mount point.
|
35
|
+
# This registers specified fs to be accessed through the specified mount
|
36
|
+
# point via internal mechanisms. After this point any calls to this mount
|
37
|
+
# point through VirtFS under this context will be mapped through the specified
|
38
|
+
# fs instance
|
39
|
+
#
|
40
|
+
# @param fs_instance [VirtFS::FS] instance of VirtFS implementation corresponding
|
41
|
+
# to filesystem to mount
|
42
|
+
# @param mount_point [String] path which to mount filesystem under
|
43
|
+
#
|
44
|
+
# @raise [SystemCallError] if mount point cannot be resolved
|
45
|
+
# @raise [RuntimeError] if mount point is being used
|
46
|
+
def mount(fs_instance, mount_point)
|
47
|
+
mp_display = mount_point
|
48
|
+
|
49
|
+
raise "mount: invalid filesystem object #{fs_instance.class.name}" unless fs_instance.respond_to?(:mount_point)
|
50
|
+
raise "mount: filesystem is busy" if fs_instance.mount_point
|
51
|
+
|
52
|
+
begin
|
53
|
+
mount_point = full_path(mount_point, true, *cwd_root)
|
54
|
+
rescue Errno::ENOENT
|
55
|
+
raise SystemCallError.new(mp_display, Errno::ENOENT::Errno)
|
56
|
+
end
|
57
|
+
mount_point += VfsRealFile::SEPARATOR unless mount_point.end_with?(VfsRealFile::SEPARATOR)
|
58
|
+
|
59
|
+
@mount_mutex.synchronize do
|
60
|
+
raise "mount: mount point #{mp_display} is busy" if @fs_lookup[mount_point]
|
61
|
+
fs_instance.mount_point = mount_point
|
62
|
+
@fs_lookup[mount_point] = fs_instance
|
63
|
+
@mount_points.push(mount_point).sort_by!(&:length).reverse!
|
64
|
+
end
|
65
|
+
nil
|
66
|
+
end
|
67
|
+
|
68
|
+
# Unmount the FS mounted at the specified mount point
|
69
|
+
#
|
70
|
+
# @param mount_point [String] mount point to unmount
|
71
|
+
# @raise [RuntimeError] if mount point is not mounted
|
72
|
+
def umount(mount_point)
|
73
|
+
mount_point = full_path(mount_point, true, *cwd_root)
|
74
|
+
@mount_mutex.synchronize do
|
75
|
+
mp_display = mount_point
|
76
|
+
mount_point += VfsRealFile::SEPARATOR unless mount_point.end_with?(VfsRealFile::SEPARATOR)
|
77
|
+
raise "umount: nothing mounted on #{mp_display}" unless @fs_lookup[mount_point]
|
78
|
+
@fs_lookup.delete(mount_point).umount
|
79
|
+
@mount_points.delete(mount_point)
|
80
|
+
end
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
84
|
+
# @return [Array<String>] array of mount points
|
85
|
+
def mount_points
|
86
|
+
@mount_mutex.synchronize do
|
87
|
+
@mount_points.collect do |p|
|
88
|
+
if p == VfsRealFile::SEPARATOR
|
89
|
+
VfsRealFile::SEPARATOR
|
90
|
+
else
|
91
|
+
p.chomp(VfsRealFile::SEPARATOR)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def fs_on(mount_point)
|
98
|
+
mp = full_path(mount_point, true, *cwd_root)
|
99
|
+
mp += VfsRealFile::SEPARATOR unless mp.end_with?(VfsRealFile::SEPARATOR)
|
100
|
+
@mount_mutex.synchronize do
|
101
|
+
@fs_lookup[mp]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Boolean] indicating if mount point is mounted
|
106
|
+
def mounted?(mount_point)
|
107
|
+
!fs_on(mount_point).nil?
|
108
|
+
end
|
109
|
+
|
110
|
+
# Change virtual file system root, after which all root calls to mount point will
|
111
|
+
# be mapped to specified dir
|
112
|
+
#
|
113
|
+
# @param dir [String] new dir to assign as virtfs context root
|
114
|
+
# @raise [SystemCallError] if specified dir does not exist
|
115
|
+
def chroot(dir)
|
116
|
+
raise SystemCallError.new(dir, Errno::ENOENT::Errno) unless dir_exist?(dir)
|
117
|
+
@dir_mutex.synchronize do
|
118
|
+
@root = full_path(dir, true, @cwd, @root)
|
119
|
+
@cwd = VfsRealFile::SEPARATOR
|
120
|
+
end
|
121
|
+
0
|
122
|
+
end
|
123
|
+
|
124
|
+
# Invoke block with the specified root, restoring before returning
|
125
|
+
#
|
126
|
+
# @see chroot
|
127
|
+
def with_root(dir)
|
128
|
+
raise SystemCallError.new(dir, Errno::ENOENT::Errno) unless dir_exist?(dir)
|
129
|
+
@dir_mutex.synchronize do
|
130
|
+
raise "Cannot nest with_root blocks" unless @saved_root.nil?
|
131
|
+
@saved_root = @root
|
132
|
+
@saved_cwd = @cwd
|
133
|
+
@root = full_path(dir, true, @cwd, @root)
|
134
|
+
@cwd = VfsRealFile::SEPARATOR
|
135
|
+
end
|
136
|
+
begin
|
137
|
+
yield
|
138
|
+
ensure
|
139
|
+
@dir_mutex.synchronize do
|
140
|
+
@root = @saved_root
|
141
|
+
@cwd = @saved_cwd
|
142
|
+
@saved_root = nil
|
143
|
+
@saved_cwd = nil
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# @return [Boolean] indicating if specified dir exists
|
149
|
+
def dir_exist?(dir)
|
150
|
+
begin
|
151
|
+
fs, p = path_lookup(dir)
|
152
|
+
rescue Errno::ENOENT
|
153
|
+
return false
|
154
|
+
end
|
155
|
+
VirtFS.fs_call(fs) { dir_exist?(p) }
|
156
|
+
end
|
157
|
+
|
158
|
+
# Change virtual filesystem working directory
|
159
|
+
#
|
160
|
+
# @param dir [String] new dir to assign to virtfs cwd
|
161
|
+
def chdir(dir)
|
162
|
+
fs = path = nil
|
163
|
+
@dir_mutex.synchronize do
|
164
|
+
nwd = remove_root(local_path(dir, @cwd, @root), @root)
|
165
|
+
fs, path = mount_lookup(nwd)
|
166
|
+
@cwd = nwd
|
167
|
+
end
|
168
|
+
fs.dir_chdir(path) if fs.respond_to?(:dir_chdir)
|
169
|
+
end
|
170
|
+
|
171
|
+
# @return [String] current filesystem working directory
|
172
|
+
def getwd
|
173
|
+
@cwd
|
174
|
+
end
|
175
|
+
|
176
|
+
# Expand symbolic links and perform mount indirection look up.
|
177
|
+
#
|
178
|
+
# @param path [String] path to lookup
|
179
|
+
# @param raise_full_path [Boolean] indicates if error should be raised if lookup fails
|
180
|
+
# @param include_last [Boolean] indicates if last path component should be returned
|
181
|
+
#
|
182
|
+
# @raise [RunTimeError] if path could not be looked up and raise_full_path is true
|
183
|
+
# @raise [SystemCallError] if path could not be looked up
|
184
|
+
#
|
185
|
+
# @api private
|
186
|
+
# @see #mount_lookup
|
187
|
+
# @see #expand_links
|
188
|
+
#
|
189
|
+
def path_lookup(path, raise_full_path = false, include_last = true)
|
190
|
+
mount_lookup(full_path(path, include_last, *cwd_root))
|
191
|
+
rescue Errno::ENOENT
|
192
|
+
raise if raise_full_path
|
193
|
+
# so we report the original path.
|
194
|
+
raise SystemCallError.new(path, Errno::ENOENT::Errno)
|
195
|
+
end
|
196
|
+
|
197
|
+
# Expand symbolic links in the path.
|
198
|
+
# This must be done here, because a symlink in one file system
|
199
|
+
# can point to a file in another filesystem.
|
200
|
+
#
|
201
|
+
# @api private
|
202
|
+
# @param p [String] path to lookup
|
203
|
+
# @param include_last [Boolean] indicates if last path component should be returned
|
204
|
+
def expand_links(p, include_last = true)
|
205
|
+
cp = VfsRealFile::SEPARATOR
|
206
|
+
components = p.split(VfsRealFile::SEPARATOR)
|
207
|
+
components.shift if components[0] == "" # root
|
208
|
+
last_component = components.pop unless include_last
|
209
|
+
|
210
|
+
#
|
211
|
+
# For each component of the path, check to see
|
212
|
+
# if it's a symbolic link. If so, expand it
|
213
|
+
# relative to its base directory.
|
214
|
+
#
|
215
|
+
components.each do |c|
|
216
|
+
ncp = VfsRealFile.join(cp, c)
|
217
|
+
#
|
218
|
+
# Each file system knows how to check for,
|
219
|
+
# and read, its own links.
|
220
|
+
#
|
221
|
+
fs, lp = mount_lookup(ncp)
|
222
|
+
if fs.file_symlink?(lp)
|
223
|
+
sl = fs.file_readlink(lp)
|
224
|
+
cp = sl[0, 1] == VfsRealFile::SEPARATOR ? sl : VfsRealFile.join(cp, sl)
|
225
|
+
else
|
226
|
+
cp = ncp
|
227
|
+
end
|
228
|
+
end
|
229
|
+
return cp if include_last
|
230
|
+
VfsRealFile.join(cp, last_component.to_s)
|
231
|
+
end
|
232
|
+
|
233
|
+
# Helper to change virtual filesystem working directory to filesystem root
|
234
|
+
# @api private
|
235
|
+
#
|
236
|
+
def cwd_root
|
237
|
+
@dir_mutex.synchronize do
|
238
|
+
return @cwd, @root
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def restore_cwd_root(cwd, root)
|
243
|
+
@dir_mutex.synchronize do
|
244
|
+
@cwd = cwd if cwd
|
245
|
+
@root = root if root
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
|
251
|
+
def local_path(path, cwd, root)
|
252
|
+
lpath = path || cwd
|
253
|
+
lpath = VfsRealFile.join(cwd, path) if Pathname(path).relative?
|
254
|
+
lpath = VirtFS.normalize_path(lpath)
|
255
|
+
apply_root(lpath, root)
|
256
|
+
end
|
257
|
+
|
258
|
+
def full_path(path, include_last, cwd, root)
|
259
|
+
expand_links(local_path(path, cwd, root), include_last)
|
260
|
+
end
|
261
|
+
|
262
|
+
#
|
263
|
+
# Mount indirection look up.
|
264
|
+
# Given a path, return its corresponding file system
|
265
|
+
# and the part of the path relative to that file system.
|
266
|
+
# It assumes symbolic links have already been expanded.
|
267
|
+
# @api private
|
268
|
+
#
|
269
|
+
def mount_lookup(path) # private
|
270
|
+
spath = "#{path}#{VfsRealFile::SEPARATOR}"
|
271
|
+
@mount_mutex.synchronize do
|
272
|
+
@mount_points.each do |mp|
|
273
|
+
next if mp.length > spath.length
|
274
|
+
next unless spath.start_with?(mp)
|
275
|
+
return @fs_lookup[mp], path if mp == VfsRealFile::SEPARATOR # root
|
276
|
+
return @fs_lookup[mp], VfsRealFile::SEPARATOR if mp == spath # path is the mount point
|
277
|
+
return @fs_lookup[mp], path.sub(mp, VfsRealFile::SEPARATOR)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
raise SystemCallError.new(path, Errno::ENOENT::Errno)
|
281
|
+
end
|
282
|
+
|
283
|
+
def under_root?(path, root)
|
284
|
+
return true if path == root
|
285
|
+
path.start_with?(root + VfsRealFile::SEPARATOR)
|
286
|
+
end
|
287
|
+
|
288
|
+
def apply_root(path, root)
|
289
|
+
return path if root == VfsRealFile::SEPARATOR
|
290
|
+
VfsRealFile.join(root, path)
|
291
|
+
end
|
292
|
+
|
293
|
+
def remove_root(path, root)
|
294
|
+
return path if root == VfsRealFile::SEPARATOR
|
295
|
+
return VfsRealFile::SEPARATOR if path == root
|
296
|
+
return path unless under_root?(path, root)
|
297
|
+
path.sub(root, "")
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
module VirtFS
|
2
|
+
# Central/Global VirtFS context manager.
|
3
|
+
# Provides a singleton context management interface for use in VirtFS.
|
4
|
+
#
|
5
|
+
# Contexts are recorded here on a per-thread-group basis
|
6
|
+
class ContextManager
|
7
|
+
attr_reader :thread_group
|
8
|
+
|
9
|
+
@context_managers = {}
|
10
|
+
@context_manager_mutex = Mutex.new
|
11
|
+
|
12
|
+
def self.my_thread_group
|
13
|
+
Thread.current.group || ThreadGroup::Default
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.current
|
17
|
+
@context_manager_mutex.synchronize do
|
18
|
+
@context_managers[my_thread_group] ||= ContextManager.new(my_thread_group)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.current!
|
23
|
+
@context_manager_mutex.synchronize do
|
24
|
+
@context_managers[my_thread_group] || raise(VirtFS::NoContextError.new)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.context
|
29
|
+
current.current_context
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.context!
|
33
|
+
current!.current_context
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.managers
|
37
|
+
@context_manager_mutex.synchronize do
|
38
|
+
@context_managers.dup
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.manager_for(tgroup)
|
43
|
+
raise ArgumentError, "value must be a ThreadGroup object" unless tgroup.is_a?(ThreadGroup)
|
44
|
+
@context_manager_mutex.synchronize do
|
45
|
+
@context_managers[tgroup]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.new_manager_for(tgroup)
|
50
|
+
raise ArgumentError, "value must be a ThreadGroup object" unless tgroup.is_a?(ThreadGroup)
|
51
|
+
@context_manager_mutex.synchronize do
|
52
|
+
@context_managers[tgroup] = ContextManager.new(tgroup)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.remove_manager_for(tgroup)
|
57
|
+
raise ArgumentError, "value must be a ThreadGroup object" unless tgroup.is_a?(ThreadGroup)
|
58
|
+
@context_manager_mutex.synchronize do
|
59
|
+
@context_managers.delete(tgroup)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.reset_all
|
64
|
+
@context_manager_mutex.synchronize do
|
65
|
+
@context_managers = {}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize(thread_group)
|
70
|
+
@thread_group = thread_group
|
71
|
+
@context_mutex = Mutex.new
|
72
|
+
reset
|
73
|
+
end
|
74
|
+
|
75
|
+
def [](key)
|
76
|
+
@context_mutex.synchronize do
|
77
|
+
@contexts[key]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Change context without saving current context state.
|
83
|
+
#
|
84
|
+
def []=(key, context)
|
85
|
+
raise ArgumentError, "Context must be a VirtFS::Context object" if context && !context.is_a?(Context)
|
86
|
+
raise ArgumentError, "Cannot change the default context" if key == :default
|
87
|
+
@context_mutex.synchronize do
|
88
|
+
if context.nil?
|
89
|
+
ctx = @contexts.delete(key)
|
90
|
+
ctx.key = nil
|
91
|
+
return ctx
|
92
|
+
end
|
93
|
+
raise "Context for given key already exists" if @contexts[key]
|
94
|
+
context.key = key
|
95
|
+
@contexts[key] = context
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def activated?
|
100
|
+
@context_mutex.synchronize do
|
101
|
+
!@saved_context.nil?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Save the current context state and change context.
|
107
|
+
#
|
108
|
+
def activate!(key)
|
109
|
+
@context_mutex.synchronize do
|
110
|
+
raise "Context already activated" if @saved_context
|
111
|
+
raise "Context for given key doesn't exist" unless (ctx = @contexts[key])
|
112
|
+
@saved_context = @current_context
|
113
|
+
@current_context = ctx
|
114
|
+
@saved_context # returns the pre-activation context.
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# Restore the context state saved by activate!
|
120
|
+
#
|
121
|
+
def deactivate!
|
122
|
+
@context_mutex.synchronize do
|
123
|
+
raise "Context not activated" unless @saved_context
|
124
|
+
ret = @current_context
|
125
|
+
@current_context = @saved_context
|
126
|
+
@saved_context = nil
|
127
|
+
ret # returns the pre-deactivated context.
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def current_context
|
132
|
+
@context_mutex.synchronize do
|
133
|
+
@current_context
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def current_context=(key)
|
138
|
+
@context_mutex.synchronize do
|
139
|
+
raise "Context for given key doesn't exist" unless (ctx = @contexts[key])
|
140
|
+
@current_context = ctx
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def reset
|
145
|
+
@context_mutex.synchronize do
|
146
|
+
@contexts = {}
|
147
|
+
@saved_context = nil
|
148
|
+
@contexts[:default] = Context.new
|
149
|
+
@current_context = @contexts[:default]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def with(key)
|
154
|
+
activate!(key)
|
155
|
+
begin
|
156
|
+
yield
|
157
|
+
ensure
|
158
|
+
deactivate!
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def without
|
163
|
+
if !activated?
|
164
|
+
yield
|
165
|
+
else
|
166
|
+
begin
|
167
|
+
saved_context = deactivate!
|
168
|
+
yield
|
169
|
+
ensure
|
170
|
+
activate!(saved_context.key)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|