Description
Currently the VM has a bunch of places where based on a certain condition an extra "event" is triggered. For example, whenever System::vm_inc_global_serial()
is called it will log whenever -Xserial.debug
is set. Another example is the object allocation code emitting dtrace/systemtap events. A third example would be signal handlers. In almost all (if not all) cases this logic is hardcoded in the C++ side of things. This means that there's no way to subscribe to these events from Ruby.
Ideally there's some sort of event loop to which the C++ VM publishes events. Ruby code in turn can subscribe and act upon these events. Such a system could then be used as the building blocks for #3488 . @brixen and I have discussed this on a few occasions in the past, but to date no concrete proposal has been drafted.
As a mockup I'd envision usage of this system to be something along the lines of the following:
Rubinius.events.subscribe(:signal) do |signal_name|
case signal_name
when 'USR1'
...
when 'TERM'
...
end
end
Rubinius.events.publish(:signal, 'TERM')
There are a few tricky things here to consider:
- VM code would have to call back in Ruby. Since VM code can run before Ruby code is fully set up this could mean that events are fired before
Rubinius.events
ever exists. - To not degrade performance the subscribers should be executed in a single background thread. Using one thread per handler is also an option, but this could put too much strain on the system.
- When passing objects from a producer to the subscribers, said objects should be visible to the GC. We don't want junk to be passed around because the GC accidentally collected an object.
A bunch of use cases for this that I can think of from the top of my head:
- Tracking object allocations
- Tracking method cache resets
- Tracking constant cache resets
- Emitting events to statsd and friends (this might even allow us to move this logic from C++ to Ruby)
- Signal handling