You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
isTaskCancelled() shipped in v1.8.0 and works, but it's poll-only: the callback has to
remember to check it. @devroble's point in the discussion is fair — cancellation shouldn't
be something every app re-implements by hand.
flutter_workmanager has onTaskStopped(taskName, stopReason) on executeTask (landed in
their 0.10.1). Worth being precise about what it actually is, because it's easy to
oversell: it's still cooperative. It doesn't preempt a running await either. It's a
notification plus a bounded cleanup window, and then the engine gets torn down. Their stopReason is Android-only and API 31+ only, and it never fires on iOS at all (the Pigeon
stub is generated into their Swift but nothing calls it).
So the thing to build here is the same shape, which is also the grace-period idea I'd
already floated in the discussion:
on cancel, fire onTaskStopped into the Dart side so the callback can persist progress
/ close handles
await that handler with a bounded budget
tear down after the budget expires
Scope
New DartWorker(onStoppedId:, cancelGrace:). cancelGrace defaults to notify-only, no
kill — i.e. current behaviour plus a notification. Duration.zero = kill as soon as the
handler returns. Keeping the default at notify-only so this can't regress anyone on
v1.8.x, and because the discussion question of which should be the default is still open.
Handler registered through initialize(onStoppedHandlers: {...}), mirroring dartWorkers.
Needs its own registry because the signature differs (no bool return, and stopReason
gets added to it later).
The Flutter engine is shared across concurrent DartWorkers, and every dispose() is
gated on activeTaskCount <= 0 because tearing it down while another task still holds a methodChannel reference is a JNI crash on freed memory. So there is no per-task hard
kill: it's kill-everything-in-flight or kill-nothing. When the cancelled task is the only
one running (the common case) the hard stop works; when something else is running
concurrently the cancelled one is left to linger and we log it loudly rather than
collaterally aborting unrelated work. Per-task kill would mean one engine per worker at
~50 MB each, which isn't obviously worth it for a worker already documented as risky past
3 concurrent.
stopReason parity is deliberately not in this issue — getStopReason() only exists on ListenableWorker, so it has to come through WorkerEnvironment in kmpworkmanager. That's
a core release; it's additive and shouldn't block the hook.
Follow-up to #67 / discussion #66.
isTaskCancelled()shipped in v1.8.0 and works, but it's poll-only: the callback has toremember to check it. @devroble's point in the discussion is fair — cancellation shouldn't
be something every app re-implements by hand.
flutter_workmanager has
onTaskStopped(taskName, stopReason)onexecuteTask(landed intheir 0.10.1). Worth being precise about what it actually is, because it's easy to
oversell: it's still cooperative. It doesn't preempt a running
awaiteither. It's anotification plus a bounded cleanup window, and then the engine gets torn down. Their
stopReasonis Android-only and API 31+ only, and it never fires on iOS at all (the Pigeonstub is generated into their Swift but nothing calls it).
So the thing to build here is the same shape, which is also the grace-period idea I'd
already floated in the discussion:
onTaskStoppedinto the Dart side so the callback can persist progress/ close handles
Scope
DartWorker(onStoppedId:, cancelGrace:).cancelGracedefaults to notify-only, nokill — i.e. current behaviour plus a notification.
Duration.zero= kill as soon as thehandler returns. Keeping the default at notify-only so this can't regress anyone on
v1.8.x, and because the discussion question of which should be the default is still open.
initialize(onStoppedHandlers: {...}), mirroringdartWorkers.Needs its own registry because the signature differs (no
boolreturn, andstopReasongets added to it later).
_callbackDispatcher(resolves by callback handle) and the main-isolate
method_channel.darthandler(resolves by callback id). fix: restore DartWorker progress events and TaskStore status (#38, #39) #40 shipped the progress fix for only one of these and left
iOS broken until fix(ios): complete DartWorker progress on iOS + device tests (#38, follow-up to #40) #41 — not repeating that.
running Task there since fix(dart-worker): cooperative cancellation via isTaskCancelled (#66) #68, so we have a real signal — this is one place we can do
better than the prior art.
Known limitation, going in with eyes open
The Flutter engine is shared across concurrent DartWorkers, and every
dispose()isgated on
activeTaskCount <= 0because tearing it down while another task still holds amethodChannelreference is a JNI crash on freed memory. So there is no per-task hardkill: it's kill-everything-in-flight or kill-nothing. When the cancelled task is the only
one running (the common case) the hard stop works; when something else is running
concurrently the cancelled one is left to linger and we log it loudly rather than
collaterally aborting unrelated work. Per-task kill would mean one engine per worker at
~50 MB each, which isn't obviously worth it for a worker already documented as risky past
3 concurrent.
stopReasonparity is deliberately not in this issue —getStopReason()only exists onListenableWorker, so it has to come throughWorkerEnvironmentin kmpworkmanager. That'sa core release; it's additive and shouldn't block the hook.
isTaskCancelled()keeps working unchanged.