restartable 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. checksums.yaml +8 -8
  2. data/TODO +2 -1
  3. data/lib/restartable.rb +63 -36
  4. data/restartable.gemspec +1 -1
  5. metadata +2 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- ZjBlMWMyMTgxZjllMGY4NTNkZGMzNTcxODY2ZjlmYTU5ZjA3MGNmMQ==
4
+ MWQ5YTBhNmJmNjQxOTdkZGRmZDlmOThhYWZmZmQ5MDY4OGJiMWM1Yw==
5
5
  data.tar.gz: !binary |-
6
- ZWE5Nzc2YjgxYTY2MjBjMDA4OGE1ZDcxZTVkNzk2Y2VlMDVmN2Q5NA==
6
+ ZjMwMzEyMzBhYzQ0NzMyZjgyZjM3YTBhMmMzZDU5OWNhNjU2MGQ0ZA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NDBkMmUxOTczOTQ5N2JlZDJjYzc1ZjMyOTc1YjgxYjNkMWMyMmEyMmJjODVl
10
- OTlhMzA5ZTFiNmM5MWVjMjUwYzFiNDk1ZjA1ZTJiMGU0ZDQ3ZjI2ZWFlNWQx
11
- MWYxNjRlZjZhN2Q3ZDU3YTliYjZjNjBjMTQ1YWEwMTNiMTcwYzQ=
9
+ NzAwZmI4M2VkMzU4ZTE1ZmE5MjJiYWFmNTFjMTI3MGMyM2UzZjYxMWMxYzI1
10
+ ZDdmN2U4M2IwODdmYjYyMjJmMDUzZjc2OTkwZDk5ODY0ZWFjY2I2ZWU3NWY2
11
+ MDk0MDBiMjFkNGM0MGQ2YmUwZmYwZmZjYTNhMDk4MGE2NDM4MjM=
12
12
  data.tar.gz: !binary |-
13
- NDU2NTA2M2M3N2M1YjBjZWE2ZWE5MzY5NzQxNjViYWU2ZjM1MDU3NmM2N2Ez
14
- MGRiZDE1YTE2MmM1N2MwYmQ0NTMwMTdjZTI0ZmVmNWY4NWFhNjU5NDM0ZGEz
15
- YTJmYjlhYWRmOGM4MDliMTQzN2Y5MGJiOTg0NDQzMmIyY2E4YWQ=
13
+ ZTczZDEwNzY1MDg3Y2VhOTI4OTVkNzA0NzlhM2FlMjYyN2VlYzhiODc4Yjlm
14
+ YmQ5NzFjYmMxNjA2N2ZjZTkzNzFkZTc0ODY4MTNlNGIzZGE5MDM3YjI2ZWVm
15
+ ZTM2ZDVhOGQxZmE2ZmZjODA4YTJiZDdiODVlMDcyZjY0MTE5ZTU=
data/TODO CHANGED
@@ -1,2 +1,3 @@
1
- run block on restart
1
+ add option to run block on restart
2
2
  bin option to run command on restart
3
+ option to auto restart when block finishes
data/lib/restartable.rb CHANGED
@@ -2,82 +2,109 @@
2
2
 
3
3
  require 'sys/proctable'
4
4
  require 'colored'
5
+ require 'thread'
5
6
 
6
7
  class Restartable
7
8
  def self.version
8
9
  Gem.loaded_specs['restartable'].version.to_s rescue 'DEV'
9
10
  end
10
11
 
11
- WAIT_SIGNALS = [[1, 'INT'], [1, 'INT'], [1, 'INT'], [1, 'TERM'], [5, 'KILL']]
12
-
13
12
  def initialize(options, &block)
14
13
  @options, @block = options, block
14
+ run!
15
+ end
16
+
17
+ private
18
+
19
+ def run!
15
20
  @mutex = Mutex.new
16
- synced_trap('INT'){ interrupt! }
17
- synced_trap('TERM'){ no_restart!; interrupt! }
21
+
22
+ receiver, sender = IO.pipe
23
+ @trap_sender = Process.fork do
24
+ receiver.close
25
+ Signal.trap('PIPE', 'EXIT')
26
+ synced_trap('INT'){ Marshal.dump(:int, sender) }
27
+ loop{ sleep }
28
+ end
29
+ sender.close
30
+
31
+ Signal.trap('INT', 'IGNORE')
32
+ synced_trap('TERM'){ terminate! }
33
+
34
+ int_receiver = Thread.new do
35
+ until receiver.eof?
36
+ Marshal.load(receiver) && interrupt!
37
+ end
38
+ end
18
39
  cycle
19
40
  end
20
41
 
21
42
  def interrupt!
22
43
  unless @interrupted
23
44
  @interrupted = true
24
- Thread.list.each do |thread|
25
- unless Thread.current == thread
26
- thread.raise SignalException.new('INT')
27
- end
45
+ (Thread.list - [Thread.current]).each do |thread|
46
+ thread.raise SignalException.new('INT')
28
47
  end
29
48
  else
30
49
  no_restart!
31
50
  end
32
51
  end
33
52
 
53
+ def terminate!
54
+ no_restart!
55
+ interrupt!
56
+ end
57
+
34
58
  def no_restart!
35
59
  @stop = true
36
60
  puts 'Don\'t restart!'.red.bold
37
61
  end
38
62
 
63
+ def synced_trap(signal, &block)
64
+ Signal.trap(signal) do
65
+ Thread.new do
66
+ @mutex.synchronize(&block)
67
+ end
68
+ end
69
+ end
70
+
71
+ WAIT_SIGNALS = [[5, 'INT'], [3, 'INT'], [1, 'INT'], [3, 'TERM'], [5, 'KILL']]
72
+
39
73
  def cycle
40
74
  until @stop
41
75
  @interrupted = false
42
76
  puts '^C to restart, double ^C to stop'.green
43
77
  begin
44
78
  @block.call
45
- sleep # wait ^C even if block finishes
79
+ loop{ sleep } # wait ^C even if block finishes
46
80
  rescue SignalException
47
- unless children.empty?
48
- puts 'Killing children…'.yellow.bold
49
- ripper = Thread.new do
50
- WAIT_SIGNALS.each do |time, signal|
51
- sleep time
52
- puts "…SIG#{signal}…".yellow
53
- children.each do |child|
54
- Process.kill(signal, child.pid)
55
- end
56
- end
57
- end
58
- Process.waitall
59
- ripper.terminate
60
- end
61
- unless @stop
62
- puts 'Waiting ^C 0.5 second than restart…'.yellow.bold
63
- sleep 0.5
64
- end
81
+ kill_children!
82
+ end
83
+ unless @stop
84
+ puts 'Waiting ^C 0.5 second than restart…'.yellow.bold
85
+ sleep 0.5
65
86
  end
66
87
  end
67
88
  end
68
89
 
69
- private
70
-
71
- def synced_trap(signal, &block)
72
- Signal.trap(signal) do
73
- Thread.new do
74
- @mutex.synchronize(&block)
90
+ def kill_children!
91
+ unless children_pids.empty?
92
+ puts 'Killing children…'.yellow.bold
93
+ ripper = Thread.new do
94
+ WAIT_SIGNALS.each do |time, signal|
95
+ sleep time
96
+ puts "…SIG#{signal}…".yellow
97
+ children_pids.each do |child_pid|
98
+ Process.kill(signal, child_pid)
99
+ end
100
+ end
75
101
  end
102
+ children_pids.each(&Process.method(:wait))
103
+ ripper.terminate
76
104
  end
77
105
  end
78
106
 
79
- def children
80
- pid = Process.pid
81
- Sys::ProcTable.ps.select{ |pe| pid == pe.ppid }
107
+ def children_pids
108
+ Sys::ProcTable.ps.select{ |pe| $$ == pe.ppid }.map(&:pid) - [@trap_sender]
82
109
  end
83
110
  end
data/restartable.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'restartable'
5
- s.version = '0.1.2'
5
+ s.version = '0.1.3'
6
6
  s.summary = %q{Run code, Ctrl-C to restart, once more Ctrl-C to stop}
7
7
  s.homepage = "http://github.com/toy/#{s.name}"
8
8
  s.authors = ['Ivan Kuchin']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restartable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Kuchin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-04-01 00:00:00.000000000 Z
11
+ date: 2013-04-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colored