workety 2.0.3

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.
@@ -0,0 +1,46 @@
1
+
2
+ # Copyright 2006-2012 Stanislav Senotrusov <stan@senotrusov.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ class GracefulStopThread
17
+ # Before dropping privileges
18
+ def initialize
19
+ @mutex = Mutex.new
20
+ @wakeup = ConditionVariable.new
21
+ end
22
+
23
+ # After changing privileges to some user/group
24
+ def start
25
+ @worker = Thread.workety do
26
+ @mutex.synchronize do
27
+ until Workety.must_stop? do
28
+
29
+ puts "Hello"
30
+
31
+ @wakeup.wait(@mutex, 10)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def join
38
+ @worker.join
39
+ end
40
+
41
+ def stop
42
+ @mutex.synchronize do
43
+ @wakeup.signal
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ # Copyright 2006-2011 Stanislav Senotrusov <stan@senotrusov.com>
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ class SimpleThread
16
+ # Before dropping privileges
17
+ def initialize
18
+ end
19
+
20
+ # After changing privileges to some user/group
21
+ def start
22
+ @t = Thread.workety do
23
+ until Workety.must_stop? do
24
+ sleep 1
25
+ end
26
+ end
27
+
28
+ Thread.workety do
29
+ sleep 10
30
+ Workety.stop
31
+ end
32
+ end
33
+
34
+ def join
35
+ @t.join
36
+ end
37
+
38
+ def stop
39
+ @t.kill
40
+ end
41
+ end
42
+
@@ -0,0 +1,89 @@
1
+
2
+ # Copyright 2006-2011 Stanislav Senotrusov <stan@senotrusov.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ class Workety::TestThread
17
+ # Before dropping privileges
18
+ def initialize
19
+ STDOUT.write "#{self.class} init, pid #{Process.pid}\n"
20
+ end
21
+
22
+ # After changing privileges to some user/group
23
+ def start
24
+ STDOUT.write "#{self.class} start, pid #{Process.pid}\n"
25
+
26
+ # nonexistant_method
27
+
28
+ STDOUT.write "STDOUT test\n"
29
+ STDERR.write "STDERR test\n"
30
+
31
+ Thread.workety do
32
+ sleep 5
33
+ bad_method
34
+ end
35
+
36
+ # Thread.new do
37
+ # begin
38
+ # sleep 5
39
+ # STDOUT.write "Workety.stop\n"
40
+ # Workety.stop
41
+ # rescue Exception => e
42
+ # e.report!
43
+ # end
44
+ # end
45
+
46
+ @ws = Thread.new do
47
+ begin
48
+ until Workety.must_stop? do
49
+ STDOUT.write "Workety.must_stop?\n"
50
+ sleep 1
51
+ end
52
+ STDOUT.write "Workety.must_stop? is true\n"
53
+
54
+ rescue Exception => e
55
+ e.report!
56
+ end
57
+ end
58
+
59
+
60
+ @t = Thread.new do
61
+ 600.times do |t|
62
+ STDOUT.write "#{self.class} Doing #{t}\n"
63
+ sleep 1
64
+ end
65
+ end
66
+
67
+ # 1000.times do
68
+ # Thread.new { 1000.times { Rails.logger.error "test" } }
69
+ # Thread.new { 1000.times { STDOUT.write "test\n" } }
70
+ # end
71
+ # cat log/workety-test_thread.log |grep -v "^test$"
72
+
73
+ end
74
+
75
+ def join
76
+ STDOUT.write "#{self.class} join\n"
77
+ @t.join
78
+ @ws.join
79
+ STDOUT.write "#{self.class} done join\n"
80
+ STDOUT.write "#{self.class} sleep\n"
81
+ #sleep 30
82
+ STDOUT.write "#{self.class} done sleep\n"
83
+ end
84
+
85
+ def stop
86
+ STDOUT.write "#{self.class} stop\n"
87
+ @t.kill
88
+ end
89
+ end
@@ -0,0 +1,56 @@
1
+
2
+ # Copyright 2006-2011 Stanislav Senotrusov <stan@senotrusov.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ class TimedExit
18
+ def initialize(timeout = 60, message = "Timeout reached")
19
+ @mutex = Mutex.new
20
+
21
+ @must_cancel = false
22
+ @timeout_reached = false
23
+
24
+ Thread.rescue_exit do
25
+ sleep timeout
26
+
27
+ @mutex.synchronize do
28
+ unless @must_cancel
29
+ @timeout_reached = true
30
+ Rails.logger.info message
31
+ Process.exit(false)
32
+ end
33
+ end
34
+ end
35
+
36
+ if block_given?
37
+ yield
38
+ cancel
39
+ end
40
+ end
41
+
42
+ def cancel
43
+ @mutex.synchronize do
44
+ Process.exit(false) if @timeout_reached # See comment below
45
+ @must_cancel = true
46
+ end
47
+ end
48
+ end
49
+
50
+ # In the following example block "ALIVE!\n" seems to not get execution, but I am not sure - does that behaviour consistent?
51
+ #require 'thread'
52
+ #m = Mutex.new
53
+ #Thread.new { m.synchronize { sleep 3; Process.exit(false) } }
54
+ #sleep 1
55
+ #m.synchronize { STDOUT.write "ALIVE!\n" }
56
+
@@ -0,0 +1,135 @@
1
+
2
+ # Copyright 2006-2011 Stanislav Senotrusov <stan@senotrusov.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ # Initialize, start, stop and join worker class
18
+ # Drop process privileges after initialize
19
+ #
20
+ # initialize() then start()
21
+ # There is no strict order for stop and join - stop may be called before join
22
+ #
23
+ # Use Workety.stop to shutdown process from within worker
24
+ # Use Workety.abort to shutdown and return exit code 1, thus leading to restart by watchdog
25
+ #
26
+ #
27
+ # This is an implementation for threaded worker.
28
+ #
29
+ # TODO: If worker does not have any threads then a different path should be given:
30
+ # * no mutex/started/must_stop
31
+ # * no Signal.threaded_trap
32
+ # * no thread list on USR1
33
+ # * Just instantiate the class and call .start on it, leaving signal handling to that class
34
+ #
35
+ #
36
+ module Workety
37
+ STOP_SELF_WATCHDOG_TIMEOUT = 65
38
+
39
+ @thread = nil
40
+ @mutex = Mutex.new
41
+ @started = false
42
+ @must_stop = false
43
+ @aborted = false
44
+
45
+ class << self
46
+
47
+ def start(class_name, user = nil, group = nil)
48
+ rescue_exit do
49
+
50
+ # Class initialize is the place to things you should do before dropping privilegies (like start listening at port 80).
51
+ #
52
+ @mutex.synchronize do
53
+ Process.exit(!@aborted) if @must_stop
54
+ @thread = class_name.constantize.new
55
+ end
56
+
57
+ Process.change_privilegies(user, group) if user || group
58
+
59
+ @mutex.synchronize do
60
+ Process.exit(!@aborted) if @must_stop
61
+ @thread.start
62
+ @started = true
63
+ end
64
+ end
65
+ end
66
+
67
+ def join
68
+ rescue_exit { @thread.join }
69
+ end
70
+
71
+ def stop_sequence(aborted)
72
+ rescue_exit do
73
+ @mutex.synchronize do
74
+ @aborted = true if aborted
75
+
76
+ unless @must_stop
77
+ @must_stop = true
78
+
79
+ Thread.rescue_exit { stop_watchdog }
80
+ Thread.rescue_exit { @thread.stop } if @started
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ # Thread initialize/start occurs inside @mutex, so calling Workety.abort/stop/must_stop?/aborted? from within it will
87
+ # lead to "ThreadError: deadlock; recursive locking"
88
+
89
+ def abort
90
+ stop_sequence(true)
91
+ end
92
+
93
+ def stop
94
+ stop_sequence(false)
95
+ end
96
+
97
+ def aborted?
98
+ @mutex.synchronize { @aborted }
99
+ end
100
+
101
+ def must_stop?
102
+ @mutex.synchronize { @must_stop }
103
+ end
104
+
105
+ def loop
106
+ until must_stop?
107
+ yield
108
+ end
109
+ end
110
+
111
+
112
+ def rescue_abort
113
+ yield
114
+ rescue ScriptError, StandardError => exception
115
+ begin
116
+ exception.report!
117
+ ensure
118
+ Workety.abort
119
+ end
120
+ end
121
+
122
+
123
+ # Timeout for stop() and join()
124
+ # When the process is stopped by a signal, watchdog or signal sender take care of timeout
125
+ # But when the process call Workety.stop/abort by itself, this timeout function will ensure successful termination
126
+ def stop_watchdog
127
+ sleep Workety::STOP_SELF_WATCHDOG_TIMEOUT
128
+ Thread.log "Timeout stopping process"
129
+ ensure
130
+ Process.exit(false)
131
+ end
132
+
133
+ end
134
+ end
135
+
@@ -0,0 +1,7 @@
1
+ begin
2
+ raise StandardError.details("ALARM!2", :foo => "bar", :name => "NAAME")
3
+ rescue => ex
4
+ ex.log!
5
+ ex.report_to_exceptional!
6
+ #ex.report_to_airbrake!
7
+ end
@@ -0,0 +1,26 @@
1
+ if action == :console
2
+ Rails.logger = ActiveSupport::BufferedLogger.new(STDOUT)
3
+ else
4
+ begin
5
+ Rails.logger = ActiveSupport::BufferedLogger.new(logfile)
6
+
7
+ rescue StandardError => exception
8
+ exception.view!
9
+
10
+ Rails.logger = ActiveSupport::BufferedLogger.new(STDOUT)
11
+ Rails.logger.warn "Continuing anyway"
12
+ end
13
+ end
14
+
15
+ Rails.logger.level = ActiveSupport::BufferedLogger.const_get(options[:log_level].upcase) if options[:log_level]
16
+
17
+ Rails.logger.auto_flushing = true
18
+ Rails.logger.flush
19
+ at_exit { Rails.logger.flush }
20
+
21
+ ActiveSupport::BufferedLogger.class_eval do
22
+ def chown_logfile(user_uid, group_gid)
23
+ @log.chown(user_uid, group_gid) if @log.respond_to?(:chown)
24
+ end
25
+ end
26
+
@@ -0,0 +1,8 @@
1
+ def mem? title = "Memory usage"
2
+ STDOUT.write "---- #{title} ----\n" + File.read("/proc/#{Process.pid}/status").split("\n").select{|l|l.match(/VmSize/)}.first.to_s + "\n"
3
+ end
4
+
5
+ def load_path?
6
+ STDOUT.write $LOAD_PATH.join("\n") + "\n"
7
+ end
8
+
@@ -0,0 +1,39 @@
1
+
2
+ # Copyright 2006-2011 Stanislav Senotrusov <stan@senotrusov.com>
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require 'thread'
17
+
18
+ t = nil
19
+
20
+ term_signal_handler = Proc.new do
21
+ t = Thread.new do
22
+ begin
23
+ puts "THREAD"
24
+ sleep 5
25
+ puts "DONE"
26
+ rescue Exception => exception
27
+ puts exception.inspect
28
+ end
29
+ end
30
+ end
31
+
32
+ Signal.trap('INT', & term_signal_handler) # Ctrl+C
33
+ Signal.trap('TERM', & term_signal_handler) # kill
34
+
35
+
36
+ Process.kill("TERM", Process.pid)
37
+
38
+ sleep 10
39
+