Skip to content

Commit 1b7053e

Browse files
authored
Store a reference to the engine in receiver objects (#478)
This PR fixes a bug which occurs when creating and using a higher order formula engine, without holding a reference to it.
2 parents 89bde34 + d6ae901 commit 1b7053e

File tree

2 files changed

+15
-1
lines changed

2 files changed

+15
-1
lines changed

RELEASE_NOTES.md

+2
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,5 @@ New `Quantity` types! These types can have units (power, current, voltage, etc.)
3737
- Two bugs in the ring buffer which is used by the `MovingWindow` class were fixed:
3838
- `len(buffer)` was not considering potentially existing gaps (areas without elements) in the buffer.
3939
- A off-by-one error in the gap calculation logic was fixed that recorded a gap when there was none if an element with a future timestamp was added that would create a gap of exactly 1.
40+
41+
- A formula engine lifetime issue, when creating higher order formula receivers without holding on to a reference to the engine, was fixed.

src/frequenz/sdk/timeseries/_formula_engine/_formula_engine.py

+13-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,19 @@ def new_receiver(
360360
if self._task is None:
361361
self._task = asyncio.create_task(self._run())
362362

363-
return self._channel.new_receiver(name, max_size)
363+
recv = self._channel.new_receiver(name, max_size)
364+
365+
# This is a hack to ensure that the lifetime of the engine is tied to the
366+
# lifetime of the receiver. This is necessary because the engine is a task that
367+
# runs forever, and in cases where higher order built for example with the below
368+
# idiom, the user would hold no references to the engine and it could get
369+
# garbage collected before the receiver. This behaviour is explained in the
370+
# `asyncio.create_task` docs here:
371+
# https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task
372+
#
373+
# formula = (grid_power_engine + bat_power_engine).build().new_receiver()
374+
recv._engine_reference = self # type: ignore # pylint: disable=protected-access
375+
return recv
364376

365377

366378
class FormulaEngine3Phase(

0 commit comments

Comments
 (0)