thread_safe 0.3.4-java → 0.3.5-java

Sign up to get free protection for your applications and to get access to all the features.
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: