// $GOROOT/src/sync/mutex.go
// A Mutex must not be copied after first use. (禁止复制首次使用后的Mutex)

// $GOROOT/src/sync/rwmutex.go
// A RWMutex must not be copied after first use.(禁止复制首次使用后的RWMutex)

// $GOROOT/src/sync/cond.go
// A Cond must not be copied after first use.(禁止复制首次使用后的Cond)
...

type signal struct{}
var ready bool

func worker(i int) {
    fmt.Printf("worker %d: is working...\\n", i)
    time.Sleep(1 * time.Second)
    fmt.Printf("worker %d: works done\\n", i)
}

func spawnGroup(f func(i int), num int, mu *sync.Mutex) <-chan signal {
    c := make(chan signal)
    var wg sync.WaitGroup
    
    for i := 0; i < num; i++ {
        wg.Add(1)
        go func(i int) {
            for {
                mu.Lock()
                if !ready {
                    mu.Unlock()
                    time.Sleep(100 * time.Millisecond)
                    continue
                }
                mu.Unlock()
                fmt.Printf("worker %d: start to work...\\n", i)
                f(i)
                wg.Done()
                return
            }
        }(i + 1)
    }
    
    go func() {
        wg.Wait()
        c <- signal(struct{}{})
    }()
    return c
}

func main() {
    fmt.Println("start a group of workers...")
    mu := &sync.Mutex{}
    c := spawnGroup(worker, 5, mu)
    
    time.Sleep(5 * time.Second) // 模拟ready前的准备工作
    fmt.Println("the group of workers start to work...")
    
    mu.Lock()
    ready = true
    mu.Unlock()
    
    <-c
    fmt.Println("the group of workers work done!")
}
type signal struct{}

var ready bool

func worker(i int) {
	fmt.Printf("worker %d: is working...\\n", i)
	time.Sleep(1 * time.Second)
	fmt.Printf("worker %d: works done\\n", i)
}

func spawnGroup(f func(i int), num int, groupSignal *sync.Cond) <-chan signal {
	c := make(chan signal)
	var wg sync.WaitGroup

	for i := 0; i < num; i++ {
		wg.Add(1)
		go func(i int) {
			groupSignal.L.Lock()
			for !ready {
				groupSignal.Wait()
			}
			groupSignal.L.Unlock()
			fmt.Printf("worker %d: start to work...\\n", i)
			f(i)
			wg.Done()
		}(i + 1)
	}

	go func() {
		wg.Wait()
		c <- signal(struct{}{})
	}()
	return c
}

func main() {
	fmt.Println("start a group of workers...")
	groupSignal := sync.NewCond(&sync.Mutex{})
	c := spawnGroup(worker, 5, groupSignal)

	time.Sleep(5 * time.Second) // 模拟ready前的准备工作
	fmt.Println("the group of workers start to work...")

	groupSignal.L.Lock()
	ready = true
	groupSignal.Broadcast()
	groupSignal.L.Unlock()

	<-c
	fmt.Println("the group of workers work done!")
}
type Foo struct {
}

var once sync.Once
var instance *Foo

func GetInstance(id int) *Foo {
	defer func() {
		if e := recover(); e != nil {
			log.Printf("goroutine-%d: caught a panic: %s", id, e)
		}
	}()
	log.Printf("goroutine-%d: enter GetInstance\\n", id)
	once.Do(func() {
		instance = &Foo{}
		time.Sleep(3 * time.Second)
		log.Printf("goroutine-%d: the addr of instance is %p\\n", id, instance)
		panic("panic in once.Do function")
	})
	return instance
}

func main() {
	var wg sync.WaitGroup
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(i int) {
			inst := GetInstance(i)
			log.Printf("goroutine-%d: the addr of instance returned is %p\\n", i, inst)
			wg.Done()
		}(i + 1)
	}
	time.Sleep(5 * time.Second)
	inst := GetInstance(0)
	log.Printf("goroutine-0: the addr of instance returned is %p\\n", inst)

	wg.Wait()
	log.Printf("all goroutines exit\\n")
}