package main

import (
	"errors"
	"fmt"
	"net"
	"os"
	"time"
)

func deferred() (n int) {
	n = 1
	defer fmt.Println("saved argument:", n)
	defer func() { n++; fmt.Println("closure sees:", n) }()
	return 10
}
func rhs() int { fmt.Println("send expression evaluated"); return 1 }

//go:noinline
func direct(n int) int { return n + 1 }

//go:noinline
func oneDefer(n int) (r int) { defer func() { r++ }(); return n }

//go:noinline
func loopDefers(n int) (r int) {
	for range n {
		defer func() { r++ }()
	}
	return
}

func tcpDeadline() error {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		return err
	}
	defer ln.Close()
	accepted := make(chan error, 1)
	release := make(chan struct{})
	done := make(chan struct{})
	go func() {
		defer close(done)
		c, e := ln.Accept()
		accepted <- e
		if e != nil {
			return
		}
		defer c.Close()
		<-release
	}()
	defer func() { close(release); ln.Close(); <-done }()
	c, err := net.DialTimeout("tcp", ln.Addr().String(), time.Second)
	if err != nil {
		return err
	}
	defer c.Close()
	if err = <-accepted; err != nil {
		return err
	}
	if err = c.SetReadDeadline(time.Now().Add(20 * time.Millisecond)); err != nil {
		return err
	}
	var b [1]byte
	_, err = c.Read(b[:])
	fmt.Println("TCP deadline:", errors.Is(err, os.ErrDeadlineExceeded))
	if !errors.Is(err, os.ErrDeadlineExceeded) {
		return fmt.Errorf("unexpected read result: %v", err)
	}
	return nil
}
func main() {
	fmt.Println("return:", deferred())
	fmt.Println("defer results:", direct(4), oneDefer(4), loopDefers(3))
	var disabled chan int
	select {
	case disabled <- rhs():
	default:
		fmt.Println("default selected")
	}
	c := make(chan int)
	close(c)
	for i := range 2 {
		select {
		case _, ok := <-c:
			fmt.Println("closed ready:", i, ok)
			c = nil
		default:
			fmt.Println("nil disabled:", i)
		}
	}
	t := time.NewTimer(time.Hour)
	fmt.Println("timer len/cap:", len(t.C), cap(t.C))
	fmt.Println("stop:", t.Stop())
	fmt.Println("reset stopped:", t.Reset(0))
	<-t.C
	fmt.Println("timer fired")
	if err := tcpDeadline(); err != nil {
		panic(err)
	}
}
