Class: Kafka::FFI::Opaque

Inherits:
Object
  • Object
show all
Extended by:
FFI::DataConverter
Defined in:
lib/kafka/ffi/opaque.rb

Overview

Opaque provides a safe mechanism for providing Ruby objects as opaque pointers.

Opaque pointers are used heavily in librdkafka to allow for passing references to application state into callbacks, configs, and other contexts. Ruby’s garbage collector cannot check for references held in external FFI memory and will garbage collect objects that are otherwise not referenced leading to a segmentation fault.

Opaque solves this by allocated a memory address which is used as a hash key to look up the Ruby object when needed. This keeps a reference to the object in Ruby so it is not garbage collected. This method allows for Ruby objects to be moved in memory during compaction (in Ruby 2.7+) compared to storing a referece to a Ruby object.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Opaque

Returns a new instance of Opaque.



68
69
70
71
72
73
# File 'lib/kafka/ffi/opaque.rb', line 68

def initialize(value)
  @value = value
  @pointer = ::FFI::MemoryPointer.new(:int8)

  Opaque.register(self)
end

Instance Attribute Details

#pointerObject (readonly)

Returns the value of attribute pointer.



66
67
68
# File 'lib/kafka/ffi/opaque.rb', line 66

def pointer
  @pointer
end

#valueObject (readonly)

Returns the value of attribute value.



65
66
67
# File 'lib/kafka/ffi/opaque.rb', line 65

def value
  @value
end

Class Method Details

.from_native(value, _ctx) ⇒ Object

Parameters:

  • value (FFI::Pointer)


56
57
58
59
60
61
62
# File 'lib/kafka/ffi/opaque.rb', line 56

def from_native(value, _ctx)
  if value.null?
    return nil
  end

  @registry.fetch(value.address, nil)
end

.register(opaque) ⇒ Object

Register the Opaque the registry, keeping a reference to it to avoid it being garbage collected. This will replace any existing Opaque with the same address.

Parameters:



32
33
34
# File 'lib/kafka/ffi/opaque.rb', line 32

def register(opaque)
  @registry[opaque.pointer.address] = opaque
end

.remove(opaque) ⇒ Object

Remove the Opaque from the registry, putting it back in contention for garbage collection.

Parameters:



40
41
42
# File 'lib/kafka/ffi/opaque.rb', line 40

def remove(opaque)
  @registry.delete(opaque.pointer.address)
end

.to_native(value, _ctx) ⇒ FFI::Pointer

Returns Pointer referencing the Opauqe value.

Parameters:

Returns:

  • (FFI::Pointer)

    Pointer referencing the Opauqe value.



47
48
49
50
51
52
53
# File 'lib/kafka/ffi/opaque.rb', line 47

def to_native(value, _ctx)
  if value.nil?
    return ::FFI::Pointer::NULL
  end

  value.pointer
end

Instance Method Details

#freeObject

Free releases the pointer back to the system and removes the Opaque from the registry. free should only be called when the Opaque is no longer stored in librdkafka as it frees the backing pointer which could cause a segfault if still referenced.



79
80
81
82
83
84
85
# File 'lib/kafka/ffi/opaque.rb', line 79

def free
  Opaque.remove(self)
  @pointer.free

  @value = nil
  @pointer = nil
end