Class: Concurrent::AbstractLocals
Relationships & Source Files | |
Extension / Inclusion / Inheritance Descendants | |
Subclasses:
|
|
Inherits: | Object |
Defined in: | lib/concurrent-ruby/concurrent/atomic/locals.rb |
Overview
**Private Implementation:** This abstraction is a private, internal implementation detail. It should never be used directly.
An abstract implementation of local storage, with sub-classes for per-thread and per-fiber locals.
Each execution context (EC, thread or fiber) has a lazily initialized array of local variable values. Each time a new local variable is created, we allocate an “index” for it.
For example, if the allocated index is 1, that means slot #1 in EVERY EC’s locals array will be used for the value of that variable.
The good thing about using a per-EC structure to hold values, rather than a global, is that no synchronization is needed when reading and writing those values (since the structure is only ever accessed by a single thread).
Of course, when a local variable is GC’d, 1) we need to recover its index for use by other new local variables (otherwise the locals arrays could get bigger and bigger with time), and 2) we need to null out all the references held in the now-unused slots (both to avoid blocking GC of those objects, and also to prevent “stale” values from being passed on to a new local when the index is reused).
Because we need to null out freed slots, we need to keep references to ALL the locals arrays, so we can null out the appropriate slots in all of them. This is why we need to use a finalizer to clean up the locals array when the EC goes out of scope.
Class Method Summary
- .new ⇒ AbstractLocals constructor
Instance Method Summary
- #fetch(index)
- #free_index(index)
- #next_index(local)
- #set(index, value)
- #synchronize (also: #weak_synchronize)
-
#weak_synchronize
Alias for #synchronize.
-
#local_finalizer(index)
private
When the local goes out of scope, clean up that slot across all locals currently assigned.
-
#locals
private
Returns the locals for the current scope, or nil if none exist.
-
#locals!
private
Returns the locals for the current scope, creating them if necessary.
-
#thread_fiber_finalizer(array_object_id)
private
When a thread/fiber goes out of scope, remove the array from @all_arrays.
Constructor Details
.new ⇒ AbstractLocals
# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 36
def initialize @free = [] @lock = Mutex.new @all_arrays = {} @next = 0 end
Instance Method Details
#fetch(index)
[ GitHub ]#free_index(index)
[ GitHub ]# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 71
def free_index(index) weak_synchronize do # The cost of GC'ing a TLV is linear in the number of ECs using local # variables. But that is natural! More ECs means more storage is used # per local variable. So naturally more CPU time is required to free # more storage. # # DO NOT use each_value which might conflict with new pair assignment # into the hash in #set method. @all_arrays.values.each do |locals| locals[index] = nil end # free index has to be published after the arrays are cleared: @free << index end end
#local_finalizer(index) (private)
When the local goes out of scope, clean up that slot across all locals currently assigned.
# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 112
def local_finalizer(index) proc do free_index(index) end end
#locals (private)
Returns the locals for the current scope, or nil if none exist.
# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 128
def locals raise NotImplementedError end
#locals! (private)
Returns the locals for the current scope, creating them if necessary.
# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 133
def locals! raise NotImplementedError end
#next_index(local)
[ GitHub ]# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 55
def next_index(local) index = synchronize do if @free.empty? @next += 1 else @free.pop end end # When the local goes out of scope, we should free the associated index # and all values stored into it. ObjectSpace.define_finalizer(local, local_finalizer(index)) index end
#set(index, value)
[ GitHub ]#synchronize Also known as: #weak_synchronize
[ GitHub ]# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 43
def synchronize @lock.synchronize { yield } end
#thread_fiber_finalizer(array_object_id) (private)
When a thread/fiber goes out of scope, remove the array from @all_arrays.
# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 119
def thread_fiber_finalizer(array_object_id) proc do weak_synchronize do @all_arrays.delete(array_object_id) end end end
#weak_synchronize
Alias for #synchronize.
# File 'lib/concurrent-ruby/concurrent/atomic/locals.rb', line 48
def weak_synchronize yield end