1// Copyright 2024 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.
4
5//go:build race
6
7package impl
8
9// When running under race detector, we add a presence map of bytes, that we can access
10// in the hook functions so that we trigger the race detection whenever we have concurrent
11// Read-Writes or Write-Writes. The race detector does not otherwise detect invalid concurrent
12// access to lazy fields as all updates of bitmaps and pointers are done using atomic operations.
13type RaceDetectHookData struct {
14 shadowPresence *[]byte
15}
16
17// Hooks for presence bitmap operations that allocate, read and write the shadowPresence
18// using non-atomic operations.
19func (data *RaceDetectHookData) raceDetectHookAlloc(size presenceSize) {
20 sp := make([]byte, size)
21 atomicStoreShadowPresence(&data.shadowPresence, &sp)
22}
23
24func (p presence) raceDetectHookPresent(num uint32) {
25 data := p.toRaceDetectData()
26 if data == nil {
27 return
28 }
29 sp := atomicLoadShadowPresence(&data.shadowPresence)
30 if sp != nil {
31 _ = (*sp)[num]
32 }
33}
34
35func (p presence) raceDetectHookSetPresent(num uint32, size presenceSize) {
36 data := p.toRaceDetectData()
37 if data == nil {
38 return
39 }
40 sp := atomicLoadShadowPresence(&data.shadowPresence)
41 if sp == nil {
42 data.raceDetectHookAlloc(size)
43 sp = atomicLoadShadowPresence(&data.shadowPresence)
44 }
45 (*sp)[num] = 1
46}
47
48func (p presence) raceDetectHookClearPresent(num uint32) {
49 data := p.toRaceDetectData()
50 if data == nil {
51 return
52 }
53 sp := atomicLoadShadowPresence(&data.shadowPresence)
54 if sp != nil {
55 (*sp)[num] = 0
56
57 }
58}
59
60// raceDetectHookAllocAndCopy allocates a new shadowPresence slice at lazy and copies
61// shadowPresence bytes from src to lazy.
62func (p presence) raceDetectHookAllocAndCopy(q presence) {
63 sData := q.toRaceDetectData()
64 dData := p.toRaceDetectData()
65 if sData == nil {
66 return
67 }
68 srcSp := atomicLoadShadowPresence(&sData.shadowPresence)
69 if srcSp == nil {
70 atomicStoreShadowPresence(&dData.shadowPresence, nil)
71 return
72 }
73 n := len(*srcSp)
74 dSlice := make([]byte, n)
75 atomicStoreShadowPresence(&dData.shadowPresence, &dSlice)
76 for i := 0; i < n; i++ {
77 dSlice[i] = (*srcSp)[i]
78 }
79}
80
81// raceDetectHookPresent is called by the generated file interface
82// (*proto.internalFuncs) Present to optionally read an unprotected
83// shadow bitmap when race detection is enabled. In regular code it is
84// a noop.
85func raceDetectHookPresent(field *uint32, num uint32) {
86 data := findPointerToRaceDetectData(field, num)
87 if data == nil {
88 return
89 }
90 sp := atomicLoadShadowPresence(&data.shadowPresence)
91 if sp != nil {
92 _ = (*sp)[num]
93 }
94}
95
96// raceDetectHookSetPresent is called by the generated file interface
97// (*proto.internalFuncs) SetPresent to optionally write an unprotected
98// shadow bitmap when race detection is enabled. In regular code it is
99// a noop.
100func raceDetectHookSetPresent(field *uint32, num uint32, size presenceSize) {
101 data := findPointerToRaceDetectData(field, num)
102 if data == nil {
103 return
104 }
105 sp := atomicLoadShadowPresence(&data.shadowPresence)
106 if sp == nil {
107 data.raceDetectHookAlloc(size)
108 sp = atomicLoadShadowPresence(&data.shadowPresence)
109 }
110 (*sp)[num] = 1
111}
112
113// raceDetectHookClearPresent is called by the generated file interface
114// (*proto.internalFuncs) ClearPresent to optionally write an unprotected
115// shadow bitmap when race detection is enabled. In regular code it is
116// a noop.
117func raceDetectHookClearPresent(field *uint32, num uint32) {
118 data := findPointerToRaceDetectData(field, num)
119 if data == nil {
120 return
121 }
122 sp := atomicLoadShadowPresence(&data.shadowPresence)
123 if sp != nil {
124 (*sp)[num] = 0
125 }
126}