iso_latte 1.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.
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: []