Class: ThreadSafe::MriCacheBackend

Inherits:
NonConcurrentCacheBackend show all
Defined in:
lib/thread_safe/mri_cache_backend.rb

Constant Summary collapse

WRITE_LOCK =

We can get away with a single global write lock (instead of a per-instance one) because of the GVL/green threads.

The previous implementation used Thread.critical on 1.8 MRI to implement the 4 composed atomic operations (put_if_absent, replace_pair, replace_if_exists, delete_pair) this however doesn't work for compute_if_absent because on 1.8 the Mutex class is itself implemented via Thread.critical and a call to Mutex#lock does not restore the previous Thread.critical value (thus any synchronisation clears the Thread.critical flag and we loose control). This poses a problem as the provided block might use synchronisation on its own.

NOTE: a neat idea of writing a c-ext to manually perform atomic put_if_absent, while relying on Ruby not releasing a GVL while calling a c-ext will not work because of the potentially Ruby implemented #hash and #eql? key methods.

Mutex.new

Instance Method Summary collapse

Constructor Details

This class inherits a constructor from ThreadSafe::NonConcurrentCacheBackend

Instance Method Details

#[]=(key, value) ⇒ Object



21
22
23
# File 'lib/thread_safe/mri_cache_backend.rb', line 21

def []=(key, value)
  WRITE_LOCK.synchronize { super }
end

#clearObject



65
66
67
# File 'lib/thread_safe/mri_cache_backend.rb', line 65

def clear
  WRITE_LOCK.synchronize { super }
end

#compute(key) ⇒ Object



37
38
39
# File 'lib/thread_safe/mri_cache_backend.rb', line 37

def compute(key)
  WRITE_LOCK.synchronize { super }
end

#compute_if_absent(key) ⇒ Object



25
26
27
28
29
30
31
# File 'lib/thread_safe/mri_cache_backend.rb', line 25

def compute_if_absent(key)
  if stored_value = _get(key) # fast non-blocking path for the most likely case
    stored_value
  else
    WRITE_LOCK.synchronize { super }
  end
end

#compute_if_present(key) ⇒ Object



33
34
35
# File 'lib/thread_safe/mri_cache_backend.rb', line 33

def compute_if_present(key)
  WRITE_LOCK.synchronize { super }
end

#delete(key) ⇒ Object



57
58
59
# File 'lib/thread_safe/mri_cache_backend.rb', line 57

def delete(key)
  WRITE_LOCK.synchronize { super }
end

#delete_pair(key, value) ⇒ Object



61
62
63
# File 'lib/thread_safe/mri_cache_backend.rb', line 61

def delete_pair(key, value)
  WRITE_LOCK.synchronize { super }
end

#get_and_set(key, value) ⇒ Object



53
54
55
# File 'lib/thread_safe/mri_cache_backend.rb', line 53

def get_and_set(key, value)
  WRITE_LOCK.synchronize { super }
end

#merge_pair(key, value) ⇒ Object



41
42
43
# File 'lib/thread_safe/mri_cache_backend.rb', line 41

def merge_pair(key, value)
  WRITE_LOCK.synchronize { super }
end

#replace_if_exists(key, new_value) ⇒ Object



49
50
51
# File 'lib/thread_safe/mri_cache_backend.rb', line 49

def replace_if_exists(key, new_value)
  WRITE_LOCK.synchronize { super }
end

#replace_pair(key, old_value, new_value) ⇒ Object



45
46
47
# File 'lib/thread_safe/mri_cache_backend.rb', line 45

def replace_pair(key, old_value, new_value)
  WRITE_LOCK.synchronize { super }
end