iso_latte 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5780f40ea4f18590135725740fccd26e885510fd
4
+ data.tar.gz: 3201f17dad3af7bf0be4ae3b42596601f7c9b62a
5
+ SHA512:
6
+ metadata.gz: 86beb89d8fcfb691d81748deb32169fbf4aa41aa4c43c0cccfd49d901f25c5dfa2430588f44e2cc2986013aa73975a64ae1aec5104096ce98a2c7bde780ee91a
7
+ data.tar.gz: fe992f6f80a746bcdbd5287d77c760498a221a8c5be79d8cdcf8ad960b9f9d957f4a7a16e78ccbb39c3f1fc72eaec714557e73800f11ef6d5eb62af724ee91b5
@@ -0,0 +1,3 @@
1
+ module IsoLatte
2
+ VERSION = "1.0"
3
+ end
data/lib/iso_latte.rb ADDED
@@ -0,0 +1,92 @@
1
+ require "ostruct"
2
+
3
+ module IsoLatte
4
+ NO_EXIT = 122
5
+ EXCEPTION_RAISED = 123
6
+
7
+ # Available options:
8
+ # stderr: a path to write stderr into. Defaults to '/dev/null'
9
+ # nil means "do not change stderr"
10
+ # finish: a callable to execute when the subprocess terminates (in any way).
11
+ # receives a boolean 'success' value, and an exitstatus as arguments
12
+ # success: a callable to execute if the subprocess completes successfully.
13
+ # note: this can exit ALONGSIDE the exit callback, if the subprocess
14
+ # exits with zero explicitly!
15
+ # kill: a callable to execute if the subprocess is killed (SIGKILL).
16
+ # fault: a callable to execute if the subprocess segfaults, core dumps, etc.
17
+ # exit: a callable to execute if the subprocess voluntarily exits with nonzero.
18
+ # receives the exit status value as its argument.
19
+ #
20
+ # It is allowable to Isolatte.fork from inside an IsoLatte.fork block (reentrant)
21
+ #
22
+ # We are using the exit statuses of 122 and 123 as sentinels that mean
23
+ # 'the code did not exit on its own' and 'the code raised an exception'.
24
+ # If you have code that actually uses those exit statuses.. change the special
25
+ # statuses I guess.
26
+ #
27
+ def self.fork(options = nil, &block)
28
+ defaults = { stderr: "/dev/null", exit: nil }
29
+ opts = OpenStruct.new(defaults.merge(options || {}))
30
+
31
+ read_ex, write_ex = IO.pipe
32
+
33
+ child_pid = Process.fork do
34
+ read_ex.close
35
+ begin
36
+ if opts.stderr
37
+ File.open(opts.stderr, "w") do |stderr_file|
38
+ STDERR.reopen(stderr_file)
39
+ STDERR.sync = true
40
+ $stderr = STDERR
41
+ block.call
42
+ end
43
+ else
44
+ block.call
45
+ end
46
+ rescue StandardError => e
47
+ # Things that stick un-marshalable attributes on exceptions need to be handled here.
48
+ ivars = ["@__better_errors_bindings_stack"]
49
+ ivars.each { |v| e.instance_variable_set(v, nil) if e.instance_variable_defined?(v) }
50
+
51
+ Marshal.dump e, write_ex
52
+ write_ex.flush
53
+ write_ex.close
54
+ exit!(EXCEPTION_RAISED)
55
+ end
56
+
57
+ exit!(NO_EXIT)
58
+ end
59
+
60
+ write_ex.close
61
+
62
+ pid, rc = Process.wait2(child_pid)
63
+ fail(Error, "Wrong child's exit received!") unless pid == child_pid
64
+
65
+ if rc.exited? && rc.exitstatus == EXCEPTION_RAISED
66
+ e = Marshal.load read_ex
67
+ read_ex.close
68
+ fail e
69
+ else
70
+ read_ex.close
71
+ end
72
+
73
+ success = rc.success? || rc.exitstatus == NO_EXIT
74
+ code = rc.exitstatus == NO_EXIT ? 0 : rc.exitstatus
75
+
76
+ if success
77
+ opts.success.call if opts.success
78
+ else
79
+ opts.fault.call if opts.fault && (rc.termsig == 6 || rc.coredump?)
80
+ opts.kill.call if opts.kill && rc.termsig == 9
81
+ end
82
+
83
+ # This can execute on success OR failure - it indicates that the subprocess
84
+ # *explicitly* exited, whether with zero or nonzero.
85
+ opts.exit.call(rc.exitstatus) if opts.exit && rc.exited? && rc.exitstatus != NO_EXIT
86
+
87
+ # This should execute *no matter what*
88
+ opts.finish.call(success, code) if opts.finish
89
+ end
90
+
91
+ class Error < StandardError; end
92
+ end
metadata ADDED
@@ -0,0 +1,47 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iso_latte
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ platform: ruby
6
+ authors:
7
+ - Emcien Engineering
8
+ - Eric Mueller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-07-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: IsoLatte allows execution to be forked from the main process for safety
15
+ email:
16
+ - engineering@emcien.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - lib/iso_latte.rb
22
+ - lib/iso_latte/version.rb
23
+ homepage: https://github.com/emcien/iso_latte
24
+ licenses:
25
+ - BSD-3-Clause
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubyforge_project:
43
+ rubygems_version: 2.4.3
44
+ signing_key:
45
+ specification_version: 4
46
+ summary: A gem for isolating execution in a subprocess
47
+ test_files: []