bosh_common 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1 @@
1
+ BOSH common / shared classes
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ $:.unshift(File.expand_path("../../rake", __FILE__))
4
+
5
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __FILE__)
6
+
7
+ require "rubygems"
8
+ require "bundler"
9
+ Bundler.setup(:default, :test)
10
+
11
+ require "rake"
12
+ begin
13
+ require "rspec/core/rake_task"
14
+ rescue LoadError
15
+ end
16
+
17
+ require "bundler_task"
18
+ require "ci_task"
19
+
20
+ gem_helper = Bundler::GemHelper.new(Dir.pwd)
21
+
22
+ desc "Build Blobstore Client gem into the pkg directory"
23
+ task "build" do
24
+ gem_helper.build_gem
25
+ end
26
+
27
+ desc "Build and install Blobstore Client into system gems"
28
+ task "install" do
29
+ sh("bundle install --local --without test development")
30
+ gem_helper.install_gem
31
+ end
32
+
33
+ BundlerTask.new
34
+
35
+ if defined?(RSpec)
36
+ namespace :spec do
37
+ desc "Run Unit Tests"
38
+ rspec_task = RSpec::Core::RakeTask.new(:unit) do |t|
39
+ t.gemfile = "Gemfile"
40
+ t.pattern = "spec/unit/**/*_spec.rb"
41
+ t.rspec_opts = %w(--format progress --colour)
42
+ end
43
+
44
+ CiTask.new do |task|
45
+ task.rspec_task = rspec_task
46
+ end
47
+ end
48
+
49
+ desc "Install dependencies and run tests"
50
+ task :spec => %w(bundler:install:test spec:unit)
51
+ end
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ class ThreadFormatter
4
+ FORMAT = "%s, [%s#%d] [%s] %5s -- %s: %s\n"
5
+
6
+ attr_accessor :datetime_format
7
+
8
+ def initialize
9
+ @datetime_format = nil
10
+ end
11
+
12
+ def call(severity, time, progname, msg)
13
+ thread_name = Thread.current[:name] || "0x#{Thread.current.object_id.to_s(16)}"
14
+ FORMAT % [severity[0..0], format_datetime(time), $$, thread_name, severity, progname,
15
+ msg2str(msg)]
16
+ end
17
+
18
+ private
19
+
20
+ def format_datetime(time)
21
+ if @datetime_format.nil?
22
+ time.strftime("%Y-%m-%dT%H:%M:%S.") << "%06d " % time.usec
23
+ else
24
+ time.strftime(@datetime_format)
25
+ end
26
+ end
27
+
28
+ def msg2str(msg)
29
+ case msg
30
+ when ::String
31
+ msg
32
+ when ::Exception
33
+ "#{ msg.message } (#{ msg.class })\n" <<
34
+ (msg.backtrace || []).join("\n")
35
+ else
36
+ msg.inspect
37
+ end
38
+ end
39
+ end
40
+
41
+ module Kernel
42
+
43
+ def with_thread_name(name)
44
+ old_name = Thread.current[:name]
45
+ Thread.current[:name] = name
46
+ yield
47
+ ensure
48
+ Thread.current[:name] = old_name
49
+ end
50
+
51
+ end
@@ -0,0 +1,130 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "logger"
4
+
5
+ module Bosh
6
+
7
+ class ThreadPool
8
+
9
+ def initialize(options = {})
10
+ @actions = []
11
+ @lock = Mutex.new
12
+ @cv = ConditionVariable.new
13
+ @max_threads = options[:max_threads] || 1
14
+ @available_threads = @max_threads
15
+ @logger = options[:logger]
16
+ @boom = nil
17
+ @original_thread = Thread.current
18
+ @threads = []
19
+ @state = :open
20
+ end
21
+
22
+ def wrap
23
+ begin
24
+ yield self
25
+ wait
26
+ ensure
27
+ shutdown
28
+ end
29
+ end
30
+
31
+ def pause
32
+ @lock.synchronize do
33
+ @state = :paused
34
+ end
35
+ end
36
+
37
+ def resume
38
+ @lock.synchronize do
39
+ @state = :open
40
+ [@available_threads, @actions.size].min.times do
41
+ @available_threads -= 1
42
+ create_thread
43
+ end
44
+ end
45
+ end
46
+
47
+ def process(&block)
48
+ @lock.synchronize do
49
+ @actions << block
50
+ if @state == :open
51
+ if @available_threads > 0
52
+ @logger.debug("Creating new thread")
53
+ @available_threads -= 1
54
+ create_thread
55
+ else
56
+ @logger.debug("All threads are currently busy, queuing action")
57
+ end
58
+ elsif @state == :paused
59
+ @logger.debug("Pool is paused, queueing action.")
60
+ end
61
+ end
62
+ end
63
+
64
+ def create_thread
65
+ thread = Thread.new do
66
+ begin
67
+ loop do
68
+ action = nil
69
+ @lock.synchronize do
70
+ action = @actions.shift unless @boom
71
+ if action
72
+ @logger.debug("Found an action that needs to be processed")
73
+ else
74
+ @logger.debug("Thread is no longer needed, cleaning up")
75
+ @available_threads += 1
76
+ @threads.delete(thread) if @state == :open
77
+ end
78
+ end
79
+
80
+ break unless action
81
+
82
+ begin
83
+ action.call
84
+ rescue Exception => e
85
+ raise_worker_exception(e)
86
+ end
87
+ end
88
+ end
89
+ @lock.synchronize { @cv.signal unless working? }
90
+ end
91
+ @threads << thread
92
+ end
93
+
94
+ def raise_worker_exception(exception)
95
+ if exception.respond_to?(:backtrace)
96
+ @logger.debug("Worker thread raised exception: #{exception} - #{exception.backtrace.join("\n")}")
97
+ else
98
+ @logger.debug("Worker thread raised exception: #{exception}")
99
+ end
100
+ @lock.synchronize do
101
+ @boom = exception if @boom.nil?
102
+ end
103
+ end
104
+
105
+ def working?
106
+ @boom.nil? && (@available_threads != @max_threads || !@actions.empty?)
107
+ end
108
+
109
+ def wait
110
+ @logger.debug("Waiting for tasks to complete")
111
+ @lock.synchronize do
112
+ @cv.wait(@lock) while working?
113
+ raise @boom if @boom
114
+ end
115
+ end
116
+
117
+ def shutdown
118
+ return if @state == :closed
119
+ @logger.debug("Shutting down pool")
120
+ @lock.synchronize do
121
+ return if @state == :closed
122
+ @state = :closed
123
+ @actions.clear
124
+ end
125
+ @threads.each { |t| t.join }
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module Common
5
+ VERSION = "0.4.0"
6
+ end
7
+ end
data/spec/Rakefile ADDED
@@ -0,0 +1,87 @@
1
+ require "tempfile"
2
+ require "rake"
3
+
4
+ APP_DIR = File.expand_path(File.join("..", ".."), __FILE__)
5
+ ENV["BUNDLE_GEMFILE"] ||= File.join(APP_DIR, "Gemfile")
6
+ require "rubygems"
7
+ require "bundler"
8
+ Bundler.setup(:default, :test)
9
+
10
+ require "rspec/core/rake_task"
11
+ require "ci/reporter/rake/rspec"
12
+
13
+ desc "Run all examples"
14
+ RSpec::Core::RakeTask.new(:spec) do |t|
15
+ t.pattern = "**/*_spec.rb"
16
+ t.rspec_opts = %w[--color]
17
+ end
18
+
19
+ task :default => [:spec]
20
+
21
+ coverage_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_coverage"))
22
+ reports_dir = File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_reports"))
23
+ dump_file = File.join(Dir.tmpdir, "bosh-common.rcov")
24
+
25
+ ENV["CI_REPORTS"] = reports_dir
26
+
27
+ namespace "spec" do
28
+ gemfile = "../Gemfile"
29
+ spec_opts = ["--format", "documentation", "--colour"]
30
+
31
+ if RUBY_VERSION < "1.9"
32
+ desc "Run specs for ci"
33
+ task "ci" => [ "ci:setup:rspec", "spec:rcov", "convert_rcov_to_clover" ]
34
+
35
+ desc "Run spec with coverage"
36
+ RSpec::Core::RakeTask.new("rcov") do |t|
37
+ FileUtils.rm_rf(dump_file)
38
+ t.gemfile = gemfile
39
+ t.pattern = "**/*_spec.rb"
40
+ t.rspec_opts = ["--format", "progress", "--colour"]
41
+ t.rcov = true
42
+ t.rcov_opts = %W{--aggregate #{dump_file} --exclude osx\/objc,gems\/,spec\/,unit\/,features\/ -o "#{coverage_dir}"}
43
+ end
44
+
45
+ task "convert_rcov_to_clover" do |t|
46
+ ignore_pattern = "spec,[.]bundle,[/]gems[/]"
47
+ clover_output = File.join(coverage_dir, "clover.xml")
48
+
49
+ sh("bundle exec rcov_analyzer #{dump_file} #{ignore_pattern} > #{clover_output}")
50
+ FileUtils.rm_rf(dump_file)
51
+ end
52
+
53
+ else
54
+ desc "Run specs for ci"
55
+ task "ci" => [ "ci:setup:rspec", "spec:rcov" ]
56
+
57
+ desc "Run spec with coverage"
58
+ task :rcov => :cleanup_coverage do
59
+ require "simplecov"
60
+ require "simplecov-rcov"
61
+ require "simplecov-clover"
62
+
63
+ class SimpleCov::Formatter::CombinedFormatter
64
+ def format(result)
65
+ SimpleCov::Formatter::CloverFormatter.new.format(result)
66
+ SimpleCov::Formatter::RcovFormatter.new.format(result)
67
+ end
68
+ end
69
+
70
+ SimpleCov.formatter = SimpleCov::Formatter::CombinedFormatter
71
+ SimpleCov.root('..')
72
+ SimpleCov.coverage_dir('cov')
73
+ SimpleCov.start do
74
+ require "rspec/core"
75
+ add_filter "/spec/"
76
+ spec_dir = File.expand_path("..", __FILE__)
77
+ RSpec::Core::Runner.disable_autorun!
78
+ RSpec::Core::Runner.run([spec_dir], STDERR, STDOUT)
79
+ end
80
+ end
81
+ end
82
+
83
+ task "cleanup_coverage" do
84
+ rm_rf "cov"
85
+ end
86
+
87
+ end
@@ -0,0 +1,8 @@
1
+ module Bosh
2
+ module Clouds
3
+ class Spec
4
+ def initialize(options)
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'rspec/core'
2
+
3
+ $:.unshift(File.expand_path("../../lib", __FILE__))
4
+
5
+ RSpec.configure do |c|
6
+ c.color_enabled = true
7
+ end
@@ -0,0 +1,69 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require File.expand_path("../../spec_helper", __FILE__)
4
+
5
+ require "common/thread_pool"
6
+
7
+ describe Bosh::ThreadPool do
8
+
9
+ before(:all) do
10
+ @logger = Logger.new(STDOUT)
11
+ @logger.level = Logger::INFO
12
+ end
13
+
14
+ it "should respect max threads" do
15
+ max = 0
16
+ current = 0
17
+ lock = Mutex.new
18
+
19
+ Bosh::ThreadPool.new(:max_threads => 2, :logger => @logger).wrap do |pool|
20
+ 4.times do
21
+ pool.process do
22
+ lock.synchronize do
23
+ current += 1
24
+ max = current if current > max
25
+ end
26
+ sleep(0.050)
27
+ lock.synchronize do
28
+ max = current if current > max
29
+ current -= 1
30
+ end
31
+ end
32
+ end
33
+ end
34
+ max.should be <= 2
35
+ end
36
+
37
+ it "should raise exceptions" do
38
+ lambda {
39
+ Bosh::ThreadPool.new(:max_threads => 2, :logger => @logger).wrap do |pool|
40
+ 5.times do |index|
41
+ pool.process do
42
+ sleep(0.050)
43
+ raise "bad" if index == 4
44
+ end
45
+ end
46
+ end
47
+ }.should raise_exception("bad")
48
+ end
49
+
50
+ it "should stop processing new work when there was an exception" do
51
+ max = 0
52
+ lock = Mutex.new
53
+
54
+ lambda {
55
+ Bosh::ThreadPool.new(:max_threads => 1, :logger => @logger).wrap do |pool|
56
+ 10.times do |index|
57
+ pool.process do
58
+ lock.synchronize { max = index if index > max }
59
+ sleep(0.050)
60
+ raise "bad" if index == 4
61
+ end
62
+ end
63
+ end
64
+ }.should raise_exception("bad")
65
+
66
+ max.should be == 4
67
+ end
68
+
69
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bosh_common
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - VMware
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-22 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &2156296060 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2156296060
25
+ description: Bosh common
26
+ email: support@vmware.com
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files: []
30
+ files:
31
+ - README
32
+ - Rakefile
33
+ - lib/common/thread_formatter.rb
34
+ - lib/common/thread_pool.rb
35
+ - lib/common/version.rb
36
+ - spec/Rakefile
37
+ - spec/lib/cloud/spec.rb
38
+ - spec/spec_helper.rb
39
+ - spec/unit/thread_pool_spec.rb
40
+ homepage: http://www.vmware.com
41
+ licenses: []
42
+ post_install_message:
43
+ rdoc_options: []
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ segments:
53
+ - 0
54
+ hash: -472840810854006979
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ segments:
62
+ - 0
63
+ hash: -472840810854006979
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.10
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Bosh common
70
+ test_files:
71
+ - spec/Rakefile
72
+ - spec/lib/cloud/spec.rb
73
+ - spec/spec_helper.rb
74
+ - spec/unit/thread_pool_spec.rb