lib: add internal object pool API and use it; adapt plugins/tests
Pooling
=======
This patch adds the `bt_object_pool` (object pool) internal API.
From <https://en.wikipedia.org/wiki/Object_pool_pattern>:
> The object pool pattern is a software creational design pattern that
> uses a set of initialized objects kept ready to use – a "pool" –
> rather than allocating and destroying them on demand. A client of the
> pool will request an object from the pool and perform operations on
> the returned object. When the client has finished, it returns the
> object to the pool rather than destroying it; this can be done
> manually or automatically.
>
> Object pools are primarily used for performance: in some
> circumstances, object pools significantly improve performance.
When initializing an object pool with bt_object_pool_initialize(), you
provide a function which can allocate a brand new object from memory, as
well as a function which can destroy and deallocate an existing object.
As of this patch, the destroy function is only used when you call
bt_object_pool_finalize() to finalize an object pool: all the objects
in the pool are destroyed.
The object pool API is:
bt_object_pool_create_object():
Creates an object from an object pool. If the pool is empty, this
function calls the "new" user function to allocate a new object
before returning it. Otherwise this function returns a recycled
object, removing it from the pool. The returned object is owned by
the caller.
bt_object_pool_recycle_object():
Recycles an object, that is, puts it back into the pool. The pool
becomes the sole owner of the object to recycle.
There is not upper limit for the number of objects in the pool as of
this patch: the pool can grow infinitely, it never shrinks.
Within the library, the function naming convention for a recyclable, or
"poolable" object `bt_X` is as such:
bt_X_new():
Allocates an object from memory. The object created by this function
might be incomplete: the object is not always ready to use after
calling this. This is the function which is passed as the "new"
function when calling bt_object_pool_initialize().
bt_X_create():
Creates an object from a pool and makes it useable, setting again
the references that were reset during bt_X_recycle(), for example.
Which pool to use is explicitly known by the function. It is part of
an object received as a parameter. This could call bt_X_new()
indirectly if the pool is empty.
bt_X_recycle():
Resets the received object and recycles it within the appropriate
pool. This function puts any reference which would keep the shared
object alive even if the owner is now partially dead, part of a
pool.
bt_X_destroy():
Finalizes and deallocates the received object.
The following objects are recyclable as of this patch:
* `bt_event`; pool belongs to its event class, recycling happens when
the event notification is destroyed.
* `bt_clock_value`; pool belongs to its clock class.
* `bt_packet`; pool belongs to its stream, recycling happens when its
reference count reaches 0.
* `bt_packet_header_field`; pool belongs to a trace (more about this
new object below).
* `bt_packet_context_field`; pool belongs to a stream class (more about
this new object below).
* `bt_event_header_field`; pool belongs to a stream class (more about
this new object below).
Unique objects
==============
Having a pool of event objects means that an event object must make sure
that everything under it which could be modified per instance is not
shared. In this case, the four `bt_field` objects (header, stream event
context, context, and payload) must not be shared.
Therefore this patch introduces unique objects (non-shared). By default,
bt_object_init() initializes a shared object. A function which creates a
unique object can mark it as unique thanks to bt_object_set_is_shared().
This is only used in developer mode to verify that the developer does
not call bt_get() or bt_put() on a unique object.
As a library user, you cannot create a unique object. It is created (and
destroyed) internally: you can only borrow the already-created object
from another object.
As of this patch, bt_event_create() and bt_field_create() are internal
functions. When you create an event notification with
bt_notification_event_create(), you pass the event class and packet to
use for the internal creation of the event object. Then you can borrow
the created (unique, non-shared) event with
bt_notification_event_borrow_event(), and its fields with
bt_event_borrow_header(), bt_event_borrow_stream_event_context(),
bt_event_borrow_context(), and bt_event_borrow_payload().
Then, recursively, you can borrow fields from compound fields
(structure, array, sequence, variant) and set their values.
On creation, the structure, variant, and array fields recursively create
all their subfields. This can't be done with the sequence field because
we need to know its length: subfields are created lazily when the
sequence's length increases; they are never destroyed until the sequence
field itself is destroyed (the sequence field's length is not
necessarily its field pointer array's length). A variant field has a
current field member which points to one of its subfields depending on
the current selection.
A packet object is still shared, as it needs to be shared between
multiple event objects. Its header and context fields, however, are
unique. Like the event object, the packet object creates its own unique
fields and you can borrow them with bt_packet_borrow_header() and
bt_packet_borrow_context().
Clock values are also unique (bt_clock_value_create() is now internal).
They are created when an event or an inactivity notification is created.
Once you borrow a clock value object using a clock class, you can set
its value with the new bt_clock_value_set_value() function. Clock value
objects now have the concepts of being set and frozen (like field
objects) because they are not immutable anymore.
CTF IR field API changes
========================
* bt_field_integer_*() functions now apply to both integer and
enumeration fields, as an enumeration field is considered to be
an integer field.
For this to be efficient (no branches), an enumeration field is
nothing more than an integer field and the functions just cast the
given `struct bt_field *` object to a common integer field:
struct bt_field_enumeration {
struct bt_field_common_integer common;
};
* bt_field_string_clear(): new function to clear an existing string
field object.
Because field objects are reused and they are not reset when an event
is recycled (for performance), you must call bt_field_string_clear()
before you initially call bt_field_string_append() or
bt_field_string_append_len(). You don't need to call
bt_field_string_clear() before initially calling
bt_field_string_set_value().
* bt_field_sequence_get_length() returns an integer (`int64_t`), not a
field object. It is useless for the user to have the actual length
integer field.
* bt_field_sequence_set_length() accepts an integer (`uint64_t`), not a
field, for the same reason as the previous point.
* bt_field_variant_get_field() is removed. This function had a dual
role where it would select the current field using the value of
a tag field, and then return the current field. It is a vestige of
the CTF writer API.
Now, you set the tag value (plain integer) with
bt_field_variant_set_tag_signed() or
bt_field_variant_set_tag_unsigned(), and then you can borrow the
variant field's current (selected) field with
bt_field_variant_borrow_current_field().
You can still get the variant field's current tag value with
bt_field_variant_get_tag_signed() and
bt_field_variant_get_tag_unsigned(). Combined with
bt_field_type_enumeration_signed_find_mappings_by_value() and
bt_field_type_enumeration_unsigned_find_mappings_by_value(), you can
get the variant field's tag's mappings, so really there's no value
keeping the actual tag enumeration field.
The CTF writer field API is unchanged and still backward compatible.
Free packet header, packet context, and event header fields
===========================================================
It is possible to create a "free" packet header field (wrapper). This is
useful to fill a packet header field without creating the packet that
would create it, because in order to create this packet, you need a
stream, which is created from a stream class, and the stream class's ID
is found in the packet header field data.
The API is:
bt_trace_create_packet_header_field():
Creates a free packet header field
(`struct bt_packet_header_field *`).
bt_packet_header_field_borrow_field():
Borrows the underlying field object to fill it.
bt_packet_move_header():
Moves the free packet header field object to a given packet,
replacing its current packet header field. After calling this, and
on success, the object is considered to belong to the packet object:
you can discard it.
What happens here is that bt_packet_move_header() first recycles its
current packet header field wrapper object to the trace's packet
header field pool, and then replaces it with the provided object.
bt_packet_header_field_release():
Releases the given packet header field. This function exists for
error handling, to discard a packet header field without leaking
when you called bt_trace_create_packet_header_field(), but you
cannot call bt_packet_move_header().
Analogous functions exist for a packet context field:
* bt_stream_class_create_packet_context_field()
* bt_packet_context_field_borrow_field()
* bt_packet_move_context()
* bt_packet_context_field_release()
Having free packet header and packet context fields is necessary for a
`src.ctf.fs` component to work because it needs to know some field
values of both packet header and context fields before even creating a
stream object (to have a single stream for multiple data stream files,
for example), thus also before creating a packet.
Analogous functions exist for an event header field:
* bt_stream_class_create_event_header_field()
* bt_event_header_field_borrow_field()
* bt_event_move_header()
* bt_event_header_field_release()
Those three new (unique, non-shared) objects use the internal
`bt_field_wrapper` API.
Other changes
=============
* bt_clock_class_cycles_to_ns(): new public function to transform cycles
to nanoseconds from Epoch using a specific clock class. The
`src.ctf.fs` component used to create a clock value just to have this
through bt_clock_value_get_value_ns_from_epoch(), and then destroy it.
With this patch, you cannot create a free clock value anymore,
therefore this new function satisfies the existing use case.
* BT_LIB_LOG*() now accepts the `%!o` specifier to log the details of
an object pool.
Signed-off-by: Philippe Proulx <eeppeliteloop@gmail.com>
73 files changed:
This page took 0.032889 seconds and 4 git commands to generate.