@@ -96,6 +96,78 @@ func TestWaitGroupRaceCondition(t *testing.T) {
9696 }
9797}
9898
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+
99171// testSink is a simple implementation of io.WriteCloser for testing
100172type testSink struct {}
101173
0 commit comments