1// Copyright 2023 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
5package main
6
7import (
8	"fmt"
9	"io"
10	"log"
11	"net"
12	"internal/runtime/atomic"
13	"sync"
14	"time"
15	_ "unsafe" // for go:linkname
16)
17
18// The bug is that netpollWaiters increases monotonically.
19// This doesn't cause a problem until it overflows.
20// Use linkname to see the value.
21//
22//go:linkname netpollWaiters runtime.netpollWaiters
23var netpollWaiters atomic.Uint32
24
25func init() {
26	register("NetpollWaiters", NetpollWaiters)
27}
28
29func NetpollWaiters() {
30	listener, err := net.Listen("tcp", "127.0.0.1:0")
31	if err != nil {
32		log.Fatal(err)
33	}
34
35	var wg sync.WaitGroup
36	wg.Add(1)
37	go func() {
38		defer wg.Done()
39		conn, err := listener.Accept()
40		if err != nil {
41			log.Fatal(err)
42		}
43		defer conn.Close()
44		if _, err := io.Copy(io.Discard, conn); err != nil {
45			log.Fatal(err)
46		}
47	}()
48
49	wg.Add(1)
50	go func() {
51		defer wg.Done()
52		conn, err := net.Dial("tcp", listener.Addr().String())
53		if err != nil {
54			log.Fatal(err)
55		}
56		defer conn.Close()
57		for i := 0; i < 10; i++ {
58			fmt.Fprintf(conn, "%d\n", i)
59			time.Sleep(time.Millisecond)
60		}
61	}()
62
63	wg.Wait()
64	if v := netpollWaiters.Load(); v != 0 {
65		log.Fatalf("current waiters %v", v)
66	}
67
68	fmt.Println("OK")
69}
70