Module: SyncAttr::Attributes::ClassMethods

Defined in:
lib/sync_attr/attributes.rb

Instance Method Summary collapse

Instance Method Details

#sync_attr_accessor(*attributes, &block) ⇒ Object

Generate a reader and writer for the attribute



78
79
80
81
# File 'lib/sync_attr/attributes.rb', line 78

def sync_attr_accessor(*attributes, &block)
  sync_attr_reader(*attributes, &block)
  sync_attr_writer(*attributes)
end

#sync_attr_reader(*attributes, &block) ⇒ Object

Thread-safe access to attributes in an object.

Attributes protected with ‘sync_attr_reader`, `sync_attr_writer`, and/or `sync_attr_accessor` can be safely read and written across many threads.

Additionally ‘sync_attr_reader` supports lazy loading the corresponding attribute in a thread-safe way. While the value is being calculated / loaded all other threads calling that attribute will block until the value is available

An optional block can be supplied to initialize the attribute when first read. Acts as a thread safe lazy initializer. The block will only be called once even if several threads call the reader at the same time

Example:

class MyClass
  include SyncAttr::Attributes

  # Generates a reader for the attribute 'hello'
  # and Lazy initializes the value to 'hello world' only on the first
  # call to the reader
  sync_attr_reader :hello do
    'hello world'
  end
end


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/sync_attr/attributes.rb', line 28

def sync_attr_reader(*attributes, &block)
  attributes.each do |attribute|
    raise NameError.new("invalid attribute name: #{attribute}") unless attribute =~ /^[_A-Za-z]\w*$/
    self.send(:define_method, attribute.to_sym) do
      var_name = "@#{attribute}".to_sym
      if instance_variable_defined?(var_name)
        # If there is no writer then it is not necessary to protect reads
        if respond_to?("#{attribute}=".to_sym, true)
          sync_attr_sync(attribute) { instance_variable_get(var_name) }
        else
          instance_variable_get(var_name)
        end
      else
        return nil unless block
        sync_attr_sync(attribute) do
          # Now that we have exclusive access make sure that another thread has
          # not just initialized this attribute
          if instance_variable_defined?(var_name)
            instance_variable_get(var_name)
          else
            instance_variable_set(var_name, instance_eval(&block))
          end
        end
      end
    end
  end
end

#sync_attr_writer(*attributes) ⇒ Object

Generates a writer to set a synchronized attribute Supply a Proc ensure an attribute is not being updated by another thread:

my_object.count = Proc.new {|count| (count||0) + 1}


59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/sync_attr/attributes.rb', line 59

def sync_attr_writer(*attributes)
  attributes.each do |attribute|
    raise NameError.new("invalid attribute name: #{attribute}") unless attribute =~ /^[_A-Za-z]\w*$/
    class_eval(<<-EOS, __FILE__, __LINE__ + 1)
      def #{attribute}=(value)
        sync_attr_sync('#{attribute}') do
          if value.is_a?(Proc)
            current_value = @#{attribute} if defined?(@#{attribute})
            @#{attribute} = value.call(current_value)
          else
            @#{attribute} = value
          end
        end
      end
    EOS
  end
end