sync_attr Build Status

Thread-safe Ruby class and instance attributes

Status

Enterprise Production Ready - In daily use in large multi-threaded application

Introduction

When working in a multi-threaded environment it is important to ensure that any attributes that are shared across threads are properly protected to ensure that inconsistent data is not created. Lazy initializing these safe attributes improves startup times and only creates resources when they are needed.

For example, without sync_attr if two threads attempt to write to the same attribute at the same time it is not deterministic what the results will be. This condition is made worse when two threads attempt to initialize class instance attributes at the same time that could take a second or longer to complete.

A sync_cattr_reader is ideal for holding data loaded from configuration files. Also, shared objects, such as connection pools can be safely initialized and shared in this way across hundreds of threads. Once initialized all reads are shared without locks since no writer is defined.

Aside from safely sharing reads sync_cattr_accessor also ensures that data is not read while it is being modified. The writes are completely thread-safe ensuring that only one thread is modifying the value at a time and that reads are suspended until the write is complete.

Features

  • Adds thread-safe accessors for class instance attributes.
  • Only one thread can read or write the value at any one time.
  • Prevents attributes from being read while it is being updated or initialized for the first time.
  • Thread-safe attribute lazy initialization. Lazy initialization allows class attributes to be loaded only when first read. As a result it's value can be read for the first time from a database or configuration file once and only when needed.
  • Avoids having to create yet another Rails initializer, when the data can be fetched or held by a class instance attribute.
  • Avoids costly startup initialization when the initialized data may never be accessed. For example, when Rake tasks are run, they may not need access to everything in the Rails environment.
  • Works with Rails and regular Ruby.

Thread-safe Class Attribute example

Create a reader for a class attribute and lazy initialize its value on the first attempt to read it. I.e. Lazy initialize the value.

Very useful for initializing shared services that take time to initialize. In particular services that may not even be called in a specific process, for example when running rake, or when opening a console.

An optional block can be supplied to initialize the synchronized class attribute when it is first read. The initializer is thread safe and will block all other reads to this attribute while it is being initialized. This ensures that the initializer is only run once and that all threads to call the reader receive the same value, regardless of how many threads call the reader at the same time.

Example:

require 'sync_attr'

# Sample class with lazy initialized Thread-safe Class Attributes
class Person
  # Create a reader for the class attribute :name
  # and lazy initialize the value to 'Joe Bloggs' only on the first
  # call to the reader.
  # Ideal for when :name is loaded from a database or configuration file.
  sync_cattr_reader :name do
    'Joe Bloggs'
  end

  # Create a reader and a writer for the class attribute :age
  # and lazy initialize the value to 21 only on the first
  # call to the reader.
  sync_cattr_accessor :age do
    21
  end
end

puts "The person is #{Person.name} with age #{Person.age}"

Person.age = 22
puts "The person is #{Person.name} now has age #{Person.age}"

# => The person is Joe Bloggs now has age 22

Person.age = Proc.new {|age| age += 1 }
puts "The person is #{Person.name} now has age #{Person.age}"

# => The person is Joe Bloggs now has age 23

Thread-safe Instance Attribute example

Create a reader for an attribute and lazy initialize its value on the first attempt to read it. I.e. Lazy initialize the value.

An optional block can be supplied to initialize the synchronized attribute when it is first read. The initializer is thread safe and will block all other reads to this attribute while it is being initialized. This ensures that the initializer is only run once per object and that all threads to call the reader receive the same value, regardless of how many threads call the reader at the same time.

Example:

require 'sync_attr'

# Sample class with lazy initialized Thread-safe attributes
class Person
  include SyncAttr::Attributes

  # Create a reader for the thread-safe attribute :name
  # and lazy initialize the value to 'Joe Bloggs' only on the first
  # call to the reader.
  sync_attr_reader :name do
    'Joe Bloggs'
  end

  # Create a thread-safe reader and a writer for the attribute :age
  # and lazy initialize the value to 21 only on the first
  # call to the reader.
  sync_attr_accessor :age do
    21
  end
end

person = Person.new
puts "The person is #{person.name} with age #{person.age}"

# => The person is Joe Bloggs with age 21

person.age = 22
puts "The person is #{person.name} now has age #{person.age}"

# => The person is Joe Bloggs now has age 22

person.age = Proc.new {|age| age += 1 }
puts "The person is #{person.name} now has age #{person.age}"

# => The person is Joe Bloggs now has age 23

Install

gem install sync_attr

Meta

This project uses Semantic Versioning.

Authors

Reid Morrison :: [email protected] :: @reidmorrison

License

Copyright 2012, 2013, 2014, 2015 Reid Morrison

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.