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 allg, hchan, reflectOffs, timers, traceBuf < fin;
47 traceBuf < traceStrings;
48 allg, hchan, notifyList, reflectOffs, timers, traceStrings < mspanSpecial, profInsert, profBlock, profMemActive, gcBitsArenas, spanSetSpine, mheapSpecial;
49 profMemActive < profMemFuture;
50 hchan, root, sched, traceStrings, notifyList, fin < trace;
51 trace < traceStackTab;
53 rwmutexW, sysmon < rwmutexR;
54 gcBitsArenas, netpollInit, profBlock, profInsert, profMemFuture, spanSetSpine, traceStackTab < gscan;
55 gscan, rwmutexR < stackpool;
58 # Generally, hchan must be acquired before gscan. But in one specific
59 # case (in syncadjustsudogs from markroot after the g has been suspended
60 # by suspendG), we allow gscan to be acquired, and then an hchan lock. To
61 # allow this case, we use this hchanLeaf rank in syncadjustsudogs(),
62 # rather than hchan. By using this special rank, we don't allow any further
63 # locks to be acquired other than more hchan locks.
66 hchan, notifyList < sudog;
67 defer, gscan, mspanSpecial, sudog < wbufSpans;
68 stackLarge, stackpool, wbufSpans < mheap;
69 mheap, mheapSpecial < globalAlloc;
71 # panic is handled specially. It is implicitly below all other locks.
75 // cyclicRanks lists lock ranks that allow multiple locks of the same
76 // rank to be acquired simultaneously. The runtime enforces ordering
77 // within these ranks using a separate mechanism.
78 var cyclicRanks = map[string]bool{
79 // Multiple timers are locked simultaneously in destroy().
81 // Multiple hchans are acquired in hchan.sortkey() order in
84 // Multiple hchanLeafs are acquired in hchan.sortkey() order in
85 // syncadjustsudogs().
90 flagO := flag.String("o", "", "write to `file` instead of stdout")
91 flagDot := flag.Bool("dot", false, "emit graphviz output instead of Go")
94 fmt.Fprintf(os.Stderr, "too many arguments")
98 g, err := dag.Parse(ranks)
106 g.TransitiveReduction()
107 // Add cyclic edges for visualization.
108 for k := range cyclicRanks {
111 // Reverse the graph. It's much easier to read this as
112 // a "<" partial order than a ">" partial order. This
113 // ways, locks are acquired from the top going down
114 // and time moves forward over the edges instead of
122 out, err = format.Source(b.Bytes())
129 err = os.WriteFile(*flagO, out, 0666)
131 _, err = os.Stdout.Write(out)
138 func generateGo(w io.Writer, g *dag.Graph) {
139 fmt.Fprintf(w, `// Code generated by mklockrank.go; DO NOT EDIT.
147 // Create numeric ranks.
149 for i, j := 0, len(topo)-1; i < j; i, j = i+1, j-1 {
150 topo[i], topo[j] = topo[j], topo[i]
153 // Constants representing the ranks of all non-leaf runtime locks, in rank order.
154 // Locks with lower rank must be taken before locks with higher rank,
155 // in addition to satisfying the partial order in lockPartialOrder.
156 // A few ranks allow self-cycles, which are specified in lockPartialOrder.
158 lockRankUnknown lockRank = iota
161 for _, rank := range topo {
162 fmt.Fprintf(w, "\t%s\n", cname(rank))
166 // lockRankLeafRank is the rank of lock that does not have a declared rank,
167 // and hence is a leaf lock.
168 const lockRankLeafRank lockRank = 1000
171 // Create string table.
173 // lockNames gives the names associated with each of the above ranks.
174 var lockNames = []string{
176 for _, rank := range topo {
177 fmt.Fprintf(w, "\t%s: %q,\n", cname(rank), rank)
181 func (rank lockRank) String() string {
185 if rank == lockRankLeafRank {
188 if rank < 0 || int(rank) >= len(lockNames) {
191 return lockNames[rank]
195 // Create partial order structure.
197 // lockPartialOrder is the transitive closure of the lock rank graph.
198 // An entry for rank X lists all of the ranks that can already be held
199 // when rank X is acquired.
201 // Lock ranks that allow self-cycles list themselves.
202 var lockPartialOrder [][]lockRank = [][]lockRank{
204 for _, rank := range topo {
206 for _, before := range g.Edges(rank) {
207 list = append(list, cname(before))
209 if cyclicRanks[rank] {
210 list = append(list, cname(rank))
213 fmt.Fprintf(w, "\t%s: {%s},\n", cname(rank), strings.Join(list, ", "))
215 fmt.Fprintf(w, "}\n")
218 // cname returns the Go const name for the given lock rank label.
219 func cname(label string) string {
220 return "lockRank" + strings.ToUpper(label[:1]) + label[1:]
223 // generateDot emits a Graphviz dot representation of g to w.
224 func generateDot(w io.Writer, g *dag.Graph) {
225 fmt.Fprintf(w, "digraph g {\n")
228 for _, node := range g.Nodes {
229 fmt.Fprintf(w, "%q;\n", node)
233 for _, node := range g.Nodes {
234 for _, to := range g.Edges(node) {
235 fmt.Fprintf(w, "%q -> %q;\n", node, to)
239 fmt.Fprintf(w, "}\n")