thread_safe 0.3.4-java → 0.3.5-java

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 34a5cc5ef3dfed9a31da69198a6aec9590070c05
4
- data.tar.gz: 84f4f94772109373b2fcfc72f3f7bebb4bf8d13c
3
+ metadata.gz: fbd202dbf911be29e025cf656ec85fa07024805f
4
+ data.tar.gz: ece8fcb51b53bea677a7de3bd3729b5a2345e394
5
5
  SHA512:
6
- metadata.gz: 12c0a59e4960fc7e913b12be6e4d05fc0be65f73eb18ed7caf06f99e76dacc306a68b90a1c1ff648fca1057f1c960d9d76048e052b7a416309e9176f8546ab25
7
- data.tar.gz: 47a278848d8ef4783b8382b7de1fa433f9e33fc3b2d3215180ffdd34ac482ec31d28dd2ad755cb124b77e14b2d885c8074257fdb74f814845c69f88d87314a77
6
+ metadata.gz: 8262d11575eda01b362dcf1661cfb023e1a6216fdffdfb0e4a47d4f48d0ea87a6d3950af9978d23dd9d85d524bbeef72f8b13b5c12db0bd930886bff2806c0d6
7
+ data.tar.gz: b1ac9f0689d82854d827dc58009e519518cb57cc25bb3908465322739b9e3f92c6d51e43e5f54fb89dc30ac6efa4136e5884fa6aecabf0b3eb8801fffb283aed
@@ -1,24 +1,43 @@
1
1
  language: ruby
2
2
  rvm:
3
- - jruby-18mode
4
- - jruby-19mode
5
- - rbx-2
6
- - 1.8.7
7
- - 1.9.3
3
+ - 2.2.0
4
+ - 2.1.5
5
+ - 2.1.4
8
6
  - 2.0.0
9
- - 2.1.0
7
+ - 1.9.3
8
+ - ruby-head
9
+ - jruby-1.7.18
10
+ - jruby-head
11
+ - rbx-2
10
12
  jdk: # for JRuby only
11
13
  - openjdk7
12
14
  - oraclejdk8
13
15
  matrix:
14
16
  exclude:
15
- - rvm: rbx-2
17
+ - rvm: 2.2.0
18
+ jdk: openjdk7
16
19
  jdk: oraclejdk8
17
- - rvm: 1.8.7
20
+ - rvm: 2.1.5
21
+ jdk: openjdk7
18
22
  jdk: oraclejdk8
19
- - rvm: 1.9.3
23
+ - rvm: 2.1.4
24
+ jdk: openjdk7
20
25
  jdk: oraclejdk8
21
26
  - rvm: 2.0.0
27
+ jdk: openjdk7
22
28
  jdk: oraclejdk8
23
- - rvm: 2.1.0
24
- jdk: oraclejdk8
29
+ - rvm: 1.9.3
30
+ jdk: openjdk7
31
+ jdk: oraclejdk8
32
+ - rvm: ruby-head
33
+ jdk: openjdk7
34
+ jdk: oraclejdk8
35
+ - rvm: rbx-2
36
+ jdk: openjdk7
37
+ jdk: oraclejdk8
38
+ allow_failures:
39
+ - rvm: ruby-head
40
+ - rvm: jruby-head
41
+ - rvm: 1.9.3
42
+
43
+ script: "rake TESTOPTS='--seed=1'"
@@ -0,0 +1,13 @@
1
+ --protected
2
+ --no-private
3
+ --embed-mixins
4
+ --output-dir ./yardoc
5
+ --markup markdown
6
+ --title=Concurrent Ruby
7
+ --template default
8
+ --template-path ./yard-template
9
+
10
+ ./lib/**/*.rb
11
+ -
12
+ README.md
13
+ LICENSE
data/Gemfile CHANGED
@@ -1,4 +1,17 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in thread_safe.gemspec
4
3
  gemspec
4
+
5
+ group :development, :test do
6
+ gem 'minitest', '~> 5.5.1'
7
+ gem 'minitest-reporters', '~> 1.0.11'
8
+ gem 'simplecov', '~> 0.9.2', :require => false
9
+ gem 'coveralls', '~> 0.7.11', :require => false
10
+ end
11
+
12
+ group :documentation do
13
+ gem 'countloc', '~> 0.4.0', :platforms => :mri, :require => false
14
+ gem 'yard', '~> 0.8.7.6', :require => false
15
+ gem 'inch', '~> 0.5.10', :platforms => :mri, :require => false
16
+ gem 'redcarpet', '~> 3.2.2', platforms: :mri # understands github markdown
17
+ end
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Threadsafe
2
2
 
3
- [![Build Status](https://travis-ci.org/headius/thread_safe.png)](https://travis-ci.org/headius/thread_safe)
3
+ [![Gem Version](https://badge.fury.io/rb/thread_safe.svg)](http://badge.fury.io/rb/thread_safe) [![Build Status](https://travis-ci.org/ruby-concurrency/thread_safe.svg?branch=master)](https://travis-ci.org/ruby-concurrency/thread_safe) [![Coverage Status](https://img.shields.io/coveralls/ruby-concurrency/thread_safe/master.svg)](https://coveralls.io/r/ruby-concurrency/thread_safe) [![Code Climate](https://codeclimate.com/github/ruby-concurrency/thread_safe.svg)](https://codeclimate.com/github/ruby-concurrency/thread_safe) [![Dependency Status](https://gemnasium.com/ruby-concurrency/thread_safe.svg)](https://gemnasium.com/ruby-concurrency/thread_safe) [![License](https://img.shields.io/badge/license-apache-green.svg)](http://opensource.org/licenses/MIT) [![Gitter chat](http://img.shields.io/badge/gitter-join%20chat%20%E2%86%92-brightgreen.svg)](https://gitter.im/ruby-concurrency/concurrent-ruby)
4
4
 
5
5
  A collection of thread-safe versions of common core Ruby classes.
6
6
 
data/Rakefile CHANGED
@@ -1,6 +1,19 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
+ ## safely load all the rake tasks in the `tasks` directory
5
+ def safe_load(file)
6
+ begin
7
+ load file
8
+ rescue LoadError => ex
9
+ puts "Error loading rake tasks from '#{file}' but will continue..."
10
+ puts ex.message
11
+ end
12
+ end
13
+ Dir.glob('tasks/**/*.rake').each do |rakefile|
14
+ safe_load rakefile
15
+ end
16
+
4
17
  task :default => :test
5
18
 
6
19
  if defined?(JRUBY_VERSION)
@@ -36,7 +49,7 @@ if defined?(JRUBY_VERSION)
36
49
  ant.jar :basedir => 'pkg/tests', :destfile => 'test/package.jar', :includes => '**/*.class'
37
50
  end
38
51
 
39
- task :package => [ :jar, 'test-jar' ]
52
+ task :package => [ :clean, :compile, :jar, 'test-jar' ]
40
53
  else
41
54
  # No need to package anything for non-jruby rubies
42
55
  task :package
@@ -63,7 +63,7 @@ public class JRubyCacheBackendLibrary implements Library {
63
63
  return true;
64
64
  } catch (Throwable t) { // ensuring we really do catch everything
65
65
  // Doug's Unsafe setup errors always have this "Could not ini.." message
66
- if (t.getMessage().contains("Could not initialize intrinsics") || isCausedBySecurityException(t)) {
66
+ if (isCausedBySecurityException(t)) {
67
67
  return false;
68
68
  }
69
69
  throw (t instanceof RuntimeException ? (RuntimeException) t : new RuntimeException(t));
@@ -72,7 +72,7 @@ public class JRubyCacheBackendLibrary implements Library {
72
72
 
73
73
  private static boolean isCausedBySecurityException(Throwable t) {
74
74
  while (t != null) {
75
- if (t instanceof SecurityException) {
75
+ if ((t.getMessage() != null && t.getMessage().contains("Could not initialize intrinsics")) || t instanceof SecurityException) {
76
76
  return true;
77
77
  }
78
78
  t = t.getCause();
@@ -1,169 +1,158 @@
1
1
  module ThreadSafe
2
- # A Ruby port of the Doug Lea's jsr166e.ConcurrentHashMapV8 class version 1.59 available in public domain.
3
- # Original source code available here: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.59
2
+ # A Ruby port of the Doug Lea's jsr166e.ConcurrentHashMapV8 class version 1.59
3
+ # available in public domain.
4
4
  #
5
- # The Ruby port skips out the +TreeBin+ (red-black trees for use in bins
6
- # whose size exceeds a threshold).
5
+ # Original source code available here:
6
+ # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.59
7
7
  #
8
- # A hash table supporting full concurrency of retrievals and
9
- # high expected concurrency for updates. However, even though all
10
- # operations are thread-safe, retrieval operations do _not_ entail locking,
11
- # and there is _not_ any support for locking the entire table
12
- # in a way that prevents all access.
8
+ # The Ruby port skips out the +TreeBin+ (red-black trees for use in bins whose
9
+ # size exceeds a threshold).
13
10
  #
14
- # Retrieval operations generally do not block, so may overlap with
15
- # update operations. Retrievals reflect the results of the most
16
- # recently _completed_ update operations holding upon their
17
- # onset. (More formally, an update operation for a given key bears a
18
- # _happens-before_ relation with any (non +nil+) retrieval for
19
- # that key reporting the updated value.) For aggregate operations
20
- # such as +clear()+, concurrent retrievals may reflect insertion or removal
21
- # of only some entries. Similarly, the +each_pair+ iterator yields elements
22
- # reflecting the state of the hash table at some point at or since
23
- # the start of the +each_pair+. Bear in mind that the results of
24
- # aggregate status methods including +size()+ and +empty?+} are typically
25
- # useful only when a map is not undergoing concurrent updates in other
26
- # threads. Otherwise the results of these methods reflect transient
27
- # states that may be adequate for monitoring or estimation purposes, but not
28
- # for program control.
11
+ # A hash table supporting full concurrency of retrievals and high expected
12
+ # concurrency for updates. However, even though all operations are
13
+ # thread-safe, retrieval operations do _not_ entail locking, and there is
14
+ # _not_ any support for locking the entire table in a way that prevents all
15
+ # access.
29
16
  #
30
- # The table is dynamically expanded when there are too many
31
- # collisions (i.e., keys that have distinct hash codes but fall into
32
- # the same slot modulo the table size), with the expected average
33
- # effect of maintaining roughly two bins per mapping (corresponding
34
- # to a 0.75 load factor threshold for resizing). There may be much
35
- # variance around this average as mappings are added and removed, but
36
- # overall, this maintains a commonly accepted time/space tradeoff for
37
- # hash tables. However, resizing this or any other kind of hash
38
- # table may be a relatively slow operation. When possible, it is a
39
- # good idea to provide a size estimate as an optional :initial_capacity
17
+ # Retrieval operations generally do not block, so may overlap with update
18
+ # operations. Retrievals reflect the results of the most recently _completed_
19
+ # update operations holding upon their onset. (More formally, an update
20
+ # operation for a given key bears a _happens-before_ relation with any (non
21
+ # +nil+) retrieval for that key reporting the updated value.) For aggregate
22
+ # operations such as +clear()+, concurrent retrievals may reflect insertion or
23
+ # removal of only some entries. Similarly, the +each_pair+ iterator yields
24
+ # elements reflecting the state of the hash table at some point at or since
25
+ # the start of the +each_pair+. Bear in mind that the results of aggregate
26
+ # status methods including +size()+ and +empty?+} are typically useful only
27
+ # when a map is not undergoing concurrent updates in other threads. Otherwise
28
+ # the results of these methods reflect transient states that may be adequate
29
+ # for monitoring or estimation purposes, but not for program control.
30
+ #
31
+ # The table is dynamically expanded when there are too many collisions (i.e.,
32
+ # keys that have distinct hash codes but fall into the same slot modulo the
33
+ # table size), with the expected average effect of maintaining roughly two
34
+ # bins per mapping (corresponding to a 0.75 load factor threshold for
35
+ # resizing). There may be much variance around this average as mappings are
36
+ # added and removed, but overall, this maintains a commonly accepted
37
+ # time/space tradeoff for hash tables. However, resizing this or any other
38
+ # kind of hash table may be a relatively slow operation. When possible, it is
39
+ # a good idea to provide a size estimate as an optional :initial_capacity
40
40
  # initializer argument. An additional optional :load_factor constructor
41
- # argument provides a further means of customizing initial table capacity
42
- # by specifying the table density to be used in calculating the amount of
43
- # space to allocate for the given number of elements. Note that using
44
- # many keys with exactly the same +hash+ is a sure way to slow down
45
- # performance of any hash table.
41
+ # argument provides a further means of customizing initial table capacity by
42
+ # specifying the table density to be used in calculating the amount of space
43
+ # to allocate for the given number of elements. Note that using many keys with
44
+ # exactly the same +hash+ is a sure way to slow down performance of any hash
45
+ # table.
46
46
  #
47
- # == Design overview
47
+ # ## Design overview
48
48
  #
49
- # The primary design goal of this hash table is to maintain
50
- # concurrent readability (typically method +[]+, but also
51
- # iteration and related methods) while minimizing update
52
- # contention. Secondary goals are to keep space consumption about
53
- # the same or better than plain +Hash+, and to support high
49
+ # The primary design goal of this hash table is to maintain concurrent
50
+ # readability (typically method +[]+, but also iteration and related methods)
51
+ # while minimizing update contention. Secondary goals are to keep space
52
+ # consumption about the same or better than plain +Hash+, and to support high
54
53
  # initial insertion rates on an empty table by many threads.
55
54
  #
56
- # Each key-value mapping is held in a +Node+. The validation-based
57
- # approach explained below leads to a lot of code sprawl because
58
- # retry-control precludes factoring into smaller methods.
55
+ # Each key-value mapping is held in a +Node+. The validation-based approach
56
+ # explained below leads to a lot of code sprawl because retry-control
57
+ # precludes factoring into smaller methods.
58
+ #
59
+ # The table is lazily initialized to a power-of-two size upon the first
60
+ # insertion. Each bin in the table normally contains a list of +Node+s (most
61
+ # often, the list has only zero or one +Node+). Table accesses require
62
+ # volatile/atomic reads, writes, and CASes. The lists of nodes within bins are
63
+ # always accurately traversable under volatile reads, so long as lookups check
64
+ # hash code and non-nullness of value before checking key equality.
59
65
  #
60
- # The table is lazily initialized to a power-of-two size upon the
61
- # first insertion. Each bin in the table normally contains a
62
- # list of +Node+s (most often, the list has only zero or one +Node+).
63
- # Table accesses require volatile/atomic reads, writes, and
64
- # CASes. The lists of nodes within bins are always accurately traversable
65
- # under volatile reads, so long as lookups check hash code
66
- # and non-nullness of value before checking key equality.
66
+ # We use the top two bits of +Node+ hash fields for control purposes -- they
67
+ # are available anyway because of addressing constraints. As explained further
68
+ # below, these top bits are used as follows:
67
69
  #
68
- # We use the top two bits of +Node+ hash fields for control
69
- # purposes -- they are available anyway because of addressing
70
- # constraints. As explained further below, these top bits are
71
- # used as follows:
72
- # 00 - Normal
73
- # 01 - Locked
74
- # 11 - Locked and may have a thread waiting for lock
75
- # 10 - +Node+ is a forwarding node
70
+ # - 00 - Normal
71
+ # - 01 - Locked
72
+ # - 11 - Locked and may have a thread waiting for lock
73
+ # - 10 - +Node+ is a forwarding node
76
74
  #
77
- # The lower 28 bits of each +Node+'s hash field contain a
78
- # the key's hash code, except for forwarding nodes, for which
79
- # the lower bits are zero (and so always have hash field == +MOVED+).
75
+ # The lower 28 bits of each +Node+'s hash field contain a the key's hash code,
76
+ # except for forwarding nodes, for which the lower bits are zero (and so
77
+ # always have hash field == +MOVED+).
80
78
  #
81
- # Insertion (via +[]=+ or its variants) of the first node in an
82
- # empty bin is performed by just CASing it to the bin. This is
83
- # by far the most common case for put operations under most
84
- # key/hash distributions. Other update operations (insert,
85
- # delete, and replace) require locks. We do not want to waste
86
- # the space required to associate a distinct lock object with
87
- # each bin, so instead use the first node of a bin list itself as
88
- # a lock. Blocking support for these locks relies +Util::CheapLockable.
89
- # However, we also need a +try_lock+ construction, so we overlay
90
- # these by using bits of the +Node+ hash field for lock control (see above),
91
- # and so normally use builtin monitors only for blocking and signalling using
79
+ # Insertion (via +[]=+ or its variants) of the first node in an empty bin is
80
+ # performed by just CASing it to the bin. This is by far the most common case
81
+ # for put operations under most key/hash distributions. Other update
82
+ # operations (insert, delete, and replace) require locks. We do not want to
83
+ # waste the space required to associate a distinct lock object with each bin,
84
+ # so instead use the first node of a bin list itself as a lock. Blocking
85
+ # support for these locks relies +Util::CheapLockable. However, we also need a
86
+ # +try_lock+ construction, so we overlay these by using bits of the +Node+
87
+ # hash field for lock control (see above), and so normally use builtin
88
+ # monitors only for blocking and signalling using
92
89
  # +cheap_wait+/+cheap_broadcast+ constructions. See +Node#try_await_lock+.
93
90
  #
94
- # Using the first node of a list as a lock does not by itself
95
- # suffice though: When a node is locked, any update must first
96
- # validate that it is still the first node after locking it, and
97
- # retry if not. Because new nodes are always appended to lists,
98
- # once a node is first in a bin, it remains first until deleted
99
- # or the bin becomes invalidated (upon resizing). However,
100
- # operations that only conditionally update may inspect nodes
101
- # until the point of update. This is a converse of sorts to the
102
- # lazy locking technique described by Herlihy & Shavit.
91
+ # Using the first node of a list as a lock does not by itself suffice though:
92
+ # When a node is locked, any update must first validate that it is still the
93
+ # first node after locking it, and retry if not. Because new nodes are always
94
+ # appended to lists, once a node is first in a bin, it remains first until
95
+ # deleted or the bin becomes invalidated (upon resizing). However, operations
96
+ # that only conditionally update may inspect nodes until the point of update.
97
+ # This is a converse of sorts to the lazy locking technique described by
98
+ # Herlihy & Shavit.
103
99
  #
104
- # The main disadvantage of per-bin locks is that other update
105
- # operations on other nodes in a bin list protected by the same
106
- # lock can stall, for example when user +eql?+ or mapping
107
- # functions take a long time. However, statistically, under
108
- # random hash codes, this is not a common problem. Ideally, the
109
- # frequency of nodes in bins follows a Poisson distribution
110
- # (http://en.wikipedia.org/wiki/Poisson_distribution) with a
111
- # parameter of about 0.5 on average, given the resizing threshold
112
- # of 0.75, although with a large variance because of resizing
113
- # granularity. Ignoring variance, the expected occurrences of
114
- # list size k are (exp(-0.5) * pow(0.5, k) / factorial(k)). The
115
- # first values are:
100
+ # The main disadvantage of per-bin locks is that other update operations on
101
+ # other nodes in a bin list protected by the same lock can stall, for example
102
+ # when user +eql?+ or mapping functions take a long time. However,
103
+ # statistically, under random hash codes, this is not a common problem.
104
+ # Ideally, the frequency of nodes in bins follows a Poisson distribution
105
+ # (http://en.wikipedia.org/wiki/Poisson_distribution) with a parameter of
106
+ # about 0.5 on average, given the resizing threshold of 0.75, although with a
107
+ # large variance because of resizing granularity. Ignoring variance, the
108
+ # expected occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
109
+ # factorial(k)). The first values are:
116
110
  #
117
- # 0: 0.60653066
118
- # 1: 0.30326533
119
- # 2: 0.07581633
120
- # 3: 0.01263606
121
- # 4: 0.00157952
122
- # 5: 0.00015795
123
- # 6: 0.00001316
124
- # 7: 0.00000094
125
- # 8: 0.00000006
126
- # more: less than 1 in ten million
111
+ # - 0: 0.60653066
112
+ # - 1: 0.30326533
113
+ # - 2: 0.07581633
114
+ # - 3: 0.01263606
115
+ # - 4: 0.00157952
116
+ # - 5: 0.00015795
117
+ # - 6: 0.00001316
118
+ # - 7: 0.00000094
119
+ # - 8: 0.00000006
120
+ # - more: less than 1 in ten million
127
121
  #
128
- # Lock contention probability for two threads accessing distinct
129
- # elements is roughly 1 / (8 * #elements) under random hashes.
122
+ # Lock contention probability for two threads accessing distinct elements is
123
+ # roughly 1 / (8 * #elements) under random hashes.
130
124
  #
131
- # The table is resized when occupancy exceeds a percentage
132
- # threshold (nominally, 0.75, but see below). Only a single
133
- # thread performs the resize (using field +size_control+, to arrange
134
- # exclusion), but the table otherwise remains usable for reads
135
- # and updates. Resizing proceeds by transferring bins, one by
136
- # one, from the table to the next table. Because we are using
137
- # power-of-two expansion, the elements from each bin must either
138
- # stay at same index, or move with a power of two offset. We
139
- # eliminate unnecessary node creation by catching cases where old
140
- # nodes can be reused because their next fields won't change. On
141
- # average, only about one-sixth of them need cloning when a table
142
- # doubles. The nodes they replace will be garbage collectable as
143
- # soon as they are no longer referenced by any reader thread that
144
- # may be in the midst of concurrently traversing table. Upon
145
- # transfer, the old table bin contains only a special forwarding
146
- # node (with hash field +MOVED+) that contains the next table as
147
- # its key. On encountering a forwarding node, access and update
148
- # operations restart, using the new table.
125
+ # The table is resized when occupancy exceeds a percentage threshold
126
+ # (nominally, 0.75, but see below). Only a single thread performs the resize
127
+ # (using field +size_control+, to arrange exclusion), but the table otherwise
128
+ # remains usable for reads and updates. Resizing proceeds by transferring
129
+ # bins, one by one, from the table to the next table. Because we are using
130
+ # power-of-two expansion, the elements from each bin must either stay at same
131
+ # index, or move with a power of two offset. We eliminate unnecessary node
132
+ # creation by catching cases where old nodes can be reused because their next
133
+ # fields won't change. On average, only about one-sixth of them need cloning
134
+ # when a table doubles. The nodes they replace will be garbage collectable as
135
+ # soon as they are no longer referenced by any reader thread that may be in
136
+ # the midst of concurrently traversing table. Upon transfer, the old table bin
137
+ # contains only a special forwarding node (with hash field +MOVED+) that
138
+ # contains the next table as its key. On encountering a forwarding node,
139
+ # access and update operations restart, using the new table.
149
140
  #
150
- # Each bin transfer requires its bin lock. However, unlike other
151
- # cases, a transfer can skip a bin if it fails to acquire its
152
- # lock, and revisit it later. Method +rebuild+ maintains a buffer of
153
- # TRANSFER_BUFFER_SIZE bins that have been skipped because of failure
154
- # to acquire a lock, and blocks only if none are available
155
- # (i.e., only very rarely). The transfer operation must also ensure
156
- # that all accessible bins in both the old and new table are usable by
157
- # any traversal. When there are no lock acquisition failures, this is
158
- # arranged simply by proceeding from the last bin (+table.size - 1+) up
159
- # towards the first. Upon seeing a forwarding node, traversals arrange
160
- # to move to the new table without revisiting nodes. However, when any
161
- # node is skipped during a transfer, all earlier table bins may have
162
- # become visible, so are initialized with a reverse-forwarding node back
163
- # to the old table until the new ones are established. (This
164
- # sometimes requires transiently locking a forwarding node, which
165
- # is possible under the above encoding.) These more expensive
166
- # mechanics trigger only when necessary.
141
+ # Each bin transfer requires its bin lock. However, unlike other cases, a
142
+ # transfer can skip a bin if it fails to acquire its lock, and revisit it
143
+ # later. Method +rebuild+ maintains a buffer of TRANSFER_BUFFER_SIZE bins that
144
+ # have been skipped because of failure to acquire a lock, and blocks only if
145
+ # none are available (i.e., only very rarely). The transfer operation must
146
+ # also ensure that all accessible bins in both the old and new table are
147
+ # usable by any traversal. When there are no lock acquisition failures, this
148
+ # is arranged simply by proceeding from the last bin (+table.size - 1+) up
149
+ # towards the first. Upon seeing a forwarding node, traversals arrange to move
150
+ # to the new table without revisiting nodes. However, when any node is skipped
151
+ # during a transfer, all earlier table bins may have become visible, so are
152
+ # initialized with a reverse-forwarding node back to the old table until the
153
+ # new ones are established. (This sometimes requires transiently locking a
154
+ # forwarding node, which is possible under the above encoding.) These more
155
+ # expensive mechanics trigger only when necessary.
167
156
  #
168
157
  # The traversal scheme also applies to partial traversals of
169
158
  # ranges of bins (via an alternate Traverser constructor)
@@ -229,10 +218,10 @@ module ThreadSafe
229
218
  end
230
219
  end
231
220
 
232
- # Key-value entry. Nodes with a hash field of +MOVED+ are special,
233
- # and do not contain user keys or values. Otherwise, keys are never +nil+,
234
- # and +NULL+ +value+ fields indicate that a node is in the process
235
- # of being deleted or created. For purposes of read-only access, a key may be read
221
+ # Key-value entry. Nodes with a hash field of +MOVED+ are special, and do
222
+ # not contain user keys or values. Otherwise, keys are never +nil+, and
223
+ # +NULL+ +value+ fields indicate that a node is in the process of being
224
+ # deleted or created. For purposes of read-only access, a key may be read
236
225
  # before a value, but can only be used after checking value to be +!= NULL+.
237
226
  class Node
238
227
  extend Util::Volatile
@@ -259,17 +248,15 @@ module ThreadSafe
259
248
  self.next = next_node
260
249
  end
261
250
 
262
- # Spins a while if +LOCKED+ bit set and this node is the first
263
- # of its bin, and then sets +WAITING+ bits on hash field and
264
- # blocks (once) if they are still set. It is OK for this
265
- # method to return even if lock is not available upon exit,
266
- # which enables these simple single-wait mechanics.
251
+ # Spins a while if +LOCKED+ bit set and this node is the first of its bin,
252
+ # and then sets +WAITING+ bits on hash field and blocks (once) if they are
253
+ # still set. It is OK for this method to return even if lock is not
254
+ # available upon exit, which enables these simple single-wait mechanics.
267
255
  #
268
- # The corresponding signalling operation is performed within
269
- # callers: Upon detecting that +WAITING+ has been set when
270
- # unlocking lock (via a failed CAS from non-waiting +LOCKED+
271
- # state), unlockers acquire the +cheap_synchronize+ lock and
272
- # perform a +cheap_broadcast+.
256
+ # The corresponding signalling operation is performed within callers: Upon
257
+ # detecting that +WAITING+ has been set when unlocking lock (via a failed
258
+ # CAS from non-waiting +LOCKED+ state), unlockers acquire the
259
+ # +cheap_synchronize+ lock and perform a +cheap_broadcast+.
273
260
  def try_await_lock(table, i)
274
261
  if table && i >= 0 && i < table.size # bounds check, TODO: why are we bounds checking?
275
262
  spins = SPIN_LOCK_ATTEMPTS
@@ -360,12 +347,12 @@ module ThreadSafe
360
347
  extend Util::Volatile
361
348
  attr_volatile :table, # The array of bins. Lazily initialized upon first insertion. Size is always a power of two.
362
349
 
363
- # Table initialization and resizing control. When negative, the
364
- # table is being initialized or resized. Otherwise, when table is
365
- # null, holds the initial table size to use upon creation, or 0
366
- # for default. After initialization, holds the next element count
367
- # value upon which to resize the table.
368
- :size_control
350
+ # Table initialization and resizing control. When negative, the
351
+ # table is being initialized or resized. Otherwise, when table is
352
+ # null, holds the initial table size to use upon creation, or 0
353
+ # for default. After initialization, holds the next element count
354
+ # value upon which to resize the table.
355
+ :size_control
369
356
 
370
357
  def initialize(options = nil)
371
358
  super()
@@ -786,10 +773,9 @@ module ThreadSafe
786
773
  current_table
787
774
  end
788
775
 
789
- # If table is too small and not already resizing, creates next
790
- # table and transfers bins. Rechecks occupancy after a transfer
791
- # to see if another resize is already needed because resizings
792
- # are lagging additions.
776
+ # If table is too small and not already resizing, creates next table and
777
+ # transfers bins. Rechecks occupancy after a transfer to see if another
778
+ # resize is already needed because resizings are lagging additions.
793
779
  def check_for_resize
794
780
  while (current_table = table) && MAX_CAPACITY > (table_size = current_table.size) && NOW_RESIZING != (size_ctrl = size_control) && size_ctrl < @counter.sum
795
781
  try_in_resize_lock(current_table, size_ctrl) do
@@ -1,14 +1,21 @@
1
1
  module ThreadSafe
2
2
  class MriCacheBackend < NonConcurrentCacheBackend
3
- # We can get away with a single global write lock (instead of a per-instance one) because of the GVL/green threads.
3
+ # We can get away with a single global write lock (instead of a per-instance
4
+ # one) because of the GVL/green threads.
4
5
  #
5
- # The previous implementation used `Thread.critical` on 1.8 MRI to implement the 4 composed atomic operations (`put_if_absent`, `replace_pair`,
6
- # `replace_if_exists`, `delete_pair`) this however doesn't work for `compute_if_absent` because on 1.8 the Mutex class is itself implemented
7
- # via `Thread.critical` and a call to `Mutex#lock` does not restore the previous `Thread.critical` value (thus any synchronisation clears the
8
- # `Thread.critical` flag and we loose control). This poses a problem as the provided block might use synchronisation on its own.
6
+ # The previous implementation used `Thread.critical` on 1.8 MRI to implement
7
+ # the 4 composed atomic operations (`put_if_absent`, `replace_pair`,
8
+ # `replace_if_exists`, `delete_pair`) this however doesn't work for
9
+ # `compute_if_absent` because on 1.8 the Mutex class is itself implemented
10
+ # via `Thread.critical` and a call to `Mutex#lock` does not restore the
11
+ # previous `Thread.critical` value (thus any synchronisation clears the
12
+ # `Thread.critical` flag and we loose control). This poses a problem as the
13
+ # provided block might use synchronisation on its own.
9
14
  #
10
- # 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
11
- # a c-ext will not work because of the potentially Ruby implemented `#hash` and `#eql?` key methods.
15
+ # NOTE: a neat idea of writing a c-ext to manually perform atomic
16
+ # put_if_absent, while relying on Ruby not releasing a GVL while calling a
17
+ # c-ext will not work because of the potentially Ruby implemented `#hash`
18
+ # and `#eql?` key methods.
12
19
  WRITE_LOCK = Mutex.new
13
20
 
14
21
  def []=(key, value)
@@ -1,7 +1,9 @@
1
1
  module ThreadSafe
2
2
  class NonConcurrentCacheBackend
3
- # WARNING: all public methods of the class must operate on the @backend directly without calling each other. This is important
4
- # because of the SynchronizedCacheBackend which uses a non-reentrant mutex for perfomance reasons.
3
+ # WARNING: all public methods of the class must operate on the @backend
4
+ # directly without calling each other. This is important because of the
5
+ # SynchronizedCacheBackend which uses a non-reentrant mutex for perfomance
6
+ # reasons.
5
7
  def initialize(options = nil)
6
8
  @backend = {}
7
9
  end
@@ -2,7 +2,8 @@ module ThreadSafe
2
2
  class SynchronizedCacheBackend < NonConcurrentCacheBackend
3
3
  require 'mutex_m'
4
4
  include Mutex_m
5
- # WARNING: Mutex_m is a non-reentrant lock, so the synchronized methods are not allowed to call each other.
5
+ # WARNING: Mutex_m is a non-reentrant lock, so the synchronized methods are
6
+ # not allowed to call each other.
6
7
 
7
8
  def [](key)
8
9
  synchronize { super }
@@ -9,9 +9,9 @@ require 'monitor'
9
9
  # array = SynchronizedDelegator.new([]) # thread-safe
10
10
  #
11
11
  # A simple `Monitor` provides a very coarse-grained way to synchronize a given
12
- # object, in that it will cause synchronization for methods that have no
13
- # need for it, but this is a trivial way to get thread-safety where none may
14
- # exist currently on some implementations.
12
+ # object, in that it will cause synchronization for methods that have no need
13
+ # for it, but this is a trivial way to get thread-safety where none may exist
14
+ # currently on some implementations.
15
15
  #
16
16
  # This class is currently being considered for inclusion into stdlib, via
17
17
  # https://bugs.ruby-lang.org/issues/8556
@@ -1,7 +1,10 @@
1
1
  module ThreadSafe
2
2
  module Util
3
- # A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8 available in public domain.
4
- # Original source code available here: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8
3
+ # A Ruby port of the Doug Lea's jsr166e.LondAdder class version 1.8
4
+ # available in public domain.
5
+ #
6
+ # Original source code available here:
7
+ # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/LongAdder.java?revision=1.8
5
8
  #
6
9
  # One or more variables that together maintain an initially zero
7
10
  # sum. When updates (method +add+) are contended across threads,
@@ -1,6 +1,7 @@
1
1
  module ThreadSafe
2
2
  module Util
3
- # Provides a cheapest possible (mainly in terms of memory usage) +Mutex+ with the +ConditionVariable+ bundled in.
3
+ # Provides a cheapest possible (mainly in terms of memory usage) +Mutex+
4
+ # with the +ConditionVariable+ bundled in.
4
5
  #
5
6
  # Usage:
6
7
  # class A
@@ -1,69 +1,65 @@
1
1
  module ThreadSafe
2
2
  module Util
3
- # A Ruby port of the Doug Lea's jsr166e.Striped64 class version 1.6 available in public domain.
4
- # Original source code available here: http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.6
3
+ # A Ruby port of the Doug Lea's jsr166e.Striped64 class version 1.6
4
+ # available in public domain.
5
5
  #
6
- # Class holding common representation and mechanics for classes supporting dynamic striping on 64bit values.
6
+ # Original source code available here:
7
+ # http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/Striped64.java?revision=1.6
7
8
  #
8
- # This class maintains a lazily-initialized table of atomically
9
- # updated variables, plus an extra +base+ field. The table size
10
- # is a power of two. Indexing uses masked per-thread hash codes.
11
- # Nearly all methods on this class are private, accessed directly
12
- # by subclasses.
9
+ # Class holding common representation and mechanics for classes supporting
10
+ # dynamic striping on 64bit values.
13
11
  #
14
- # Table entries are of class +Cell+; a variant of AtomicLong padded
15
- # to reduce cache contention on most processors. Padding is
16
- # overkill for most Atomics because they are usually irregularly
17
- # scattered in memory and thus don't interfere much with each
18
- # other. But Atomic objects residing in arrays will tend to be
19
- # placed adjacent to each other, and so will most often share
20
- # cache lines (with a huge negative performance impact) without
12
+ # This class maintains a lazily-initialized table of atomically updated
13
+ # variables, plus an extra +base+ field. The table size is a power of two.
14
+ # Indexing uses masked per-thread hash codes. Nearly all methods on this
15
+ # class are private, accessed directly by subclasses.
16
+ #
17
+ # Table entries are of class +Cell+; a variant of AtomicLong padded to
18
+ # reduce cache contention on most processors. Padding is overkill for most
19
+ # Atomics because they are usually irregularly scattered in memory and thus
20
+ # don't interfere much with each other. But Atomic objects residing in
21
+ # arrays will tend to be placed adjacent to each other, and so will most
22
+ # often share cache lines (with a huge negative performance impact) without
21
23
  # this precaution.
22
24
  #
23
- # In part because +Cell+s are relatively large, we avoid creating
24
- # them until they are needed. When there is no contention, all
25
- # updates are made to the +base+ field. Upon first contention (a
26
- # failed CAS on +base+ update), the table is initialized to size 2.
27
- # The table size is doubled upon further contention until
28
- # reaching the nearest power of two greater than or equal to the
29
- # number of CPUS. Table slots remain empty (+nil+) until they are
25
+ # In part because +Cell+s are relatively large, we avoid creating them until
26
+ # they are needed. When there is no contention, all updates are made to the
27
+ # +base+ field. Upon first contention (a failed CAS on +base+ update), the
28
+ # table is initialized to size 2. The table size is doubled upon further
29
+ # contention until reaching the nearest power of two greater than or equal
30
+ # to the number of CPUS. Table slots remain empty (+nil+) until they are
30
31
  # needed.
31
32
  #
32
- # A single spinlock (+busy+) is used for initializing and
33
- # resizing the table, as well as populating slots with new +Cell+s.
34
- # There is no need for a blocking lock: When the lock is not
35
- # available, threads try other slots (or the base). During these
36
- # retries, there is increased contention and reduced locality,
37
- # which is still better than alternatives.
33
+ # A single spinlock (+busy+) is used for initializing and resizing the
34
+ # table, as well as populating slots with new +Cell+s. There is no need for
35
+ # a blocking lock: When the lock is not available, threads try other slots
36
+ # (or the base). During these retries, there is increased contention and
37
+ # reduced locality, which is still better than alternatives.
38
38
  #
39
- # Per-thread hash codes are initialized to random values.
40
- # Contention and/or table collisions are indicated by failed
41
- # CASes when performing an update operation (see method
42
- # +retry_update+). Upon a collision, if the table size is less than
43
- # the capacity, it is doubled in size unless some other thread
44
- # holds the lock. If a hashed slot is empty, and lock is
45
- # available, a new +Cell+ is created. Otherwise, if the slot
46
- # exists, a CAS is tried. Retries proceed by "double hashing",
47
- # using a secondary hash (XorShift) to try to find a
48
- # free slot.
39
+ # Per-thread hash codes are initialized to random values. Contention and/or
40
+ # table collisions are indicated by failed CASes when performing an update
41
+ # operation (see method +retry_update+). Upon a collision, if the table size
42
+ # is less than the capacity, it is doubled in size unless some other thread
43
+ # holds the lock. If a hashed slot is empty, and lock is available, a new
44
+ # +Cell+ is created. Otherwise, if the slot exists, a CAS is tried. Retries
45
+ # proceed by "double hashing", using a secondary hash (XorShift) to try to
46
+ # find a free slot.
49
47
  #
50
- # The table size is capped because, when there are more threads
51
- # than CPUs, supposing that each thread were bound to a CPU,
52
- # there would exist a perfect hash function mapping threads to
53
- # slots that eliminates collisions. When we reach capacity, we
54
- # search for this mapping by randomly varying the hash codes of
55
- # colliding threads. Because search is random, and collisions
56
- # only become known via CAS failures, convergence can be slow,
57
- # and because threads are typically not bound to CPUS forever,
58
- # may not occur at all. However, despite these limitations,
59
- # observed contention rates are typically low in these cases.
48
+ # The table size is capped because, when there are more threads than CPUs,
49
+ # supposing that each thread were bound to a CPU, there would exist a
50
+ # perfect hash function mapping threads to slots that eliminates collisions.
51
+ # When we reach capacity, we search for this mapping by randomly varying the
52
+ # hash codes of colliding threads. Because search is random, and collisions
53
+ # only become known via CAS failures, convergence can be slow, and because
54
+ # threads are typically not bound to CPUS forever, may not occur at all.
55
+ # However, despite these limitations, observed contention rates are
56
+ # typically low in these cases.
60
57
  #
61
- # It is possible for a +Cell+ to become unused when threads that
62
- # once hashed to it terminate, as well as in the case where
63
- # doubling the table causes no thread to hash to it under
64
- # expanded mask. We do not try to detect or remove such cells,
65
- # under the assumption that for long-running instances, observed
66
- # contention levels will recur, so the cells will eventually be
58
+ # It is possible for a +Cell+ to become unused when threads that once hashed
59
+ # to it terminate, as well as in the case where doubling the table causes no
60
+ # thread to hash to it under expanded mask. We do not try to detect or
61
+ # remove such cells, under the assumption that for long-running instances,
62
+ # observed contention levels will recur, so the cells will eventually be
67
63
  # needed again; and for short-lived ones, it does not matter.
68
64
  class Striped64
69
65
  # Padded variant of AtomicLong supporting only raw accesses plus CAS.
@@ -85,8 +81,8 @@ module ThreadSafe
85
81
 
86
82
  extend Volatile
87
83
  attr_volatile :cells, # Table of cells. When non-null, size is a power of 2.
88
- :base, # Base value, used mainly when there is no contention, but also as a fallback during table initialization races. Updated via CAS.
89
- :busy # Spinlock (locked via CAS) used when resizing and/or creating Cells.
84
+ :base, # Base value, used mainly when there is no contention, but also as a fallback during table initialization races. Updated via CAS.
85
+ :busy # Spinlock (locked via CAS) used when resizing and/or creating Cells.
90
86
 
91
87
  alias_method :busy?, :busy
92
88
 
@@ -1,7 +1,9 @@
1
1
  module ThreadSafe
2
2
  module Util
3
3
  module Volatile
4
- # Provides +volatile+ (in the JVM's sense) attribute accessors implemented atop of the +AtomicReference+s.
4
+ # Provides +volatile+ (in the JVM's sense) attribute accessors implemented
5
+ # atop of the +AtomicReference+s.
6
+ #
5
7
  # Usage:
6
8
  # class Foo
7
9
  # extend ThreadSafe::Util::Volatile
@@ -1,7 +1,9 @@
1
1
  module ThreadSafe
2
2
  module Util
3
- # A xorshift random number (positive +Fixnum+s) generator, provides reasonably cheap way to generate thread local random numbers without contending for
4
- # the global +Kernel.rand+.
3
+ # A xorshift random number (positive +Fixnum+s) generator, provides
4
+ # reasonably cheap way to generate thread local random numbers without
5
+ # contending for the global +Kernel.rand+.
6
+ #
5
7
  # Usage:
6
8
  # x = XorShiftRandom.get # uses Kernel.rand to generate an initial seed
7
9
  # while true
@@ -1,5 +1,5 @@
1
1
  module ThreadSafe
2
- VERSION = "0.3.4"
2
+ VERSION = "0.3.5"
3
3
  end
4
4
 
5
5
  # NOTE: <= 0.2.0 used Threadsafe::VERSION
@@ -0,0 +1,45 @@
1
+ require 'yard'
2
+ YARD::Rake::YardocTask.new
3
+
4
+ root = File.expand_path File.join(File.dirname(__FILE__), '..')
5
+
6
+ namespace :yard do
7
+
8
+ cmd = lambda do |command|
9
+ puts ">> executing: #{command}"
10
+ system command or raise "#{command} failed"
11
+ end
12
+
13
+ desc 'Pushes generated documentation to github pages: http://ruby-concurrency.github.io/thread_safe/'
14
+ task :push => [:setup, :yard] do
15
+
16
+ message = Dir.chdir(root) do
17
+ `git log -n 1 --oneline`.strip
18
+ end
19
+ puts "Generating commit: #{message}"
20
+
21
+ Dir.chdir "#{root}/yardoc" do
22
+ cmd.call "git add -A"
23
+ cmd.call "git commit -m '#{message}'"
24
+ cmd.call 'git push origin gh-pages'
25
+ end
26
+
27
+ end
28
+
29
+ desc 'Setups second clone in ./yardoc dir for pushing doc to github'
30
+ task :setup do
31
+
32
+ unless File.exist? "#{root}/yardoc/.git"
33
+ cmd.call "rm -rf #{root}/yardoc" if File.exist?("#{root}/yardoc")
34
+ Dir.chdir "#{root}" do
35
+ cmd.call 'git clone --single-branch --branch gh-pages git@github.com:ruby-concurrency/thread_safe.git ./yardoc'
36
+ end
37
+ end
38
+ Dir.chdir "#{root}/yardoc" do
39
+ cmd.call 'git fetch origin'
40
+ cmd.call 'git reset --hard origin/gh-pages'
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -4,7 +4,7 @@ require File.join(File.dirname(__FILE__), "test_helper")
4
4
  class TestArray < Minitest::Test
5
5
  def test_concurrency
6
6
  ary = ThreadSafe::Array.new
7
- (1..100).map do |i|
7
+ (1..THREADS).map do |i|
8
8
  Thread.new do
9
9
  1000.times do
10
10
  ary << i
@@ -11,7 +11,7 @@ class TestCache < Minitest::Test
11
11
 
12
12
  def test_concurrency
13
13
  cache = @cache
14
- (1..100).map do |i|
14
+ (1..THREADS).map do |i|
15
15
  Thread.new do
16
16
  1000.times do |j|
17
17
  key = i*1000+j
@@ -4,7 +4,7 @@ require File.join(File.dirname(__FILE__), "test_helper")
4
4
  class TestHash < Minitest::Test
5
5
  def test_concurrency
6
6
  hsh = ThreadSafe::Hash.new
7
- (1..100).map do |i|
7
+ (1..THREADS).map do |i|
8
8
  Thread.new do
9
9
  1000.times do |j|
10
10
  hsh[i*1000+j] = i
@@ -1,14 +1,35 @@
1
- require 'thread'
2
- require 'rubygems'
3
- gem 'minitest', '>= 4'
1
+ unless defined?(JRUBY_VERSION)
2
+ require 'simplecov'
3
+ require 'coveralls'
4
+
5
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
6
+ SimpleCov::Formatter::HTMLFormatter,
7
+ Coveralls::SimpleCov::Formatter
8
+ ]
9
+
10
+ SimpleCov.start do
11
+ project_name 'thread_safe'
12
+
13
+ add_filter '/examples/'
14
+ add_filter '/pkg/'
15
+ add_filter '/test/'
16
+ add_filter '/tasks/'
17
+ add_filter '/yard-template/'
18
+ add_filter '/yardoc/'
19
+
20
+ command_name 'Mintest'
21
+ end
22
+ end
23
+
4
24
  require 'minitest/autorun'
5
25
 
6
- if Minitest.const_defined?('Test')
7
- # We're on Minitest 5+. Nothing to do here.
8
- else
9
- # Minitest 4 doesn't have Minitest::Test yet.
10
- Minitest::Test = MiniTest::Unit::TestCase
11
- end
26
+ require 'minitest/reporters'
27
+ Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new(color: true)
28
+
29
+ require 'thread'
30
+ require 'thread_safe'
31
+
32
+ THREADS = (RUBY_ENGINE == 'ruby' ? 100 : 10)
12
33
 
13
34
  if defined?(JRUBY_VERSION) && ENV['TEST_NO_UNSAFE']
14
35
  # to be used like this: rake test TEST_NO_UNSAFE=true
@@ -7,7 +7,7 @@ Gem::Specification.new do |gem|
7
7
  gem.email = ["headius@headius.com", "thedarkone2@gmail.com"]
8
8
  gem.description = %q{Thread-safe collections and utilities for Ruby}
9
9
  gem.summary = %q{A collection of data structures and utilities to make thread-safe programming in Ruby easier}
10
- gem.homepage = "https://github.com/headius/thread_safe"
10
+ gem.homepage = "https://github.com/ruby-concurrency/thread_safe"
11
11
 
12
12
  gem.files = `git ls-files`.split($\)
13
13
  gem.files += ['lib/thread_safe/jruby_cache_backend.jar'] if defined?(JRUBY_VERSION)
@@ -20,7 +20,7 @@ Gem::Specification.new do |gem|
20
20
  gem.version = ThreadSafe::VERSION
21
21
  gem.license = "Apache-2.0"
22
22
 
23
- gem.add_development_dependency 'atomic', ['>= 1.1.7', '< 2']
23
+ gem.add_development_dependency 'atomic', '= 1.1.16'
24
24
  gem.add_development_dependency 'rake'
25
25
  gem.add_development_dependency 'minitest', '>= 4'
26
26
  end
@@ -0,0 +1,125 @@
1
+ /* Override this file with custom rules */
2
+
3
+ body {
4
+ line-height: 18px;
5
+ }
6
+
7
+ .docstring code, .docstring .object_link a, #filecontents code {
8
+ padding: 0px 3px 1px 3px;
9
+ border: 1px solid #eef;
10
+ background: #f5f5ff;
11
+ }
12
+
13
+ #filecontents pre code, .docstring pre code {
14
+ border: none;
15
+ background: none;
16
+ padding: 0;
17
+ }
18
+
19
+ #filecontents pre.code, .docstring pre.code, .tags pre.example, .docstring code, .docstring .object_link a,
20
+ #filecontents code {
21
+ -moz-border-radius: 2px;
22
+ -webkit-border-radius: 2px;
23
+ }
24
+
25
+ /* syntax highlighting */
26
+ .source_code {
27
+ display: none;
28
+ padding: 3px 8px;
29
+ border-left: 8px solid #ddd;
30
+ margin-top: 5px;
31
+ }
32
+
33
+ #filecontents pre.code, .docstring pre.code, .source_code pre {
34
+ font-family: monospace;
35
+ }
36
+
37
+ #filecontents pre.code, .docstring pre.code {
38
+ display: block;
39
+ }
40
+
41
+ .source_code .lines {
42
+ padding-right: 12px;
43
+ color: #555;
44
+ text-align: right;
45
+ }
46
+
47
+ #filecontents pre.code, .docstring pre.code,
48
+ .tags pre.example {
49
+ padding: 5px 12px;
50
+ margin-top: 4px;
51
+ border: 1px solid #eef;
52
+ background: #f5f5ff;
53
+ }
54
+
55
+ pre.code {
56
+ color: #000;
57
+ }
58
+
59
+ pre.code .info.file {
60
+ color: #555;
61
+ }
62
+
63
+ pre.code .val {
64
+ color: #036A07;
65
+ }
66
+
67
+ pre.code .tstring_content,
68
+ pre.code .heredoc_beg, pre.code .heredoc_end,
69
+ pre.code .qwords_beg, pre.code .qwords_end,
70
+ pre.code .tstring, pre.code .dstring {
71
+ color: #036A07;
72
+ }
73
+
74
+ pre.code .fid,
75
+ pre.code .rubyid_new,
76
+ pre.code .rubyid_to_s,
77
+ pre.code .rubyid_to_sym,
78
+ pre.code .rubyid_to_f,
79
+ pre.code .rubyid_to_i,
80
+ pre.code .rubyid_each {
81
+ color: inherit;
82
+ }
83
+
84
+ pre.code .comment {
85
+ color: #777;
86
+ font-style: italic;
87
+ }
88
+
89
+ pre.code .const, pre.code .constant {
90
+ color: inherit;
91
+ font-weight: bold;
92
+ font-style: italic;
93
+ }
94
+
95
+ pre.code .label,
96
+ pre.code .symbol {
97
+ color: #C5060B;
98
+ }
99
+
100
+ pre.code .kw,
101
+ pre.code .rubyid_require,
102
+ pre.code .rubyid_extend,
103
+ pre.code .rubyid_include,
104
+ pre.code .int {
105
+ color: #0000FF;
106
+ }
107
+
108
+ pre.code .ivar {
109
+ color: #660E7A;
110
+ }
111
+
112
+ pre.code .gvar,
113
+ pre.code .rubyid_backref,
114
+ pre.code .rubyid_nth_ref {
115
+ color: #6D79DE;
116
+ }
117
+
118
+ pre.code .regexp, .dregexp {
119
+ color: #036A07;
120
+ }
121
+
122
+ pre.code a {
123
+ border-bottom: 1px dotted #bbf;
124
+ }
125
+
@@ -0,0 +1,16 @@
1
+ <div id="footer">
2
+ Generated on <%= Time.now.strftime("%c") %> by
3
+ <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
4
+ <%= YARD::VERSION %> (ruby-<%= RUBY_VERSION %>).
5
+ </div>
6
+
7
+ <script>
8
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
9
+ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
10
+ m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
11
+ })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
12
+
13
+ ga('create', 'UA-57940973-1', 'auto');
14
+ ga('send', 'pageview');
15
+
16
+ </script>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thread_safe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: java
6
6
  authors:
7
7
  - Charles Oliver Nutter
@@ -9,26 +9,20 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-05-27 00:00:00.000000000 Z
12
+ date: 2015-03-11 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: atomic
16
16
  version_requirements: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - '>='
19
- - !ruby/object:Gem::Version
20
- version: 1.1.7
21
- - - <
18
+ - - '='
22
19
  - !ruby/object:Gem::Version
23
- version: '2'
20
+ version: 1.1.16
24
21
  requirement: !ruby/object:Gem::Requirement
25
22
  requirements:
26
- - - '>='
27
- - !ruby/object:Gem::Version
28
- version: 1.1.7
29
- - - <
23
+ - - '='
30
24
  - !ruby/object:Gem::Version
31
- version: '2'
25
+ version: 1.1.16
32
26
  prerelease: false
33
27
  type: :development
34
28
  - !ruby/object:Gem::Dependency
@@ -68,6 +62,7 @@ extensions: []
68
62
  extra_rdoc_files: []
69
63
  files:
70
64
  - .travis.yml
65
+ - .yardopts
71
66
  - Gemfile
72
67
  - LICENSE
73
68
  - README.md
@@ -101,6 +96,7 @@ files:
101
96
  - lib/thread_safe/util/volatile_tuple.rb
102
97
  - lib/thread_safe/util/xor_shift_random.rb
103
98
  - lib/thread_safe/version.rb
99
+ - tasks/update_doc.rake
104
100
  - test/src/thread_safe/SecurityManager.java
105
101
  - test/test_array.rb
106
102
  - test/test_cache.rb
@@ -109,7 +105,9 @@ files:
109
105
  - test/test_helper.rb
110
106
  - test/test_synchronized_delegator.rb
111
107
  - thread_safe.gemspec
112
- homepage: https://github.com/headius/thread_safe
108
+ - yard-template/default/fulldoc/html/css/common.css
109
+ - yard-template/default/layout/html/footer.erb
110
+ homepage: https://github.com/ruby-concurrency/thread_safe
113
111
  licenses:
114
112
  - Apache-2.0
115
113
  metadata: {}
@@ -129,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
127
  version: '0'
130
128
  requirements: []
131
129
  rubyforge_project:
132
- rubygems_version: 2.2.2
130
+ rubygems_version: 2.4.5
133
131
  signing_key:
134
132
  specification_version: 4
135
133
  summary: A collection of data structures and utilities to make thread-safe programming in Ruby easier
@@ -141,3 +139,4 @@ test_files:
141
139
  - test/test_hash.rb
142
140
  - test/test_helper.rb
143
141
  - test/test_synchronized_delegator.rb
142
+ has_rdoc: