1
0
mirror of https://github.com/taigrr/go-fastping synced 2025-01-18 05:03:15 -08:00

Stop to use panic

This commit is contained in:
Tatsushi Demachi 2014-04-21 10:15:18 +09:00
parent 85b47b7a02
commit f81b4cf5d3
3 changed files with 86 additions and 37 deletions

View File

@ -40,7 +40,7 @@ func main() {
}) })
p.MaxRTT = time.Second p.MaxRTT = time.Second
quit := p.RunLoop() quit, errch := p.RunLoop()
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt) signal.Notify(c, os.Interrupt)
@ -65,6 +65,9 @@ loop:
} }
results[host] = nil results[host] = nil
} }
case err := <-errch:
fmt.Println("Ping failed: %v", err)
break loop;
} }
} }
wait := make(chan bool) wait := make(chan bool)

View File

@ -151,11 +151,11 @@ func (p *Pinger) AddHandler(event string, handler interface{}) error {
// Invoke a single send/receive procedure. It sends packets to all hosts which // Invoke a single send/receive procedure. It sends packets to all hosts which
// have already been added by AddIP() etc. and wait those responses. When it // have already been added by AddIP() etc. and wait those responses. When it
// receives a response, it calls "receive" handler registered by AddHander(). // receives a response, it calls "receive" handler registered by AddHander().
// After MaxRTT seconds, it calls "idle" handler and returns to caller. It // After MaxRTT seconds, it calls "idle" handler and returns to caller with
// means it blocks until MaxRTT seconds passed. For the purpose of // an error value. It means it blocks until MaxRTT seconds passed. For the
// sending/receiving packets over and over, use RunLoop(). // purpose of sending/receiving packets over and over, use RunLoop().
func (p *Pinger) Run() { func (p *Pinger) Run() error {
p.run(true, make(chan chan<- bool)) return p.run(true, make(chan chan<- bool))
} }
// Invode send/receive procedure repeatedly. It sends packets to all hosts which // Invode send/receive procedure repeatedly. It sends packets to all hosts which
@ -164,33 +164,42 @@ func (p *Pinger) Run() {
// After MaxRTT seconds, it calls "idle" handler, resend packets and wait those // After MaxRTT seconds, it calls "idle" handler, resend packets and wait those
// response. MaxRTT works as an interval time. // response. MaxRTT works as an interval time.
// //
// This is a non-blocking method so immediately returns with a channel value. // This is a non-blocking method so immediately returns with channel values.
// If you want to stop sending packets, send a channel value of bool type to it // If you want to stop sending packets, send a channel value of bool type to it
// and wait for graceful shutdown. For example, // and wait for graceful shutdown. For example,
// //
// quit := p.RunLoop()
// wait := make(chan bool) // wait := make(chan bool)
// quit, errch := p.RunLoop()
// ticker := time.NewTicker(time.Millisecond * 250)
// select {
// case err := <-errch:
// log.Fatalf("Ping failed: %v", err)
// case <-ticker.C:
// quit <- wait // quit <- wait
// <-wait // case <-wait:
// break
// }
// //
// For more detail, please see "cmd/ping/ping.go". // For more detail, please see "cmd/ping/ping.go".
func (p *Pinger) RunLoop() chan<- chan<- bool { func (p *Pinger) RunLoop() (chan<- chan<- bool, <-chan error) {
quit := make(chan chan<- bool) quit := make(chan chan<- bool)
go p.run(false, quit) errch := make(chan error)
return quit go func(ch chan<- error) {
err := p.run(false, quit)
if err != nil {
ch <- err
}
}(errch)
return quit, errch
} }
func (p *Pinger) run(once bool, quit <-chan chan<- bool) { func (p *Pinger) run(once bool, quit <-chan chan<- bool) error {
p.debugln("Run(): Start") p.debugln("Run(): Start")
conn, err := net.ListenIP("ip4:icmp", &net.IPAddr{IP: net.IPv4zero}) conn, err := net.ListenIP("ip4:icmp", &net.IPAddr{IP: net.IPv4zero})
if err != nil { if err != nil {
panic(err) return err
} }
defer func() { defer conn.Close()
if err := conn.Close(); err != nil {
panic(err)
}
}()
var join chan<- bool var join chan<- bool
recv, stoprecv, waitjoin := make(chan *packet), make(chan chan<- bool), make(chan bool) recv, stoprecv, waitjoin := make(chan *packet), make(chan chan<- bool), make(chan bool)
@ -199,7 +208,7 @@ func (p *Pinger) run(once bool, quit <-chan chan<- bool) {
go p.recvICMP4(conn, recv, stoprecv) go p.recvICMP4(conn, recv, stoprecv)
p.debugln("Run(): call sendICMP4()") p.debugln("Run(): call sendICMP4()")
queue := p.sendICMP4(conn) queue, err := p.sendICMP4(conn)
ticker := time.NewTicker(p.MaxRTT) ticker := time.NewTicker(p.MaxRTT)
@ -217,13 +226,13 @@ mainloop:
hdl() hdl()
} }
} }
if once { if once || err != nil {
p.debugln("Run(): stoprecv <- waitjoin") p.debugln("Run(): stoprecv <- waitjoin")
stoprecv <- waitjoin stoprecv <- waitjoin
break mainloop break mainloop
} }
p.debugln("Run(): call sendICMP4()") p.debugln("Run(): call sendICMP4()")
queue = p.sendICMP4(conn) queue, err = p.sendICMP4(conn)
case r := <-recv: case r := <-recv:
p.debugln("Run(): <-recv") p.debugln("Run(): <-recv")
p.procRecv(r, queue) p.procRecv(r, queue)
@ -239,10 +248,10 @@ mainloop:
join <- true join <- true
} }
p.debugln("Run(): End") p.debugln("Run(): End")
return return err
} }
func (p *Pinger) sendICMP4(conn *net.IPConn) map[string]*net.IPAddr { func (p *Pinger) sendICMP4(conn *net.IPConn) (map[string]*net.IPAddr, error) {
p.debugln("sendICMP4(): Start") p.debugln("sendICMP4(): Start")
p.id = rand.Intn(0xffff) p.id = rand.Intn(0xffff)
p.seq = rand.Intn(0xffff) p.seq = rand.Intn(0xffff)
@ -258,7 +267,12 @@ func (p *Pinger) sendICMP4(conn *net.IPConn) map[string]*net.IPAddr {
}, },
}).Marshal() }).Marshal()
if err != nil { if err != nil {
panic(err) for i := 0; i < qlen; i++ {
p.debugln("sendICMP4(): wait goroutine")
<-sent
p.debugln("sendICMP4(): join goroutine")
}
return queue, err
} }
queue[k] = v queue[k] = v
@ -286,7 +300,7 @@ func (p *Pinger) sendICMP4(conn *net.IPConn) map[string]*net.IPAddr {
p.debugln("sendICMP4(): join goroutine") p.debugln("sendICMP4(): join goroutine")
} }
p.debugln("sendICMP4(): End") p.debugln("sendICMP4(): End")
return queue return queue, nil
} }
func (p *Pinger) recvICMP4(conn *net.IPConn, recv chan<- *packet, stoprecv <-chan chan<- bool) { func (p *Pinger) recvICMP4(conn *net.IPConn, recv chan<- *packet, stoprecv <-chan chan<- bool) {

View File

@ -61,7 +61,10 @@ func TestRun(t *testing.T) {
p.AddHandler("idle", func() { p.AddHandler("idle", func() {
idle = true idle = true
}) })
p.Run() err := p.Run()
if err != nil {
t.Fatalf("Pinger returns error: %v", err)
}
if !called { if !called {
t.Fatalf("Pinger didn't get any responses") t.Fatalf("Pinger didn't get any responses")
} }
@ -104,7 +107,9 @@ func TestMultiRun(t *testing.T) {
}) })
p1.MaxRTT, p2.MaxRTT = time.Millisecond*100, time.Millisecond*100 p1.MaxRTT, p2.MaxRTT = time.Millisecond*100, time.Millisecond*100
p1.Run() if err := p1.Run(); err != nil {
t.Fatalf("Pinger 1 returns error: %v", err)
}
if res1 == 0 { if res1 == 0 {
t.Fatalf("Pinger 1 didn't get any responses") t.Fatalf("Pinger 1 didn't get any responses")
} }
@ -113,7 +118,9 @@ func TestMultiRun(t *testing.T) {
} }
res1, res2 = 0, 0 res1, res2 = 0, 0
p2.Run() if err := p2.Run(); err != nil {
t.Fatalf("Pinger 2 returns error: %v", err)
}
if res1 > 0 { if res1 > 0 {
t.Fatalf("Pinger 1 got response") t.Fatalf("Pinger 1 got response")
} }
@ -122,9 +129,28 @@ func TestMultiRun(t *testing.T) {
} }
res1, res2 = 0, 0 res1, res2 = 0, 0
go p1.Run() errch1, errch2 := make(chan error), make(chan error)
go p2.Run() go func(ch chan error) {
time.Sleep(time.Millisecond * 200) err := p1.Run()
if err != nil {
ch <- err
}
}(errch1)
go func(ch chan error) {
err := p2.Run()
if err != nil {
ch <- err
}
}(errch2)
ticker := time.NewTicker(time.Millisecond * 200)
select {
case err := <-errch1:
t.Fatalf("Pinger 1 returns error: %v", err)
case err := <-errch2:
t.Fatalf("Pinger 2 returns error: %v", err)
case <-ticker.C:
break
}
mu.Lock() mu.Lock()
if res1 != 1 { if res1 != 1 {
t.Fatalf("Pinger 1 didn't get correct response") t.Fatalf("Pinger 1 didn't get correct response")
@ -151,11 +177,17 @@ func TestRunLoop(t *testing.T) {
idleCount++ idleCount++
}) })
quit := p.RunLoop()
time.Sleep(time.Millisecond * 250)
wait := make(chan bool) wait := make(chan bool)
quit, errch := p.RunLoop()
ticker := time.NewTicker(time.Millisecond * 250)
select {
case err := <-errch:
t.Fatalf("Pinger returns error %v", err)
case <-ticker.C:
quit <- wait quit <- wait
<-wait case <-wait:
break
}
if recvCount < 2 { if recvCount < 2 {
t.Fatalf("Pinger recieve count less than 2") t.Fatalf("Pinger recieve count less than 2")