Module: Lich::Common::GtkSignalHandlerRetention

Defined in:
documented/common/gtk.rb

Overview

Retain both the receiver wrapper and its callback Procs on the Ruby side. ruby-gnome stores signal handlers in C-side closures that Ruby's GC cannot see, which can leave long-lived GTK callbacks eligible for collection.

Instance Method Summary collapse

Instance Method Details

#signal_connect(signal, *args) {|Object| ... } ⇒ Integer, Object

Note:

Callbacks are retained in Lich::Common.gtk_signal_handlers until the receiver is destroyed or the handler is manually disconnected

Connects a signal handler block to a GTK receiver while retaining the callback.

ruby-gnome stores signal handlers in C-side closures that Ruby's GC cannot see, which can leave long-lived GTK callbacks eligible for collection. This method wraps the native #signal_connect to retain both the receiver wrapper and the callback Proc on the Ruby side, ensuring they survive until the receiver is destroyed.

The first call to signal_connect on a receiver also installs a 'destroy' signal handler that automatically cleans up retained references when the widget is destroyed.

Parameters:

  • signal (String)

    the GTK signal name, e.g. "clicked"

  • args (Array)

    additional arguments passed to the native signal_connect

Yields:

  • (Object)

    the callback block, which receives signal-specific arguments

Returns:

  • (Integer, Object)

    the signal handler ID from the native GTK layer

Raises:

  • (StandardError)

    if the native signal connection fails

See Also:



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'documented/common/gtk.rb', line 251

def signal_connect(signal, *args, &block)
  return super(signal, *args, &block) unless block

  receiver = self
  entry = Lich::Common.with_gtk_registry_lock do
    retained_entry = Lich::Common.gtk_signal_handlers[receiver] ||= {
      receiver: self,
      handlers: [],
      cleanup_installed: false
    }
    retained_entry[:handlers] << block
    retained_entry
  end

  cleanup = proc do
    Lich::Common.release_gtk_signal_handlers(receiver)
    false
  end

  install_cleanup = Lich::Common.with_gtk_registry_lock do
    next false if entry[:cleanup_installed]

    entry[:cleanup_installed] = true
    entry[:handlers] << cleanup
    true
  end

  if install_cleanup
    begin
      super('destroy', &cleanup)
    rescue StandardError
      Lich::Common.with_gtk_registry_lock do
        entry[:handlers].delete(cleanup)
        entry[:cleanup_installed] = false
      end
    end
  end

  begin
    super(signal, *args, &block)
  rescue StandardError
    Lich::Common.with_gtk_registry_lock do
      entry[:handlers].delete(cleanup)
      entry[:handlers].delete(block)
      entry[:cleanup_installed] = false unless entry[:handlers].include?(cleanup)
      Lich::Common.gtk_signal_handlers.delete(receiver) if entry[:handlers].empty?
    end
    raise
  end
end