Split handlers up into three groups.

This commit is contained in:
Alex Bramley 2013-09-28 00:15:54 +01:00
parent 12eb9c89eb
commit 144001d109
5 changed files with 44 additions and 15 deletions

View File

@ -22,7 +22,9 @@ type Conn struct {
cfg *Config cfg *Config
// Handlers // Handlers
handlers *hSet intHandlers *hSet
fgHandlers *hSet
bgHandlers *hSet
// State tracker for nicks and channels // State tracker for nicks and channels
st state.Tracker st state.Tracker
@ -116,12 +118,14 @@ func Client(cfg *Config) *Conn {
cfg.Me.Name = "Powered by GoIRC" cfg.Me.Name = "Powered by GoIRC"
} }
conn := &Conn{ conn := &Conn{
cfg: cfg, cfg: cfg,
in: make(chan *Line, 32), in: make(chan *Line, 32),
out: make(chan string, 32), out: make(chan string, 32),
handlers: handlerSet(), intHandlers: handlerSet(),
stRemovers: make([]Remover, 0, len(stHandlers)), fgHandlers: handlerSet(),
lastsent: time.Now(), bgHandlers: handlerSet(),
stRemovers: make([]Remover, 0, len(stHandlers)),
lastsent: time.Now(),
} }
conn.addIntHandlers() conn.addIntHandlers()
conn.initialise() conn.initialise()

View File

@ -87,7 +87,7 @@ func TestClientAndStateTracking(t *testing.T) {
} }
// Check that the internal handlers are correctly set up // Check that the internal handlers are correctly set up
for k, _ := range intHandlers { for k, _ := range intHandlers {
if _, ok := c.handlers.set[strings.ToLower(k)]; !ok { if _, ok := c.intHandlers.set[strings.ToLower(k)]; !ok {
t.Errorf("Missing internal handler for '%s'.", k) t.Errorf("Missing internal handler for '%s'.", k)
} }
} }
@ -95,7 +95,7 @@ func TestClientAndStateTracking(t *testing.T) {
// Now enable the state tracking code and check its handlers // Now enable the state tracking code and check its handlers
c.EnableStateTracking() c.EnableStateTracking()
for k, _ := range stHandlers { for k, _ := range stHandlers {
if _, ok := c.handlers.set[strings.ToLower(k)]; !ok { if _, ok := c.intHandlers.set[strings.ToLower(k)]; !ok {
t.Errorf("Missing state handler for '%s'.", k) t.Errorf("Missing state handler for '%s'.", k)
} }
} }
@ -124,7 +124,7 @@ func TestClientAndStateTracking(t *testing.T) {
// Finally, check state tracking handlers were all removed correctly // Finally, check state tracking handlers were all removed correctly
for k, _ := range stHandlers { for k, _ := range stHandlers {
if _, ok := c.handlers.set[strings.ToLower(k)]; ok && k != "NICK" { if _, ok := c.intHandlers.set[strings.ToLower(k)]; ok && k != "NICK" {
// A bit leaky, because intHandlers adds a NICK handler. // A bit leaky, because intHandlers adds a NICK handler.
t.Errorf("State handler for '%s' not removed correctly.", k) t.Errorf("State handler for '%s' not removed correctly.", k)
} }

View File

@ -121,9 +121,15 @@ func (hs *hSet) dispatch(conn *Conn, line *Line) {
if !ok { if !ok {
return return
} }
wg := &sync.WaitGroup{}
for hn := list.start; hn != nil; hn = hn.next { for hn := list.start; hn != nil; hn = hn.next {
go hn.Handle(conn, line.Copy()) wg.Add(1)
go func(hn *hNode) {
hn.Handle(conn, line.Copy())
wg.Done()
}(hn)
} }
wg.Wait()
} }
// Handlers are triggered on incoming Lines from the server, with the handler // Handlers are triggered on incoming Lines from the server, with the handler
@ -133,7 +139,15 @@ func (hs *hSet) dispatch(conn *Conn, line *Line) {
// strings of digits like "332" (mainly because I really didn't feel like // strings of digits like "332" (mainly because I really didn't feel like
// putting massive constant tables in). // putting massive constant tables in).
func (conn *Conn) Handle(name string, h Handler) Remover { func (conn *Conn) Handle(name string, h Handler) Remover {
return conn.handlers.add(name, h) return conn.fgHandlers.add(name, h)
}
func (conn *Conn) HandleBG(name string, h Handler) Remover {
return conn.bgHandlers.add(name, h)
}
func (conn *Conn) handle(name string, h Handler) Remover {
return conn.intHandlers.add(name, h)
} }
func (conn *Conn) HandleFunc(name string, hf HandlerFunc) Remover { func (conn *Conn) HandleFunc(name string, hf HandlerFunc) Remover {
@ -141,7 +155,18 @@ func (conn *Conn) HandleFunc(name string, hf HandlerFunc) Remover {
} }
func (conn *Conn) dispatch(line *Line) { func (conn *Conn) dispatch(line *Line) {
conn.handlers.dispatch(conn, line) // We run the internal handlers first, including all state tracking ones.
// This ensures that user-supplied handlers that use the tracker have a
// consistent view of the connection state in handlers that mutate it.
conn.intHandlers.dispatch(conn, line)
// Background handlers are run in parallel and do not block the event loop.
// This is useful for things that may need to do significant work.
go conn.bgHandlers.dispatch(conn, line)
// Foreground handlers have a guarantee of protocol consistency: all the
// handlers for one event will have finished before the handlers for the
// next start processing. They are run in parallel but block the event
// loop, so care should be taken to ensure these handlers are quick :-)
conn.fgHandlers.dispatch(conn, line)
} }
func (conn *Conn) LogPanic(line *Line) { func (conn *Conn) LogPanic(line *Line) {

View File

@ -21,7 +21,7 @@ func (conn *Conn) addIntHandlers() {
for n, h := range intHandlers { for n, h := range intHandlers {
// internal handlers are essential for the IRC client // internal handlers are essential for the IRC client
// to function, so we don't save their Removers here // to function, so we don't save their Removers here
conn.Handle(n, h) conn.handle(n, h)
} }
} }

View File

@ -26,7 +26,7 @@ var stHandlers = map[string]HandlerFunc{
func (conn *Conn) addSTHandlers() { func (conn *Conn) addSTHandlers() {
for n, h := range stHandlers { for n, h := range stHandlers {
conn.stRemovers = append(conn.stRemovers, conn.Handle(n, h)) conn.stRemovers = append(conn.stRemovers, conn.handle(n, h))
} }
} }