1 // Copyright 2022 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
7 // mklockrank records the static rank graph of the locks in the
8 // runtime and generates the rank checking structures in lockrank.go.
23 // ranks describes the lock rank graph. See "go doc internal/dag" for
26 // "a < b" means a must be acquired before b if both are held
27 // (or, if b is held, a cannot be acquired).
29 // "NONE < a" means no locks may be held when a is acquired.
31 // If a lock is not given a rank, then it is assumed to be a leaf
32 // lock, which means no other lock can be acquired while it is held.
33 // Therefore, leaf locks do not need to be given an explicit rank.
35 // TODO: It's often hard to correlate rank names to locks. Change
36 // these to be more consistent with the locks they label.
38 NONE < sysmon, sweepWaiters, assistQueue, cpuprof, sweep, pollDesc, deadlock, itab, notifyList, root, rwmutexW, defer;
39 sysmon < scavenge, forcegc;
40 assistQueue, cpuprof, forcegc, pollDesc, scavenge, sweep, sweepWaiters < sched;
44 scavenge, sweep < hchan;
46 traceBuf < traceStrings;
47 allg, hchan, notifyList, reflectOffs, timers, traceStrings < mspanSpecial, profInsert, profBlock, profMemActive, gcBitsArenas, spanSetSpine, mheapSpecial, fin;
48 profMemActive < profMemFuture;
49 hchan, root, sched, traceStrings, notifyList, fin < trace;
50 trace < traceStackTab;
52 rwmutexW, sysmon < rwmutexR;
53 gcBitsArenas, netpollInit, profBlock, profInsert, profMemFuture, spanSetSpine, traceStackTab < gscan;
54 gscan, rwmutexR < stackpool;
57 # Generally, hchan must be acquired before gscan. But in one specific
58 # case (in syncadjustsudogs from markroot after the g has been suspended
59 # by suspendG), we allow gscan to be acquired, and then an hchan lock. To
60 # allow this case, we use this hchanLeaf rank in syncadjustsudogs(),
61 # rather than hchan. By using this special rank, we don't allow any further
62 # locks to be acquired other than more hchan locks.
65 hchan, notifyList < sudog;
66 defer, gscan, mspanSpecial, sudog < wbufSpans;
67 stackLarge, stackpool, wbufSpans < mheap;
68 mheap, mheapSpecial < globalAlloc;
70 # panic is handled specially. It is implicitly below all other locks.
74 // cyclicRanks lists lock ranks that allow multiple locks of the same
75 // rank to be acquired simultaneously. The runtime enforces ordering
76 // within these ranks using a separate mechanism.
77 var cyclicRanks = map[string]bool{
78 // Multiple timers are locked simultaneously in destroy().
80 // Multiple hchans are acquired in hchan.sortkey() order in
83 // Multiple hchanLeafs are acquired in hchan.sortkey() order in
84 // syncadjustsudogs().
89 flagO := flag.String("o", "", "write to `file` instead of stdout")
90 flagDot := flag.Bool("dot", false, "emit graphviz output instead of Go")
93 fmt.Fprintf(os.Stderr, "too many arguments")
97 g, err := dag.Parse(ranks)
105 g.TransitiveReduction()
106 // Add cyclic edges for visualization.
107 for k := range cyclicRanks {
110 // Reverse the graph. It's much easier to read this as
111 // a "<" partial order than a ">" partial order. This
112 // ways, locks are acquired from the top going down
113 // and time moves forward over the edges instead of
121 out, err = format.Source(b.Bytes())
128 err = os.WriteFile(*flagO, out, 0666)
130 _, err = os.Stdout.Write(out)
137 func generateGo(w io.Writer, g *dag.Graph) {
138 fmt.Fprintf(w, `// Code generated by mklockrank.go; DO NOT EDIT.
146 // Create numeric ranks.
148 for i, j := 0, len(topo)-1; i < j; i, j = i+1, j-1 {
149 topo[i], topo[j] = topo[j], topo[i]
152 // Constants representing the ranks of all non-leaf runtime locks, in rank order.
153 // Locks with lower rank must be taken before locks with higher rank,
154 // in addition to satisfying the partial order in lockPartialOrder.
155 // A few ranks allow self-cycles, which are specified in lockPartialOrder.
157 lockRankUnknown lockRank = iota
160 for _, rank := range topo {
161 fmt.Fprintf(w, "\t%s\n", cname(rank))
165 // lockRankLeafRank is the rank of lock that does not have a declared rank,
166 // and hence is a leaf lock.
167 const lockRankLeafRank lockRank = 1000
170 // Create string table.
172 // lockNames gives the names associated with each of the above ranks.
173 var lockNames = []string{
175 for _, rank := range topo {
176 fmt.Fprintf(w, "\t%s: %q,\n", cname(rank), rank)
180 func (rank lockRank) String() string {
184 if rank == lockRankLeafRank {
187 if rank < 0 || int(rank) >= len(lockNames) {
190 return lockNames[rank]
194 // Create partial order structure.
196 // lockPartialOrder is the transitive closure of the lock rank graph.
197 // An entry for rank X lists all of the ranks that can already be held
198 // when rank X is acquired.
200 // Lock ranks that allow self-cycles list themselves.
201 var lockPartialOrder [][]lockRank = [][]lockRank{
203 for _, rank := range topo {
205 for _, before := range g.Edges(rank) {
206 list = append(list, cname(before))
208 if cyclicRanks[rank] {
209 list = append(list, cname(rank))
212 fmt.Fprintf(w, "\t%s: {%s},\n", cname(rank), strings.Join(list, ", "))
214 fmt.Fprintf(w, "}\n")
217 // cname returns the Go const name for the given lock rank label.
218 func cname(label string) string {
219 return "lockRank" + strings.ToUpper(label[:1]) + label[1:]
222 // generateDot emits a Graphviz dot representation of g to w.
223 func generateDot(w io.Writer, g *dag.Graph) {
224 fmt.Fprintf(w, "digraph g {\n")
227 for _, node := range g.Nodes {
228 fmt.Fprintf(w, "%q;\n", node)
232 for _, node := range g.Nodes {
233 for _, to := range g.Edges(node) {
234 fmt.Fprintf(w, "%q -> %q;\n", node, to)
238 fmt.Fprintf(w, "}\n")