Project

General

Profile

Feature #15277

at_exec

Added by marcandre (Marc-Andre Lafortune) 12 months ago. Updated 11 months ago.

Status:
Open
Priority:
Normal
Target version:
-
[ruby-core:89680]

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.

History

Updated by normalperson (Eric Wong) 12 months ago

ruby-core@marc-andre.ca wrote:

https://bugs.ruby-lang.org/issues/15277

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
to exec. One has to monkey-patch the builtin method.

Use case: we roll our own in DeepCover. Some test suites will call exec,
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) 12 months 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) 11 months 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 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.

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) 11 months 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.)

Also available in: Atom PDF