clean_thread 1.0.0

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.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in clean_thread.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Infonium Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/NEWS.rdoc ADDED
@@ -0,0 +1,29 @@
1
+ = Release Notes
2
+
3
+ == 1.0.0 - 2011-06-28 - Dwayne Litzenberger
4
+
5
+ - First public release
6
+
7
+ == 0.5 - 2011-04-04 - Dwayne Litzenberger
8
+
9
+ - Set the Java thread name (when running under JRuby). This makes debugging
10
+ with jconsole/JMX easier.
11
+
12
+ == 0.4 - 2011-02-26 - Dwayne Litzenberger
13
+
14
+ - Fix bug where ThreadFinish was raised when a thread invoked its own
15
+ CleanThread#finish method with :nowait=>true.
16
+
17
+ == 0.3 - 2011-02-24 - Dwayne Litzenberger
18
+
19
+ - Fix bug where CleanThread#alive? would raise an exception if the thread was
20
+ not yet started.
21
+
22
+ == 0.2 - 2010-10-21 - Dwayne Litzenberger
23
+
24
+ - Removed references to HospitalPortal::Database, use ActiveRecord::Base instead.
25
+ - Add display_exception() method, which outputs exceptions to $stderr by default.
26
+
27
+ == 0.1 - 2010-02-10 - John Duff
28
+
29
+ - Removed dependency on HospitalPortal::Database and Java
data/README.rdoc ADDED
@@ -0,0 +1,72 @@
1
+ = clean_thread - Support for background threads.
2
+
3
+ CleanThread helps you create background threads that will exit cleanly upon
4
+ request.
5
+
6
+ You may either subclass this class and override its main() method, or pass
7
+ a block to CleanThread.new.
8
+
9
+ Code invoked by CleanThread should check periodically whether the
10
+ thread needs to exit, either by invoking the check_finishing method (which
11
+ raises ThreadFinish if the finish method has been called), or by manually
12
+ checking the result of the finishing? method and exiting if it returns true.
13
+
14
+ In addition to providing the #finish and #check_finishing methods,
15
+ CleanThread takes care of the following:
16
+
17
+ - Dumping a backtrace to stderr if there is an exception
18
+ - Setting the Java thread name (if running under JRuby)
19
+ - Releasing ActiveRecord connections (if ActiveRecord is loaded)
20
+
21
+ = Installation
22
+
23
+ gem install clean_thread
24
+
25
+ = Example Usage
26
+
27
+ == Overriding CleanThread#main
28
+
29
+ require 'clean_thread'
30
+
31
+ class MyThread < CleanThread
32
+ def main
33
+ loop do
34
+ check_finishing
35
+ # ... do some steps
36
+ check_finishing
37
+ # ... do some more steps
38
+ check_finishing
39
+ # ... do yet more steps
40
+ end
41
+ end
42
+ end
43
+
44
+ t = MyThread.new
45
+ t.start
46
+ # ...
47
+ t.finish
48
+
49
+ == Passing a block to new
50
+
51
+ require 'clean_thread'
52
+
53
+ t = CleanThread.new do
54
+ loop do
55
+ CleanThread.check_finishing
56
+ # ... do some steps
57
+ CleanThread.check_finishing
58
+ # ... do some more steps
59
+ CleanThread.check_finishing
60
+ # ... do yet more steps
61
+ end
62
+ end
63
+
64
+ t.start # Start the thread
65
+ # ...
66
+ t.finish # Stop the thread
67
+
68
+ = License
69
+
70
+ Copyright © 2009-2011 Infonium Inc.
71
+
72
+ License: See the MIT-LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.pattern = 'test/**/*_test.rb'
7
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "clean_thread/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "clean_thread"
7
+ s.version = CleanThread::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Dwayne Litzenberger"]
10
+ s.email = ["dlitz@patientway.com"]
11
+ s.homepage = "https://github.com/dlitz/clean_thread"
12
+ s.summary = %q{Support for threads that exit cleanly}
13
+ s.description = <<EOF
14
+ HospitalPortal::CleanThread provides support for developing threads that exit cleanly.
15
+
16
+ Reliable J2EE deployment requires that all threads started by an application
17
+ are able to exit cleanly upon request.
18
+ EOF
19
+
20
+ s.rubyforge_project = "clean_thread"
21
+
22
+ s.files = `git ls-files`.split("\n")
23
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
24
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
25
+ s.require_paths = ["lib"]
26
+ end
@@ -0,0 +1,134 @@
1
+ require 'thread'
2
+
3
+ class CleanThread
4
+
5
+ class ThreadFinish < Exception
6
+ # NB: Most exceptions should inherit from StandardError, but this is deliberate.
7
+ end
8
+
9
+ # If the current thread was invoked by CleanThread, invoke
10
+ # CleanThread#check_finishing.
11
+ def self.check_finishing
12
+ ct = Thread.current[:clean_thread]
13
+ return nil if ct.nil?
14
+ ct.check_finishing
15
+ end
16
+
17
+ # If the current thread was invoked by CleanThread, return the result of
18
+ # CleanThread#finishing?. Otherwise, return nil.
19
+ def self.finishing?
20
+ ct = Thread.current[:clean_thread]
21
+ return nil if ct.nil?
22
+ return ct.finishing?
23
+ end
24
+
25
+ # Initialize a new CleanThread object.
26
+ #
27
+ # If a block is given, that block will be executed in a new thread when the
28
+ # start method is called. If no block is given, the main method will be used.
29
+ #
30
+ # The block is passed the CleanThread instance it belongs to, plus any
31
+ # arguments passed to the new method.
32
+ def initialize(*args, &block)
33
+ @_cleanthread_mutex = Mutex.new
34
+ @_cleanthread_stopping = false # locked by _cleanthread_mutex
35
+ @_cleanthread_thread = nil # locked by _cleanthread_mutex. Once set, it is not changed.
36
+ @_cleanthread_proc = block
37
+ @_cleanthread_args = args
38
+ end
39
+
40
+ # Start the thread.
41
+ def start
42
+ @_cleanthread_mutex.synchronize {
43
+ if @_cleanthread_thread.nil?
44
+ @_cleanthread_thread = Thread.new do
45
+ begin
46
+ # Set the Java thread name (for debugging)
47
+ Java::java.lang.Thread.current_thread.name = "#{self.class.name}-#{self.object_id}" if defined?(Java)
48
+
49
+ # Set the CleanThread instance (for use with the CleanThread.check_finishing
50
+ # and CleanThread.finishing? class methods
51
+ Thread.current[:clean_thread] = self
52
+
53
+ if @_cleanthread_proc.nil?
54
+ main(*@_cleanthread_args)
55
+ else
56
+ @_cleanthread_proc.call(self, *@_cleanthread_args)
57
+ end
58
+ rescue ThreadFinish
59
+ # Do nothing - exit cleanly
60
+ rescue Exception, ScriptError, SystemStackError, SyntaxError, StandardError => exc
61
+ # NOTE: rescue Exception should be enough here, but JRuby seems to miss some exceptions if you do that.
62
+ #
63
+ # Output backtrace, since otherwise we won't see anything until the main thread joins this thread.
64
+ display_exception(exc)
65
+ raise
66
+ ensure
67
+ # This is needed. Otherwise, the database connections aren't returned to the pool and things break.
68
+ ActiveRecord::Base.connection_handler.clear_active_connections! if defined? ActiveRecord::Base
69
+ end
70
+ end
71
+ else
72
+ raise TypeError.new("Thread already started")
73
+ end
74
+ }
75
+ return nil
76
+ end
77
+
78
+ # Ask the thread to finish, and wait for the thread to stop.
79
+ #
80
+ # If the :nowait option is true, then just ask the thread to finish without
81
+ # waiting for it to stop.
82
+ #
83
+ # When a thread invokes its own finish method, ThreadFinish is raised,
84
+ # unless :nowait is true.
85
+ def finish(options={})
86
+ @_cleanthread_mutex.synchronize {
87
+ raise RuntimeError.new("not started") if @_cleanthread_thread.nil?
88
+ @_cleanthread_stopping = true
89
+ }
90
+ unless options[:nowait]
91
+ raise ThreadFinish if @_cleanthread_thread == ::Thread.current
92
+ @_cleanthread_thread.join
93
+ end
94
+ return nil
95
+ end
96
+
97
+ # Return true if the thread is alive.
98
+ def alive?
99
+ @_cleanthread_thread && @_cleanthread_thread.alive?
100
+ end
101
+
102
+ # Wait for the thread to stop.
103
+ def join
104
+ return @_cleanthread_thread.join
105
+ end
106
+
107
+ # Return true if the finish method has been called.
108
+ def finishing?
109
+ @_cleanthread_mutex.synchronize { return @_cleanthread_stopping }
110
+ end
111
+
112
+ # Exit the thread if the finish method has been called.
113
+ #
114
+ # Functionally equivalent to:
115
+ # raise ThreadFinish if finishing?
116
+ def check_finishing
117
+ raise ThreadFinish if finishing?
118
+ return nil
119
+ end
120
+
121
+ protected
122
+ # Override this method if you want this thread to do something.
123
+ def main(cleanthread)
124
+ # default is to do nothing
125
+ end
126
+
127
+ # Override this method if you want the stacktrace output to happen differently
128
+ def display_exception(exception)
129
+ lines = []
130
+ lines << "#{exception.class.name}: #{exception.message}\n"
131
+ lines += exception.backtrace.map{|line| "\tfrom #{line}"}
132
+ $stderr.puts lines.join("\n")
133
+ end
134
+ end
@@ -0,0 +1,3 @@
1
+ class CleanThread # reopen
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,3 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + "/../lib")
@@ -0,0 +1,50 @@
1
+ require File.expand_path "../../test_helper", __FILE__
2
+
3
+ require 'clean_thread'
4
+
5
+ class CleanThreadTest < Test::Unit::TestCase
6
+ def test_allow_multple_finish
7
+ t = CleanThread.new do |t|
8
+ loop do
9
+ t.check_finishing
10
+ end
11
+ end
12
+ t.start
13
+ t.finish
14
+ assert_nothing_raised do
15
+ t.finish
16
+ end
17
+ end
18
+
19
+ def test_allow_multple_finish_with_first_nowait
20
+ t = CleanThread.new do |t|
21
+ loop do
22
+ t.check_finishing
23
+ end
24
+ end
25
+ t.start
26
+ t.finish(:nowait=>true)
27
+ sleep 0.1 while t.alive?
28
+ assert_nothing_raised do
29
+ t.finish
30
+ end
31
+ end
32
+
33
+ def test_alive_should_not_raise_error_if_thread_not_yet_started
34
+ t = CleanThread.new { }
35
+ assert_nothing_raised do
36
+ assert !t.alive?
37
+ end
38
+ end
39
+
40
+ def test_finish_nowait_should_not_raise_exception
41
+ success = false
42
+ t = CleanThread.new do |t|
43
+ t.finish(:nowait=>true)
44
+ success = true
45
+ end
46
+ t.start
47
+ t.join
48
+ assert success, "finish(:nowait=>true) should not raise an exception when invoked from within a CleanThread"
49
+ end
50
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clean_thread
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Dwayne Litzenberger
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-06-28 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |
23
+ HospitalPortal::CleanThread provides support for developing threads that exit cleanly.
24
+
25
+ Reliable J2EE deployment requires that all threads started by an application
26
+ are able to exit cleanly upon request.
27
+
28
+ email:
29
+ - dlitz@patientway.com
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files: []
35
+
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - MIT-LICENSE
40
+ - NEWS.rdoc
41
+ - README.rdoc
42
+ - Rakefile
43
+ - clean_thread.gemspec
44
+ - lib/clean_thread.rb
45
+ - lib/clean_thread/version.rb
46
+ - test/test_helper.rb
47
+ - test/unit/cleanthread_test.rb
48
+ has_rdoc: true
49
+ homepage: https://github.com/dlitz/clean_thread
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ hash: 3
63
+ segments:
64
+ - 0
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ requirements: []
76
+
77
+ rubyforge_project: clean_thread
78
+ rubygems_version: 1.6.2
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Support for threads that exit cleanly
82
+ test_files:
83
+ - test/test_helper.rb
84
+ - test/unit/cleanthread_test.rb