|
| 1 | +// Copyright 2025 CUE Authors |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// https://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +package adt |
| 16 | + |
| 17 | +import "fmt" |
| 18 | + |
| 19 | +// depKind is a type of dependency that is tracked with incDependent and |
| 20 | +// decDependent. For each there should be matching pairs passed to these |
| 21 | +// functions. The debugger, when used, tracks and verifies that these |
| 22 | +// dependencies are balanced. |
| 23 | +type depKind int |
| 24 | + |
| 25 | +//go:generate go run golang.org/x/tools/cmd/stringer -type=depKind |
| 26 | + |
| 27 | +const ( |
| 28 | + // PARENT dependencies are used to track the completion of parent |
| 29 | + // closedContexts within the closedness tree. |
| 30 | + PARENT depKind = iota + 1 |
| 31 | + |
| 32 | + // ARC dependencies are used to track the completion of corresponding |
| 33 | + // closedContexts in parent Vertices. |
| 34 | + ARC |
| 35 | + |
| 36 | + // NOTIFY dependencies keep a note while dependent conjuncts are collected |
| 37 | + NOTIFY // root node of source |
| 38 | + |
| 39 | + // TASK dependencies are used to track the completion of a task. |
| 40 | + TASK |
| 41 | + |
| 42 | + // DISJUNCT is used to mark an incomplete disjunct. |
| 43 | + DISJUNCT |
| 44 | + |
| 45 | + // EVAL tracks that the conjunct associated with a closeContext has been |
| 46 | + // inserted using scheduleConjunct. A closeContext may not be deleted |
| 47 | + // as long as the conjunct has not been evaluated yet. |
| 48 | + // This prevents a node from being released if an ARC decrement happens |
| 49 | + // before a node is evaluated. |
| 50 | + EVAL |
| 51 | + |
| 52 | + // COMP tracks pending arcs in comprehensions. |
| 53 | + COMP |
| 54 | + |
| 55 | + // ROOT dependencies are used to track that all nodes of parents are |
| 56 | + // added to a tree. |
| 57 | + ROOT // Always refers to self. |
| 58 | + |
| 59 | + // INIT dependencies are used to hold ownership of a closeContext during |
| 60 | + // initialization and prevent it from being finalized when scheduling a |
| 61 | + // node's conjuncts. |
| 62 | + INIT |
| 63 | + |
| 64 | + // DEFER is used to track recursive processing of a node. |
| 65 | + DEFER // Always refers to self. |
| 66 | + |
| 67 | + // SHARED is used to track shared nodes. The processing of shared nodes may |
| 68 | + // change until all other conjuncts have been processed. |
| 69 | + SHARED |
| 70 | + |
| 71 | + // TEST is used for testing notifications. |
| 72 | + TEST // Always refers to self. |
| 73 | +) |
| 74 | + |
| 75 | +// ccDep is used to record counters which is used for debugging only. |
| 76 | +// It is purpose is to be precise about matching inc/dec as well as to be able |
| 77 | +// to traverse dependency. |
| 78 | +type ccDep struct { |
| 79 | + dependency *closeContext |
| 80 | + kind depKind |
| 81 | + decremented bool |
| 82 | + |
| 83 | + // task keeps a reference to a task for TASK dependencies. |
| 84 | + task *task |
| 85 | + // taskID indicates the sequence number of a task within a scheduler. |
| 86 | + taskID int |
| 87 | +} |
| 88 | + |
| 89 | +func (c *closeContext) addDependent(ctx *OpContext, kind depKind, dependant *closeContext) *ccDep { |
| 90 | + if dependant == nil { |
| 91 | + dependant = c |
| 92 | + } |
| 93 | + |
| 94 | + if ctx.LogEval > 1 { |
| 95 | + ctx.Logf(ctx.vertex, "INC(%s) %v %p parent: %p %d\n", kind, c.Label(), c, c.parent, c.conjunctCount) |
| 96 | + } |
| 97 | + |
| 98 | + dep := &ccDep{kind: kind, dependency: dependant} |
| 99 | + c.dependencies = append(c.dependencies, dep) |
| 100 | + |
| 101 | + return dep |
| 102 | +} |
| 103 | + |
| 104 | +// matchDecrement checks that this decrement matches a previous increment. |
| 105 | +func (c *closeContext) matchDecrement(ctx *OpContext, v *Vertex, kind depKind, dependant *closeContext) { |
| 106 | + if dependant == nil { |
| 107 | + dependant = c |
| 108 | + } |
| 109 | + |
| 110 | + if ctx.LogEval > 1 { |
| 111 | + ctx.Logf(ctx.vertex, "DEC(%s) %v %p %d\n", kind, c.Label(), c, c.conjunctCount) |
| 112 | + } |
| 113 | + |
| 114 | + for _, d := range c.dependencies { |
| 115 | + if d.kind != kind { |
| 116 | + continue |
| 117 | + } |
| 118 | + if d.dependency != dependant { |
| 119 | + continue |
| 120 | + } |
| 121 | + // Only one typ-dependant pair possible. |
| 122 | + if d.decremented { |
| 123 | + // There might be a duplicate entry, so continue searching. |
| 124 | + continue |
| 125 | + } |
| 126 | + |
| 127 | + d.decremented = true |
| 128 | + return |
| 129 | + } |
| 130 | + |
| 131 | + if DebugDeps { |
| 132 | + panic(fmt.Sprintf("unmatched decrement: %s", kind)) |
| 133 | + } |
| 134 | +} |
0 commit comments