bitmap_race.go raw

   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  
   7  package 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.
  13  type 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.
  19  func (data *RaceDetectHookData) raceDetectHookAlloc(size presenceSize) {
  20  	sp := make([]byte, size)
  21  	atomicStoreShadowPresence(&data.shadowPresence, &sp)
  22  }
  23  
  24  func (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  
  35  func (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  
  48  func (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.
  62  func (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.
  85  func 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.
 100  func 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.
 117  func 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  }
 127