Components
There are two types of components in OpenCXL.
- LabeledComponent (
opencxl/util/component.py
) - RunnableComponent (
opencxl/util/component.py
)
Most major components in OpenCXL are one of these two classes. They were created to facilitate logging and debugging.
LabeledComponent
As the name implies, LabeledComponent
provides label functionality to the component. It allows
one to differentiate multiple instances of the components. e.g. CxlRootPortDevice:Port0
. It is
also the base class for RunnableComponent
.
RunnableComponent
A component should be a RunnableComponent
if one of the following conditions apply:
- Needs an ability to run and stop.
- Needs to "block" the flow (usually initialization) until it is ready.
As mentioned, it is also a subclass of LabeledComponent
, hence all labeled features come with it.
Usage
Labels
When instantiating one of these components, an optional label
argument could be passed in to use
for differentiating a specific instance of the class.
class CxlRootPortDevice(RunnableComponent):
def __init__(
self,
...
label: Optional[str] = None,
...
):
super().__init__(label)
...
Instantiate:
label = f"Port{port_index}"
root_port_device = CxlRootPortDevice(..., label=label)
Depending on the port_index
, the same log line will output different tags.
async def _cxl_mem_read(self, addr: int) -> Result:
logger.info(self._create_message(f"CXL.mem Read: addr=0x{addr:x}"))
Depending on which object, would output:
[CxlRootPortDevice:Port0] CXL.mem Read: HPA addr:0x100000000000
[CxlRootPortDevice:Port5] CXL.mem Read: HPA addr:0x150000000000
This tagged message could be created using _create_message()
method. If you do not specify label
when instantiating this component, the tag portion (e.g.:Port0
) of the label will not be
included, hence would only show CxlRootPortDevice
.
Run and Stop
When defining a RunnableComponent
, two methods must overriden.
_run()
_stop()
_run() Method
_run()
of a component is called from its base class by RunnableComponent.run()
with synchronization mechanisms around it. Readiness status is set by _change_status_to_running()
method. Once the Prior to setting the object as ready, all member objects should be ready. wait_for_ready()
method can be used to ensure this.
class CxlIoManager(RunnableComponent):
def __init__():
...
def _run():
...
await self._change_status_to_running()
await asyncio.gather(*run_tasks)
Instantiate and run from another component.
class CxlPortDevice(RunnableComponent):
def __init__(self, ...):
self._cxl_io_manager = CxlIoManager(...)
...
async def _run(self):
run_tasks = [
create_task(self._cxl_io_manager.run()),
...
]
wait_tasks = [
create_task(self._cxl_io_manager.wait_for_ready()),
...
]
await gather(*wait_tasks)
await self._change_status_to_running()
await gather(*run_tasks)
...
_stop() Method
Similar to _run()
, _stop()
is called from RunnableComponent.stop()
. When _stop()
returns, the object should be ready to be restarted and be ready to be destroyed cleanly.
async def _stop(self):
tasks = [
create_task(self._cxl_io_manager.stop()),
...
]
await gather(*tasks)
Summary
To recap, a component class in OpenCXL should:
- Inherit from either
LabeledComponent
orRunnableComponent
class.- A vast majority of the time, likely from
RunnableComponent
- A vast majority of the time, likely from
- Use
_create_message()
method to create properly tagged logs. - Define
_run()
and_stop()
method - Use
_change_status_to_running()
method to indicate itself as ready