this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

internal/core/runtime: add lock protection to loaded map

The Runtime.loaded map was accessed without synchronization, causing
data races when Value.Syntax() calls BuildData() while other goroutines
call SetBuildData().

Fix by adding index.lock protection to SetBuildData and BuildData,
matching the pattern already used for imports, importsByPath, and
importsByBuild maps in the same index.

Updates #2733

Signed-off-by: Marcel van Lohuizen <mpvl@gmail.com>
Change-Id: I457d62b1ba70676042a9339bee85d606a0417933
Reviewed-on: https://cue.gerrithub.io/c/cue-lang/cue/+/1230713
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Unity-Result: CUE porcuepine <cue.porcuepine@gmail.com>
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>

+74
+4
internal/core/runtime/runtime.go
··· 47 47 } 48 48 49 49 func (r *Runtime) SetBuildData(b *build.Instance, x interface{}) { 50 + r.index.lock.Lock() 51 + defer r.index.lock.Unlock() 50 52 r.loaded[b] = x 51 53 } 52 54 53 55 func (r *Runtime) BuildData(b *build.Instance) (x interface{}, ok bool) { 56 + r.index.lock.RLock() 57 + defer r.index.lock.RUnlock() 54 58 x, ok = r.loaded[b] 55 59 return x, ok 56 60 }
+70
internal/core/runtime/runtime_test.go
··· 1 + // Copyright 2025 CUE Authors 2 + // 3 + // Licensed under the Apache License, Version 2.0 (the "License"); 4 + // you may not use this file except in compliance with the License. 5 + // You may obtain a copy of the License at 6 + // 7 + // http://www.apache.org/licenses/LICENSE-2.0 8 + // 9 + // Unless required by applicable law or agreed to in writing, software 10 + // distributed under the License is distributed on an "AS IS" BASIS, 11 + // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 + // See the License for the specific language governing permissions and 13 + // limitations under the License. 14 + 15 + package runtime 16 + 17 + import ( 18 + "sync" 19 + "testing" 20 + 21 + "cuelang.org/go/cue/build" 22 + ) 23 + 24 + // TestBuildDataConcurrent tests that concurrent access to BuildData and 25 + // SetBuildData is race-free. This is a regression test for 26 + // https://github.com/cue-lang/cue/issues/2733 27 + // 28 + // The race occurred when one goroutine called SetBuildData (map write) while 29 + // another called BuildData (map read) on the same runtime's loaded map. 30 + func TestBuildDataConcurrent(t *testing.T) { 31 + r := New() 32 + 33 + // Create multiple build instances to operate on. 34 + instances := make([]*build.Instance, 10) 35 + for i := range instances { 36 + instances[i] = &build.Instance{} 37 + } 38 + 39 + // Concurrently read and write to the loaded map. 40 + // Without the fix, this would cause a fatal error: 41 + // "concurrent map read and map write" 42 + var wg sync.WaitGroup 43 + for range 100 { 44 + wg.Add(2) 45 + // Writer goroutine 46 + go func() { 47 + defer wg.Done() 48 + for i, inst := range instances { 49 + r.SetBuildData(inst, i) 50 + } 51 + }() 52 + // Reader goroutine 53 + go func() { 54 + defer wg.Done() 55 + for _, inst := range instances { 56 + r.BuildData(inst) 57 + } 58 + }() 59 + } 60 + wg.Wait() 61 + 62 + // Verify data integrity after concurrent access. 63 + for i, inst := range instances { 64 + if v, ok := r.BuildData(inst); ok { 65 + if v != i { 66 + t.Errorf("BuildData(%d) = %v, want %d", i, v, i) 67 + } 68 + } 69 + } 70 + }