thread_safe 0.3.3 → 0.3.4

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: f01ddce23e913574f7d1b43fb6307097a944bd80
4
- data.tar.gz: c961292af3ebf3409fd37481c705c6c509f1da8a
3
+ metadata.gz: ae6423a7b9dcc55d72b4902edbe19f7250be3e7a
4
+ data.tar.gz: 6ebfedec76eb35f880df879162da2cdcd5690882
5
5
  SHA512:
6
- metadata.gz: 061a7b8615337b3db8a5ce99889c95b3dade77cfa151c6eed42d672593771e7814d5350009438b5b76fb0e2ca2e155b3e185417766673c5ae4c08da8646d8e52
7
- data.tar.gz: f751f6900efa28f043e0bca2db507cd70f215ad2e241c646c9b14dd7ca75eaf0a8b12000e966ae1e4cbe0cf3ac454534ed79ca4c2b3f6fdd0117e5a2da69ec8f
6
+ metadata.gz: 54b9546b99a8ca9414f17d1e4f210c5b4938028df6dcf8f43ffdcbb51e25b5cd2dea3e7dbc36e8bb8aabc0ec48845105f9d2b1ff42b2334ad8332f1d4b656ec2
7
+ data.tar.gz: 1b681c10cd317386b4f49668433889f7e8962eac41a7b8e2be208ac07d5c3758b2ef1c0a278095aa8a4bbb901ed7d071ed9887892fdc463cd864915aa2e3ae26
@@ -35,9 +35,13 @@ module ThreadSafe
35
35
  end
36
36
 
37
37
  def [](key)
38
- if value = super
38
+ if value = super # non-falsy value is an existing mapping, return it right away
39
39
  value
40
- elsif @default_proc && !key?(key)
40
+ # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
41
+ # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
42
+ # would be returned)
43
+ # note: nil == value check is not technically necessary
44
+ elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
41
45
  @default_proc.call(self, key)
42
46
  else
43
47
  value
@@ -55,7 +59,13 @@ module ThreadSafe
55
59
  elsif NULL != default_value
56
60
  default_value
57
61
  else
58
- raise KEY_ERROR, 'key not found'
62
+ raise_fetch_no_key
63
+ end
64
+ end
65
+
66
+ def fetch_or_store(key, default_value = NULL)
67
+ fetch(key) do
68
+ put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
59
69
  end
60
70
  end
61
71
 
@@ -127,6 +137,10 @@ module ThreadSafe
127
137
  undef :freeze
128
138
 
129
139
  private
140
+ def raise_fetch_no_key
141
+ raise KEY_ERROR, 'key not found'
142
+ end
143
+
130
144
  def initialize_copy(other)
131
145
  super
132
146
  populate_from(other)
@@ -1,5 +1,5 @@
1
1
  module ThreadSafe
2
- VERSION = "0.3.3"
2
+ VERSION = "0.3.4"
3
3
  end
4
4
 
5
5
  # NOTE: <= 0.2.0 used Threadsafe::VERSION
data/test/test_array.rb CHANGED
@@ -1,20 +1,18 @@
1
- require 'test/unit'
2
1
  require 'thread_safe'
2
+ require File.join(File.dirname(__FILE__), "test_helper")
3
3
 
4
- class TestArray < Test::Unit::TestCase
4
+ class TestArray < Minitest::Test
5
5
  def test_concurrency
6
6
  ary = ThreadSafe::Array.new
7
- assert_nothing_raised do
8
- (1..100).map do |i|
9
- Thread.new do
10
- 1000.times do
11
- ary << i
12
- ary.each {|x| x * 2}
13
- ary.shift
14
- ary.last
15
- end
7
+ (1..100).map do |i|
8
+ Thread.new do
9
+ 1000.times do
10
+ ary << i
11
+ ary.each {|x| x * 2}
12
+ ary.shift
13
+ ary.last
16
14
  end
17
- end.map(&:join)
18
- end
15
+ end
16
+ end.map(&:join)
19
17
  end
20
18
  end
data/test/test_cache.rb CHANGED
@@ -1,29 +1,26 @@
1
- require 'test/unit'
2
1
  require 'thread_safe'
3
2
  require 'thread'
4
3
  require File.join(File.dirname(__FILE__), "test_helper")
5
4
 
6
5
  Thread.abort_on_exception = true
7
6
 
8
- class TestCache < Test::Unit::TestCase
7
+ class TestCache < Minitest::Test
9
8
  def setup
10
9
  @cache = ThreadSafe::Cache.new
11
10
  end
12
11
 
13
12
  def test_concurrency
14
13
  cache = @cache
15
- assert_nothing_raised do
16
- (1..100).map do |i|
17
- Thread.new do
18
- 1000.times do |j|
19
- key = i*1000+j
20
- cache[key] = i
21
- cache[key]
22
- cache.delete(key)
23
- end
14
+ (1..100).map do |i|
15
+ Thread.new do
16
+ 1000.times do |j|
17
+ key = i*1000+j
18
+ cache[key] = i
19
+ cache[key]
20
+ cache.delete(key)
24
21
  end
25
- end.map(&:join)
26
- end
22
+ end
23
+ end.map(&:join)
27
24
  end
28
25
 
29
26
  def test_retrieval
@@ -470,9 +467,14 @@ class TestCache < Test::Unit::TestCase
470
467
  assert_equal(1, (@cache.fetch(:a) {flunk}))
471
468
  end
472
469
 
473
- assert_raise(ThreadSafe::Cache::KEY_ERROR) do
470
+ assert_raises(ThreadSafe::Cache::KEY_ERROR) do
474
471
  @cache.fetch(:b)
475
472
  end
473
+
474
+ assert_no_size_change do
475
+ assert_equal 1, (@cache.fetch(:b, :c) {1}) # assert block supersedes default value argument
476
+ assert_equal false, @cache.key?(:b)
477
+ end
476
478
  end
477
479
  end
478
480
 
@@ -508,6 +510,86 @@ class TestCache < Test::Unit::TestCase
508
510
  end
509
511
  end
510
512
 
513
+ def test_fetch_or_store
514
+ with_or_without_default_proc do |default_proc_set|
515
+ assert_size_change 1 do
516
+ assert_equal 1, @cache.fetch_or_store(:a, 1)
517
+ assert_equal 1, @cache[:a]
518
+ end
519
+
520
+ @cache.delete(:a)
521
+
522
+ assert_size_change 1 do
523
+ assert_equal 1, (@cache.fetch_or_store(:a) {1})
524
+ assert_equal 1, @cache[:a]
525
+ end
526
+
527
+ assert_no_size_change do
528
+ assert_equal(1, (@cache.fetch_or_store(:a) {flunk}))
529
+ end
530
+
531
+ assert_raises(ThreadSafe::Cache::KEY_ERROR) do
532
+ @cache.fetch_or_store(:b)
533
+ end
534
+
535
+ assert_size_change 1 do
536
+ assert_equal 1, (@cache.fetch_or_store(:b, :c) {1}) # assert block supersedes default value argument
537
+ assert_equal 1, @cache[:b]
538
+ end
539
+ end
540
+ end
541
+
542
+ def test_falsy_fetch_or_store
543
+ with_or_without_default_proc do
544
+ assert_equal false, @cache.key?(:a)
545
+
546
+ assert_size_change 1 do
547
+ assert_equal(nil, @cache.fetch_or_store(:a, nil))
548
+ assert_equal nil, @cache[:a]
549
+ assert_equal true, @cache.key?(:a)
550
+ end
551
+ @cache.delete(:a)
552
+
553
+ assert_size_change 1 do
554
+ assert_equal(false, @cache.fetch_or_store(:a, false))
555
+ assert_equal false, @cache[:a]
556
+ assert_equal true, @cache.key?(:a)
557
+ end
558
+ @cache.delete(:a)
559
+
560
+ assert_size_change 1 do
561
+ assert_equal(nil, (@cache.fetch_or_store(:a) {}))
562
+ assert_equal nil, @cache[:a]
563
+ assert_equal true, @cache.key?(:a)
564
+ end
565
+ @cache.delete(:a)
566
+
567
+ assert_size_change 1 do
568
+ assert_equal(false, (@cache.fetch_or_store(:a) {false}))
569
+ assert_equal false, @cache[:a]
570
+ assert_equal true, @cache.key?(:a)
571
+ end
572
+
573
+ @cache[:a] = nil
574
+ assert_no_size_change do
575
+ assert_equal(nil, (@cache.fetch_or_store(:a) {flunk}))
576
+ end
577
+ end
578
+ end
579
+
580
+ def test_fetch_or_store_with_return
581
+ with_or_without_default_proc do
582
+ r = lambda do
583
+ @cache.fetch_or_store(:a) { return 10 }
584
+ end.call
585
+
586
+ assert_no_size_change do
587
+ assert_equal 10, r
588
+ assert_equal false, @cache.key?(:a)
589
+ end
590
+ end
591
+ end
592
+
511
593
  def test_clear
512
594
  @cache[:a] = 1
513
595
  assert_size_change -1 do
@@ -554,11 +636,9 @@ class TestCache < Test::Unit::TestCase
554
636
  @cache[:b] = 1
555
637
  @cache[:c] = 1
556
638
 
557
- assert_nothing_raised do
558
- assert_size_change 1 do
559
- @cache.each_pair do |k, v|
560
- @cache[:z] = 1
561
- end
639
+ assert_size_change 1 do
640
+ @cache.each_pair do |k, v|
641
+ @cache[:z] = 1
562
642
  end
563
643
  end
564
644
  end
@@ -704,14 +784,13 @@ class TestCache < Test::Unit::TestCase
704
784
  end
705
785
 
706
786
  def test_is_unfreezable
707
- assert_raise(NoMethodError) { @cache.freeze }
787
+ assert_raises(NoMethodError) { @cache.freeze }
708
788
  end
709
789
 
710
790
  def test_marshal_dump_load
711
- assert_nothing_raised do
712
- new_cache = Marshal.load(Marshal.dump(@cache))
713
- assert_equal 0, new_cache.size
714
- end
791
+ new_cache = Marshal.load(Marshal.dump(@cache))
792
+ assert_instance_of ThreadSafe::Cache, new_cache
793
+ assert_equal 0, new_cache.size
715
794
  @cache[:a] = 1
716
795
  new_cache = Marshal.load(Marshal.dump(@cache))
717
796
  assert_equal 1, @cache[:a]
@@ -719,7 +798,7 @@ class TestCache < Test::Unit::TestCase
719
798
  end
720
799
 
721
800
  def test_marshal_dump_doesnt_work_with_default_proc
722
- assert_raise(TypeError) do
801
+ assert_raises(TypeError) do
723
802
  Marshal.dump(ThreadSafe::Cache.new {})
724
803
  end
725
804
  end
@@ -740,7 +819,8 @@ class TestCache < Test::Unit::TestCase
740
819
  end
741
820
 
742
821
  def assert_valid_options(options)
743
- assert_nothing_raised { ThreadSafe::Cache.new(options) }
822
+ c = ThreadSafe::Cache.new(options)
823
+ assert_instance_of ThreadSafe::Cache, c
744
824
  end
745
825
 
746
826
  def assert_invalid_option(option_name, value)
@@ -748,7 +828,7 @@ class TestCache < Test::Unit::TestCase
748
828
  end
749
829
 
750
830
  def assert_invalid_options(options)
751
- assert_raise(ArgumentError) { ThreadSafe::Cache.new(options) }
831
+ assert_raises(ArgumentError) { ThreadSafe::Cache.new(options) }
752
832
  end
753
833
 
754
834
  def assert_size_change(change, cache = @cache)
@@ -782,7 +862,7 @@ class TestCache < Test::Unit::TestCase
782
862
  before_had_value = before_had_key ? @cache[key] : nil
783
863
 
784
864
  assert_no_size_change do
785
- assert_raise(TestException) do
865
+ assert_raises(TestException) do
786
866
  @cache.send(method, key, *args) { raise TestException, '' }
787
867
  end
788
868
  assert_equal before_had_key, @cache.key?(key)
@@ -1,11 +1,10 @@
1
1
  require 'thread'
2
- require 'test/unit'
3
2
  require 'thread_safe'
4
3
  require File.join(File.dirname(__FILE__), "test_helper")
5
4
 
6
5
  Thread.abort_on_exception = true
7
6
 
8
- class TestCacheTorture < Test::Unit::TestCase # this is not run unless RUBY_VERSION =~ /1\.8/ || ENV['TRAVIS'] (see the end of the file)
7
+ class TestCacheTorture < Minitest::Test # this is not run unless RUBY_VERSION =~ /1\.8/ || ENV['TRAVIS'] (see the end of the file)
9
8
  THREAD_COUNT = 40
10
9
  KEY_COUNT = (((2**13) - 2) * 0.75).to_i # get close to the doubling cliff
11
10
  LOW_KEY_COUNT = (((2**8 ) - 2) * 0.75).to_i # get close to the doubling cliff
@@ -310,15 +309,13 @@ class TestCacheTorture < Test::Unit::TestCase # this is not run unless RUBY_VERS
310
309
  def do_thread_loop(name, code, options = {}, &block)
311
310
  options = DEFAULTS.merge(options)
312
311
  meth = define_loop name, code, options[:prelude]
313
- assert_nothing_raised do
314
- keys = to_keys_array(options[:key_count])
315
- run_thread_loop(meth, keys, options, &block)
316
-
317
- if options[:key_count] > 1
318
- options[:key_count] = (options[:key_count] / 40).to_i
319
- keys = to_hash_collision_keys_array(options[:key_count])
320
- run_thread_loop(meth, keys, options.merge(:loop_count => (options[:loop_count] * 5)), &block)
321
- end
312
+ keys = to_keys_array(options[:key_count])
313
+ run_thread_loop(meth, keys, options, &block)
314
+
315
+ if options[:key_count] > 1
316
+ options[:key_count] = (options[:key_count] / 40).to_i
317
+ keys = to_hash_collision_keys_array(options[:key_count])
318
+ run_thread_loop(meth, keys, options.merge(:loop_count => (options[:loop_count] * 5)), &block)
322
319
  end
323
320
  end
324
321
 
data/test/test_hash.rb CHANGED
@@ -1,19 +1,17 @@
1
- require 'test/unit'
2
1
  require 'thread_safe'
2
+ require File.join(File.dirname(__FILE__), "test_helper")
3
3
 
4
- class TestHash < Test::Unit::TestCase
4
+ class TestHash < Minitest::Test
5
5
  def test_concurrency
6
6
  hsh = ThreadSafe::Hash.new
7
- assert_nothing_raised do
8
- (1..100).map do |i|
9
- Thread.new do
10
- 1000.times do |j|
11
- hsh[i*1000+j] = i
12
- hsh[i*1000+j]
13
- hsh.delete(i*1000+j)
14
- end
7
+ (1..100).map do |i|
8
+ Thread.new do
9
+ 1000.times do |j|
10
+ hsh[i*1000+j] = i
11
+ hsh[i*1000+j]
12
+ hsh.delete(i*1000+j)
15
13
  end
16
- end.map(&:join)
17
- end
14
+ end
15
+ end.map(&:join)
18
16
  end
19
17
  end
data/test/test_helper.rb CHANGED
@@ -1,4 +1,14 @@
1
1
  require 'thread'
2
+ require 'rubygems'
3
+ gem 'minitest', '>= 4'
4
+ require 'minitest/autorun'
5
+
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
2
12
 
3
13
  if defined?(JRUBY_VERSION) && ENV['TEST_NO_UNSAFE']
4
14
  # to be used like this: rake test TEST_NO_UNSAFE=true
@@ -10,7 +20,7 @@ if defined?(JRUBY_VERSION) && ENV['TEST_NO_UNSAFE']
10
20
  manager.deny java.lang.RuntimePermission.new("accessClassInPackage.sun.misc")
11
21
  java.lang.System.setSecurityManager manager
12
22
 
13
- class TestNoUnsafe < Test::Unit::TestCase
23
+ class TestNoUnsafe < Minitest::Test
14
24
  def test_security_manager_is_used
15
25
  begin
16
26
  java_import 'sun.misc.Unsafe'
@@ -1,7 +1,7 @@
1
- require 'test/unit'
2
1
  require 'thread_safe/synchronized_delegator.rb'
2
+ require File.join(File.dirname(__FILE__), "test_helper")
3
3
 
4
- class TestSynchronizedDelegator < Test::Unit::TestCase
4
+ class TestSynchronizedDelegator < Minitest::Test
5
5
 
6
6
  def test_wraps_array
7
7
  sync_array = SynchronizedDelegator.new(array = [])
data/thread_safe.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |gem|
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)
14
+ gem.files -= ['.gitignore'] # see https://github.com/headius/thread_safe/issues/40#issuecomment-42315441
14
15
  gem.platform = 'java' if defined?(JRUBY_VERSION)
15
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
16
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
@@ -21,4 +22,5 @@ Gem::Specification.new do |gem|
21
22
 
22
23
  gem.add_development_dependency 'atomic', ['>= 1.1.7', '< 2']
23
24
  gem.add_development_dependency 'rake'
25
+ gem.add_development_dependency 'minitest', '>= 4'
24
26
  end
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.3
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Charles Oliver Nutter
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-04-07 00:00:00.000000000 Z
12
+ date: 2014-05-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: atomic
@@ -45,6 +45,20 @@ dependencies:
45
45
  - - '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
+ - !ruby/object:Gem::Dependency
49
+ name: minitest
50
+ requirement: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '4'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '4'
48
62
  description: Thread-safe collections and utilities for Ruby
49
63
  email:
50
64
  - headius@headius.com
@@ -53,7 +67,6 @@ executables: []
53
67
  extensions: []
54
68
  extra_rdoc_files: []
55
69
  files:
56
- - .gitignore
57
70
  - .travis.yml
58
71
  - Gemfile
59
72
  - LICENSE
data/.gitignore DELETED
@@ -1,22 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .rbx/*
4
- .bundle
5
- .config
6
- .yardoc
7
- Gemfile.lock
8
- InstalledFiles
9
- _yardoc
10
- coverage
11
- doc/
12
- lib/bundler/man
13
- lib/thread_safe/jruby_cache_backend.jar
14
- pkg
15
- rdoc
16
- spec/reports
17
- test/tmp
18
- test/version_tmp
19
- tmp
20
- .DS_Store
21
- *.swp
22
- test/package.jar