Parent and child tasks: blocking and cascading cancel #68
Labels
No labels
assets
bug
duplicate
enhancement
help wanted
invalid
question
testing
ui
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
liamjd/UntitledColonyBuilder#68
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Part of milestone #5. Depends on #66 and #67.
Tasks may spawn child tasks (construction spawns haul tasks per required resource; later, terrain levelling). The parent must not be workable until its children are complete, and cancelling a parent must cancel its children.
Design decisions
Two points from the original AC were revised before implementation:
TaskManager.add_subtask(parent, child)rather thanTask.add_child(task). The manager is the only thing that assigns ids, setscreation_seqand emitstask_added, so registration stays on one path; it is also the only place that can validate the parent is registered and not already COMPLETE/CANCELLED.Taskstays a data object with no back-reference to its manager (which would also create a second reference cycle).Task.parentis aWeakRef.Entity extends RefCountedand GDScript has no cycle collector, so a strongparent<->childrenpair leaks for the process lifetime. Expose it through a property getter so existing call sites keep working, and annotate theget_ref()result explicitly (it returnsVariant).Strong references to a task are held by
_taskswhile active,_completedafter completion, andparent.childrenthroughout. A cancelled child is erased from_tasksand survives only viaparent.children— cancelled children are therefore kept in the array so a cancelled subtree stays inspectable.AC
TaskManager.add_subtask(parent: Task, child: Task, priority: int = -1) -> intregisters the child and links both ways;-1means inherit the parent's priority, any other value overrides itpush_errorand does not register the childTask.can_start(ctx)becomes concrete on the base class: it returns false while any child is not COMPLETE, then delegates to a new abstractTask._can_start(ctx)hook. Existing stub tasks and tests are renamed to the hook._tasks, and hold a local strong reference to the subtree root across the cascade so weakparentlinks do not go null mid-walk.next_for()already uses rather than adding a parallel one — the explicit call is so the status is correct betweennext_for()calls (signals, UI).creation_seqspecial case for children inTaskManager.add()is removed: children getcreation_seq = idlike any other task. Ordering comes from priority inheritance plus the BLOCKED gate, not from a sequence trick. (The oldparent.creation_seq + 1gave every sibling the same value, which makes the unstablesort_customtie-break non-deterministic.)parentreturning null for a root taskCancelling a child on its own is out of scope here and tracked separately as #85 — the rules in this issue leave such a parent BLOCKED indefinitely, which is recorded (not endorsed) by
test_a_cancelled_child_leaves_its_parent_unworkableintest/unit/test_task_tree.gd.Closed in PR#86