Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions docs/source/guide/resources.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
Resources
=========

A *resource* is an object that the framework owns for the whole job and
hands to the algorithms that declare they use it, such as a calibration
service, a geometry, or a library that may only be used by one caller at a
time.
Resources were introduced in Phlex v0.4.0.

Kinds of resources
------------------

Unlimited resources
Any number of calls may use the resource at the same time.
The type must *not* define a ``token_type`` member.
Algorithms receive a ``T const*``.

Serialized resources
Across *all* algorithms that declare the resource, at most one call
holding it runs at a time.
The type must define a ``token_type`` member, and algorithms receive a
value of that type:

- a pointer to the resource, if ``T*`` converts to ``token_type``
(for example ``using token_type = T const*;``);
- a copy of the resource, if ``token_type`` is ``T`` itself, in which
case ``T`` must be copy-constructible;
- otherwise, the result of ``T::token()``, which must return something
convertible to ``token_type`` and be either a ``static`` member
function or a ``const``-qualified member function.
A ``token()`` that is neither is rejected by ``add_serialized_resource``.

This is the framework's way to protect a non-thread-safe library that
several algorithms share.
A serialized resource limits how many calls run at once; it does not make
the calls run on one particular thread.

Each resource type may be registered only once per job.

Registering resources
---------------------

Resources are registered by a plugin that uses ``PHLEX_REGISTER_RESOURCES``
from ``phlex/resource.hpp`` and links to the ``phlex::resource`` CMake
target:

.. code-block:: cpp

#include "phlex/configuration.hpp"
#include "phlex/resource.hpp"

struct calibration {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nonblocking: please add a sentence explaining that these resource definitions should live in a common header included by both the registering resource plugin and every consuming algorithm module. That is the pattern used by test/plugins/resources_for_testing.hpp. The geometry example in phlex-examples likewise shares declarations through my_geometry.hpp, although it currently supplies geometry as a job-layer data product rather than a registered resource. A separate shared resource library is not generally required for the header-only types shown here: the existing resource integration test links the registration and consumer plugins separately.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 6d45107 right after the registration example. Resource types should be defined in a header included by both the registering plugin and every consuming module, which is what test/plugins/resources_for_testing.hpp does. The page also notes that header-only types like these need no separate shared library.

explicit calibration(int version) : version{version} {}
int version;
};

struct legacy_generator {
using token_type = legacy_generator*;
// ... wraps a library that is not thread-safe
};

PHLEX_REGISTER_RESOURCES(r, config)
{
r.add_unlimited_resource<calibration>(config.get<int>("version"));
r.add_serialized_resource<legacy_generator>();
}

Define the resource types in a header that is included both by the plugin
that registers them and by every algorithm module that uses them, as
``test/plugins/resources_for_testing.hpp`` does.
Header-only types like these need no separate shared library.

The arguments of ``add_unlimited_resource`` and ``add_serialized_resource``
are forwarded to the resource's constructor.

Resource plugins are configured in the top-level ``resources`` table and
are loaded before all other plugins.
Only C++ resource plugins are supported:

.. code-block:: jsonnet

{
resources: {
services: { cpp: 'my_resources', version: 3 },
},
}

Using resources
---------------

An algorithm declares the resources it uses by passing ``resource<T>{}``
(from ``phlex/core/resource_api.hpp``) to ``input_family(...)`` after all
product selectors.
The algorithm receives one extra parameter per resource, after its product
parameters and in the same order:

.. code-block:: cpp

m.transform(
"generate",
[](int seed, calibration const* calib, legacy_generator* gen) -> double { /* ... */ },
concurrency::unlimited)
.input_family(product_selector{.creator = "seeds", .layer = "event"},
resource<calibration>{},
resource<legacy_generator>{});

A node can run no more calls at once than both its own ``concurrency``
argument and its serialized resources allow.
Using a resource type that no plugin registered is an error.

Limitations
-----------

- Providers cannot use resources
(`issue #945 <https://github.com/Framework-R-D/phlex/issues/945>`_).
- Output modules registered with ``output(...)`` cannot use resources,
because they have no ``input_family(...)``.
- Algorithms written in Python cannot use resources.
- For an unfold, the resource parameters go to the unfold function, not to
the unfolder's constructor
(`issue #946 <https://github.com/Framework-R-D/phlex/issues/946>`_).
- Resources with a pool of several tokens are not supported yet.
- Phlex needs `oneTBB 2023.0.0 or later
<https://github.com/uxlfoundation/oneTBB/releases#release-v2023.0.0>`_,
which provides the preview ``flow::resource_limiter`` class; CMake
configuration fails without it.
The ``phlex::core`` target defines ``TBB_PREVIEW_FLOW_GRAPH_RESOURCE_LIMITING``
for its users.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
7 changes: 7 additions & 0 deletions phlex/core/resource_api.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
#include <type_traits>

namespace phlex {
/// @brief Declares that an algorithm uses the registered resource of type @p T.
///
/// Pass @c resource<T>{} to @c input_family(...) after all product selectors; each type
/// may appear only once. The algorithm receives one extra parameter per resource, after
/// its product parameters and in the same order: @c T @c const* for a resource registered
/// with @c add_unlimited_resource, and @c T::token_type for one registered with
/// @c add_serialized_resource.
template <typename T>
struct resource {
static_assert(!std::is_const_v<T>, "resource<T> must name a non-const resource type.");
Expand Down
16 changes: 16 additions & 0 deletions phlex/resource.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,29 @@ namespace phlex::detail {
{
}

/// @brief Registers a resource of type @p Resource that any number of calls may use at once.
///
/// @p Resource must not define @c token_type. The resource is constructed from @p args
/// and owned by the framework; algorithms that declare @c resource<Resource>{} in
/// @c input_family(...) receive a @c Resource @c const* parameter. Each resource type may
/// be registered only once per job.
template <typename Resource, typename... Args>
requires unlimited_resource_registration<Resource, Args...>
void add_unlimited_resource(Args&&... args) const
{
resources_.template add_unlimited<Resource>(std::forward<Args>(args)...);
}

/// @brief Registers a resource of type @p Resource that only one call at a time may use.
///
/// @p Resource must define @c token_type. The resource is constructed from @p args and
/// owned by the framework. Across all algorithms that declare @c resource<Resource>{},
/// at most one call runs at a time; it receives a @c token_type parameter, which is
/// a pointer to the resource if @c Resource* converts to @c token_type; a copy of the
/// resource if @c token_type is @c Resource, which must then be copy-constructible; and
/// otherwise the result of @c Resource::token(), which must return something convertible
/// to @c token_type and be a @c static or @c const-qualified member function. Each
/// resource type may be registered only once per job.
template <typename Resource, typename... Args>
requires serialized_resource_registration<Resource, Args...>
void add_serialized_resource(Args&&... args) const
Expand Down
Loading