bitmap_race.go

  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}