EEZ Studio Async Native Actions: Return Completion to the GUI Thread

EEZ Flow can keep a native action pending while the application performs a slow operation. That does not mean a worker thread may resume the Flow engine directly. The async API describes an action's execution lifetime; thread ownership remains an application integration responsibility.

A useful design keeps the EEZ async handle in the GUI-owned operation record. The worker receives ordinary job data and returns a result identified by an application request ID. The GUI owner then updates the action's result properties and completes it once.

Read the async contract before creating a worker

EEZ's maintainer documents beginAsyncExecution(), endAsyncExecution() and the asynchronous user-property accessors in the async native-action guidance. The same guidance explicitly requires these calls to run from the GUI thread in a multithreaded environment.

The framework declarations expose the async handle and property operations. They do not create a worker, a queue or a cancellation policy for the application.

Inspect the framework revision used in the product before adopting a recipe. In the implementation snapshot reviewed here, beginAsyncExecution() captures the current action context. Completion resumes the sequence output and frees the async handle. That makes reusing a completed handle a particularly dangerous interpretation of a duplicate result.

These official files are unversioned snapshots read for this review. The operation model below is a proposed integration pattern, not a tested promise about every framework revision.

GUI owns the async handle; a worker receives job data and returns a request-tagged result; the GUI matches the request, updates properties and completes once.
Proposed integration pattern. Workers exchange application data; the GUI owner controls EEZ async context and completion. Page lifetime must be verified separately.

Keep the job and the Flow handle separate

At native-action entry, validate the requested work and decide whether the application can accept it. If another operation is active, apply an explicit policy: reject, coalesce, replace through a defined cancellation path, or enqueue within a known bound. Do not let reentry overwrite the only stored handle for an earlier action.

When beginning asynchronous execution, create a GUI-owned record that associates an application request ID with the EEZ handle and the operation's state. Pass the worker a copy of the necessary input data and that request ID. Avoid giving it a widget pointer or asking it to dereference the Flow handle.

The result message should carry enough information for the GUI to determine success, failure and any returned data. It should not require the worker to reach into UI-owned storage to discover where the result belongs. If data is referenced rather than copied, define its ownership and lifetime explicitly.

Also handle a job-submission failure after async execution has begun. A full worker queue must lead to a supported failure result and one completion path, not an action left pending forever. This edge case belongs in the design before normal operation is tested.

Make completion a GUI-owned state transition

When the GUI receives a result, first match its request ID to an active operation. If there is no matching active request, classify the message as stale or duplicate according to the application's policy. Do not use a previously completed pointer to ask the framework whether it is still valid.

For a matching operation, update result properties through the documented asynchronous accessors while the handle remains valid. Then complete the action through the supported API and retire the application's handle reference. Treat completion as consuming the application's permission to use that handle again.

Be careful about callbacks and continuation behavior. Completion can advance Flow execution, which may trigger further work. Establish the application's state transition so a newly invoked action cannot accidentally reuse the retiring operation record. The exact ordering must be checked against the chosen framework and the application's reentry policy.

LVGL's threading guidance provides the broader reason for a GUI owner: ordinary UI operations need serialization. Sending data back to that owner is different from holding a GUI mutex while a worker performs a long transaction.

Give navigation and cancellation their own policy

A slow operation can outlive the screen that initiated it. Do not infer that possession of an async handle makes every page variable or widget safe after navigation. Inspect the selected framework's lifetime behavior before combining async work with screen deletion.

For an initial integration, a simple policy can keep the initiating Flow lifetime valid until the operation reaches a terminal result. A more flexible design can move operation ownership to a retained application-level Flow. Either choice must be tested with the project's actual screen lifetime settings.

Cancellation has two meanings that must be distinguished: the UI no longer wants the result, and the underlying operation has actually stopped. A worker may still return a result after the first condition. Correlate it by request ID and resolve the outstanding action according to the defined policy. Do not silently drop the only completion while leaving the Flow pending.

Exercise failure paths with controlled results

Use a worker stub or safe service fixture to produce a small sequence of deliberate outcomes. Start with normal completion, then inject immediate submission failure, delayed completion, a reported operation error, a duplicate result and a result from an older request.

For each case, record native-action entry, accepted request ID, property update and completion count. Verify that one accepted action reaches exactly one intended terminal path. Check that a stale result cannot update a newer operation's output properties.

Finally, exercise the chosen navigation policy while a request is pending. The test should establish either that the required Flow lifetime remains valid or that the supported cancellation/lifecycle mechanism resolves it safely. A responsive screen alone is not sufficient evidence.

No worker or target test is reported here. The architectural boundary is the useful result: workers perform application work, while the GUI owner retains responsibility for EEZ async context, property updates and exactly-once completion handling.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *