MCPcopy
hub / github.com/purpleidea/mgmt / TestValueResSendRecvAliasRace

Function TestValueResSendRecvAliasRace

engine/resources/value_race_test.go:66–144  ·  view source on GitHub ↗

TestValueResSendRecvAliasRace reproduces the value-resource send/recv aliasing race that survives the issue #926 Sendable fix. ValueRes.CheckApply does `obj.cachedAny = obj.Any` and then publishes `&ValueSends{Any: obj.cachedAny}`. That makes the published snapshot's `Any` pointer alias obj.Any's l

(t *testing.T)

Source from the content-addressed store, hash-verified

64// gates the test) with the in-place recv write emulated, and the receiver reads
65// Sent() the way engine/graph/sendrecv.go does.
66func TestValueResSendRecvAliasRace(t *testing.T) {
67 res := &ValueRes{}
68 res.SetKind("value")
69 res.SetName("test-value-race")
70
71 api := (&local.API{
72 Prefix: t.TempDir(),
73 Logf: func(string, ...interface{}) {},
74 }).Init()
75
76 // A persistent recv slot so every CheckApply takes the
77 // `obj.cachedAny = obj.Any` branch, exactly as the engine would after
78 // receiving on `any`.
79 recv := map[string]*engine.Send{
80 "any": {Changed: true},
81 }
82
83 init := &engine.Init{
84 Send: engine.GenerateSendFunc(res), // routes into the Sendable trait
85 Recv: func() map[string]*engine.Send { return recv },
86 Local: api,
87 Logf: func(string, ...interface{}) {},
88 }
89 if err := res.Init(init); err != nil {
90 t.Fatalf("func Init failed: %+v", err)
91 }
92
93 // obj.Any must be a stable, non-nil *interface{} across cycles so the
94 // recv in-place write keeps aliasing prior snapshots. This mirrors
95 // types.Into instantiating the pointer once and then mutating in place.
96 any := interface{}("value")
97 res.Any = &any
98
99 ctx := context.Background()
100 const iterations = 100000
101 start := make(chan struct{})
102 wg := &sync.WaitGroup{}
103
104 wg.Add(2)
105 // Sender / value Worker: emulate the recv in-place write into obj.Any's
106 // pointee, then run the real CheckApply which publishes the snapshot.
107 go func() {
108 defer wg.Done()
109 <-start
110
111 for i := 0; i < iterations; i++ {
112 // Equivalent to lang/types.Into writing into the
113 // existing non-nil *interface{} in place (rv.Set on the
114 // interface word). Same value keeps CheckApply cheap,
115 // and the race is on the access, not the contents.
116 *res.Any = "value"
117 if _, err := res.CheckApply(ctx, true); err != nil {
118 t.Errorf("method CheckApply failed: %+v", err)
119 return
120 }
121 runtime.Gosched()
122 }
123 }()

Callers

nothing calls this directly

Calls 14

InitMethod · 0.95
CheckApplyMethod · 0.95
GenerateSendFuncFunction · 0.92
ValueOfFunction · 0.92
closeFunction · 0.85
TempDirMethod · 0.80
AddMethod · 0.80
SetKindMethod · 0.65
SetNameMethod · 0.65
InitMethod · 0.65
BackgroundMethod · 0.65
DoneMethod · 0.65

Tested by

no test coverage detected