package main

import (
	"flag"
	"fmt"
	"log"
	"os"
	"runtime/pprof"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

var serverDone = make(chan struct{})
var serverDone1 = make(chan struct{})
var serverDone2 = make(chan struct{})
var serverDone3 = make(chan struct{})
var serverDone4 = make(chan struct{})
var serverDone5 = make(chan struct{})

var ticker *WaitCond
var waiter *WaitCond

var prof = flag.Bool("prof", false, "profile the app?")

func main() {
	ticker = NewWaitCond()
	waiter = NewWaitCond()

	if *prof {
		f, err := os.Create("cpu.pprof")
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	timer := time.NewTicker(100 * time.Millisecond)

	go func() {
		for range timer.C {
			ticker.Signal()
			ticker.Reset()
			waiter.Signal()
			waiter.Reset()
		}
	}()
	defer timer.Stop()

	run(1, messageLoop1)
	run(2, messageLoop2)
	run(3, messageLoop3)
	run(4, messageLoop4)
	run(5, messageLoop5)
	run(6, messageLoop6)
	run(5, messageLoop5)
	run(4, messageLoop4)
	run(3, messageLoop3)
	run(2, messageLoop2)
	run(1, messageLoop1)
}

func run(name int, f func()) {
	var start syscall.Timespec
	r, _, errno := syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 2, uintptr(unsafe.Pointer(&start)), 0)
	if r != 0 {
		log.Fatal(errno)
	}

	var wg sync.WaitGroup
	wg.Add(1000)
	for i := 0; i < 1000; i++ {
		go func() {
			f()
			wg.Done()
		}()
	}
	<-time.After(10 * time.Second)
	close(serverDone)

	wg.Wait()

	var end syscall.Timespec
	r, _, errno = syscall.Syscall(syscall.SYS_CLOCK_GETTIME, 2, uintptr(unsafe.Pointer(&end)), 0)
	if r != 0 {
		log.Fatal(errno)
	}
	d := time.Duration(end.Sec-start.Sec)*time.Second + time.Duration(end.Nsec-start.Nsec)*time.Nanosecond
	fmt.Println(name, d)

	serverDone = make(chan struct{})
}

func messageLoop1() {
	//var ticker = time.NewTicker(100 * time.Millisecond)
	//defer ticker.Stop()
	var counter = 0
	for {
		select {
		case <-serverDone:
			return
		case <-ticker.Channel():
			counter += 1
		}
		<-waiter.Channel()
	}
}

func messageLoop2() {
	//var ticker = time.NewTicker(100 * time.Millisecond)
	//defer ticker.Stop()
	var counter = 0
	for {
		select {
		case <-serverDone:
			return
		case <-serverDone1:
			return
		case <-ticker.Channel():
			counter += 1
		}
		<-waiter.Channel()
	}
}

func messageLoop3() {
	//var ticker = time.NewTicker(100 * time.Millisecond)
	//defer ticker.Stop()
	var counter = 0
	for {
		select {
		case <-serverDone:
			return
		case <-serverDone1:
			return
		case <-serverDone2:
			return
		case <-ticker.Channel():
			counter += 1
		}
		<-waiter.Channel()
	}
}

func messageLoop4() {
	//var ticker = time.NewTicker(100 * time.Millisecond)
	//defer ticker.Stop()
	var counter = 0
	for {
		select {
		case <-serverDone:
			return
		case <-serverDone1:
			return
		case <-serverDone2:
			return
		case <-serverDone3:
			return
		case <-ticker.Channel():
			counter += 1
		}
		<-waiter.Channel()
	}
}

func messageLoop5() {
	//var ticker = time.NewTicker(100 * time.Millisecond)
	//defer ticker.Stop()
	var counter = 0
	for {
		select {
		case <-serverDone:
			return
		case <-serverDone1:
			return
		case <-serverDone2:
			return
		case <-serverDone3:
			return
		case <-serverDone4:
			return
		case <-ticker.Channel():
			counter += 1
		}
		<-waiter.Channel()
	}
}

func messageLoop6() {
	//var ticker = time.NewTicker(100 * time.Millisecond)
	//defer ticker.Stop()
	var counter = 0
	for {
		select {
		case <-serverDone:
			return
		case <-serverDone1:
			return
		case <-serverDone2:
			return
		case <-serverDone3:
			return
		case <-serverDone4:
			return
		case <-serverDone5:
			return
		case <-ticker.Channel():
			counter += 1
		}
		<-waiter.Channel()
	}
}
