@@ -96,6 +96,78 @@ func TestWaitGroupRaceCondition(t *testing.T) {
96
96
}
97
97
}
98
98
99
+ // TestStackTraceScenario tests the specific scenario from the original stack trace
100
+ // where magiclink.Wait() is called during endpoint swapping.
101
+ func TestStackTraceScenario (t * testing.T ) {
102
+ // Create a temp file to simulate a TUN device
103
+ tmpFile , err := os .CreateTemp ("" , "test_tun" )
104
+ if err != nil {
105
+ t .Skip ("Cannot create temp file for test" )
106
+ }
107
+ defer os .Remove (tmpFile .Name ())
108
+ defer tmpFile .Close ()
109
+
110
+ fd := int (tmpFile .Fd ())
111
+
112
+ // Create a magiclink endpoint
113
+ endpoint , err := NewEndpoint (fd , 1500 , & testSink {})
114
+ if err != nil {
115
+ t .Fatalf ("Failed to create endpoint: %v" , err )
116
+ }
117
+ defer endpoint .Dispose ()
118
+
119
+ magicLink , ok := endpoint .(* magiclink )
120
+ if ! ok {
121
+ t .Fatalf ("Expected magiclink, got %T" , endpoint )
122
+ }
123
+
124
+ // Simulate the exact scenario from the stack trace:
125
+ // seamless.go:312>fdbased.go:413 - magiclink.Wait() calls endpoint.Wait()
126
+ panicked := false
127
+ done := make (chan struct {})
128
+
129
+ // Start a goroutine that continuously calls Wait() like the tunnel waiter
130
+ go func () {
131
+ defer func () {
132
+ if r := recover (); r != nil {
133
+ panicked = true
134
+ }
135
+ close (done )
136
+ }()
137
+
138
+ for i := 0 ; i < 100 ; i ++ {
139
+ magicLink .Wait ()
140
+ time .Sleep (time .Millisecond )
141
+ }
142
+ }()
143
+
144
+ // Concurrently perform rapid endpoint swaps
145
+ for i := 0 ; i < 10 ; i ++ {
146
+ tmpFile2 , err := os .CreateTemp ("" , "test_tun2" )
147
+ if err != nil {
148
+ continue
149
+ }
150
+ fd2 := int (tmpFile2 .Fd ())
151
+
152
+ // Rapid swap - this should not cause WaitGroup reuse panic
153
+ magicLink .Swap (fd2 , 1500 )
154
+
155
+ tmpFile2 .Close ()
156
+ os .Remove (tmpFile2 .Name ())
157
+ time .Sleep (time .Millisecond * 2 )
158
+ }
159
+
160
+ // Wait for the wait goroutine to complete
161
+ select {
162
+ case <- done :
163
+ if panicked {
164
+ t .Fatal ("WaitGroup reuse panic occurred in stack trace scenario" )
165
+ }
166
+ case <- time .After (time .Second * 15 ):
167
+ t .Fatal ("Test timed out" )
168
+ }
169
+ }
170
+
99
171
// testSink is a simple implementation of io.WriteCloser for testing
100
172
type testSink struct {}
101
173
0 commit comments