Feature #15277
openat_exec
Description
There's currently no easy way to have code executed before a subsequent call to exec
. One has to monkey-patch the builtin method.
I'd like to propose a new method at_exec
that would be very similar to at_exit
, except that the callbacks are triggered before the current process is replaced by the external command.
# This would output "Hello", "Bye", and "Foo"
at_exec { puts "Bye!" }
puts "Hello"
exec "echo Foo"
Use case: we roll our own in DeepCover
. Some test suites will call exec
, and we need to store our counters before that happens.
Updated by normalperson (Eric Wong) about 6 years ago
ruby-core@marc-andre.ca wrote:
Would this work for subprocesses? (system, spawn, ``, popen, ...)
(if so, it would defeat vfork optimization we do under Linux)
There's currently no easy way to have code executed before a subsequent call
toexec
. One has to monkey-patch the builtin method.Use case: we roll our own in
DeepCover
. Some test suites will callexec
,
and we need to store our counters before that happens.
As described above, Ruby already has a mechanism for hooking
existing method calls. I tend to avoid monkey patching because
it leads to surprising behavior. However, at_exec
would have
the SAME surprises as monkey-patching.
Updated by marcandre (Marc-Andre Lafortune) about 6 years ago
normalperson (Eric Wong) wrote:
Would this work for subprocesses? (system, spawn, ``, popen, ...)
I'm not sure I understand the question. exec
effectively terminates the Ruby process although the at_exit
callbacks are not called. system
, spawn
, etc... don't do such a thing and would thus would not call the at_exec
callbacks.
Ruby already has a mechanism for hooking existing method calls
Right. I believe that monkey-patching builtin method calls is something that should be highly discouraged though.
However,
at_exec
would have the SAME surprises as monkey-patching.
Maybe so. If (god forbid!) there are multiple such monkey-patches, each with their own callback queues and potentially slightly different implementations, than there would be more surprises.
I imagine that the need of calling at_exec
is rare though, and my goal is not really to minimize surprises. My goal is to make it easier & nicer to get that result, and avoid having monkey patching as the "official solution" to this issue. We can't even prepend a module to intercept the call to exec
, so we have to do the alias_method chaining.
Updated by normalperson (Eric Wong) about 6 years ago
ruby-core@marc-andre.ca wrote:
normalperson (Eric Wong) wrote:
Would this work for subprocesses? (system, spawn, ``,
popen, ...)I'm not sure I understand the question.
exec
effectively
terminates the Ruby process although theat_exit
callbacks
are not called.system
,spawn
, etc... don't do such a
thing and would thus would not call theat_exec
callbacks.
system, spawn, etc. implicitly call exec, but only after fork/vfork
in a child.
Anyways, I'm still against this proposal because there is an
existing and generic way to support such behavior.
Updated by shevegen (Robert A. Heiler) about 6 years ago
I imagine that the need of calling at_exec is rare though
I think something similar could be said about at_exit. While I
think it is fine that it exists, I found that in larger projects
it can be quite annoying if some other project uses at_exit.
For example, this old project makes use of at_exit:
https://rubygems.org/gems/cgi-exception
It prints exceptions that a .cgi script has onto the web-page,
a bit similar as to how .php files work.
I copy/paste the code so that it is easier to read:
## when process exit, print exception if exception raised
at_exit do
if $!
ex = $!
CGIExceptionPrinter.new($_header_printed).handle(ex)
if defined?(EditorKicker)
EditorKicker.handle(ex) #if ENV['EDITOR_KICKER']
end
end
end
The display of where an error line occurs is nice, but I found
that at_exit events can be difficult to control and lead to
some more complexity. It may not always be easy to find out
which at_exit is triggered first and where. This is largely
a reason why I avoid at_exit, even if it may be useful.
While I am neutral on the proposal here, thus not having a pro
or con opinion, I think we can generalize this a bit by looking
not only at at_exit, but also autoload. Autoload is similar in
some ways in that it "says": "if a constant is first used and
unknown, load these files". So it also is a bit like an at-"event"
or a hook. There are other hooks/events in ruby; catch-sigint
or the included, inherited etc...
Perhaps it may be useful to easily specific "at" events.
So for example, rather than a pre-defined at_exec method,
there could be a simpler "build" API that allows such events
to happen. Like the various attr APIs like attr_reader,
attr_writer and so on.
Perhaps like this way:
define_at :exec { puts "Bye!" }
at_exec { puts "Bye!" }
(I guess the first line needs (), but I only wanted to demonstrate
the idea here. This would generate at_* methods, without having
them pre-defined.)
Of course I don't know if Marc-Andre agrees to any of this - I only
wanted to give this as a rough idea what I mean. :)
You could then think of at_exit as being similarly defined. And any
other methods that a ruby hacker may want to use, via an "at_" prefix.
But anyway, I do not want to distract from the suggestion. Marc-Andre
also gave a use case and I think even if one may not like the special
case of at_exec, I think the usefulness of the described use case is
still a possibility.
Ultimately the hook/events/conditional_code execution outside of
more "classical" if/else checks is something that matz may have to
think about. (On an unimportant side note, the old programming
language LPC, used for some old text-MUDs, also had certain events
and hooks that could be "attached" onto mobs/monsters/npcs, to
enable certain additional ad-hoc functionality or behaviour, without
this having to necessarily be written into the files that would be
used to create the initial object at hand. Of course ruby is a lot
more flexible than LPC, but I wanted to mention this since such
conditional hooks also existed in older languages, at the least
to some extent.)
Updated by vihai (Daniele Orlandi) over 4 years ago
I would like to add a use-case that I'm encountering right now.
I would like to call prctl(PR_SET_PDEATHSIG, ...) in order to ensure that a spawned process is always killed regardless of how cleanly the parent process exits. I couldn't do this in the parent because fork(2) clears such setting so I must be able to invoke prctl between fork and exec.
An alternative solution would be to add a callback to spawn's options to be called before exec.
Updated by hsbt (Hiroshi SHIBATA) 9 months ago
- Status changed from Open to Assigned