BREAKING CHANGES: Run callbacks in main thread and int callback id.

Execute callbacks in main thread. This will break callbacks that
use a long time to execute. Create your own thread in AddCallback
using gorutines on long running callbacks.

Use deterministic IDs for AddCallback. Changes the id from SHA-hash
to int.
This commit is contained in:
Thomas Jager 2016-02-06 21:33:04 +01:00
parent ab737c68eb
commit da78ed515c
4 changed files with 64 additions and 52 deletions

View File

@ -46,6 +46,20 @@ AddCallback Example
//event.Arguments[0] Contains the channel //event.Arguments[0] Contains the channel
}); });
Please note: Callbacks are run in the main thread. If a callback needs a long
time to execute please run it in a new thread.
Example:
ircobj.AddCallback("PRIVMSG", func(event *irc.Event) {
go func(event *irc.Event) {
//event.Message() contains the message
//event.Nick Contains the sender
//event.Arguments[0] Contains the channel
}(e)
});
Commands Commands
-------- --------
ircobj := irc.IRC("<nick>", "<user>") //Create new ircobj ircobj := irc.IRC("<nick>", "<user>") //Create new ircobj

View File

@ -1,10 +1,6 @@
package irc package irc
import ( import (
"crypto/sha1"
"fmt"
"math/rand"
"reflect"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -14,23 +10,22 @@ import (
// which takes only an Event pointer as parameter. Valid event codes are all // which takes only an Event pointer as parameter. Valid event codes are all
// IRC/CTCP commands and error/response codes. This function returns the ID of // IRC/CTCP commands and error/response codes. This function returns the ID of
// the registered callback for later management. // the registered callback for later management.
func (irc *Connection) AddCallback(eventcode string, callback func(*Event)) string { func (irc *Connection) AddCallback(eventcode string, callback func(*Event)) int {
eventcode = strings.ToUpper(eventcode) eventcode = strings.ToUpper(eventcode)
id := 0
if _, ok := irc.events[eventcode]; !ok { if _, ok := irc.events[eventcode]; !ok {
irc.events[eventcode] = make(map[string]func(*Event)) irc.events[eventcode] = make(map[int]func(*Event))
id = 0
} else {
id = len(irc.events[eventcode])
} }
h := sha1.New()
rawId := []byte(fmt.Sprintf("%v%d", reflect.ValueOf(callback).Pointer(), rand.Int63()))
h.Write(rawId)
id := fmt.Sprintf("%x", h.Sum(nil))
irc.events[eventcode][id] = callback irc.events[eventcode][id] = callback
return id return id
} }
// Remove callback i (ID) from the given event code. This functions returns // Remove callback i (ID) from the given event code. This functions returns
// true upon success, false if any error occurs. // true upon success, false if any error occurs.
func (irc *Connection) RemoveCallback(eventcode string, i string) bool { func (irc *Connection) RemoveCallback(eventcode string, i int) bool {
eventcode = strings.ToUpper(eventcode) eventcode = strings.ToUpper(eventcode)
if event, ok := irc.events[eventcode]; ok { if event, ok := irc.events[eventcode]; ok {
@ -52,7 +47,7 @@ func (irc *Connection) ClearCallback(eventcode string) bool {
eventcode = strings.ToUpper(eventcode) eventcode = strings.ToUpper(eventcode)
if _, ok := irc.events[eventcode]; ok { if _, ok := irc.events[eventcode]; ok {
irc.events[eventcode] = make(map[string]func(*Event)) irc.events[eventcode] = make(map[int]func(*Event))
return true return true
} }
@ -61,7 +56,7 @@ func (irc *Connection) ClearCallback(eventcode string) bool {
} }
// Replace callback i (ID) associated with a given event code with a new callback function. // Replace callback i (ID) associated with a given event code with a new callback function.
func (irc *Connection) ReplaceCallback(eventcode string, i string, callback func(*Event)) { func (irc *Connection) ReplaceCallback(eventcode string, i int, callback func(*Event)) {
eventcode = strings.ToUpper(eventcode) eventcode = strings.ToUpper(eventcode)
if event, ok := irc.events[eventcode]; ok { if event, ok := irc.events[eventcode]; ok {
@ -120,7 +115,7 @@ func (irc *Connection) RunCallbacks(event *Event) {
} }
for _, callback := range callbacks { for _, callback := range callbacks {
go callback(event) callback(event)
} }
} else if irc.VerboseCallbackHandler { } else if irc.VerboseCallbackHandler {
irc.Log.Printf("%v (0) >> %#v\n", event.Code, event) irc.Log.Printf("%v (0) >> %#v\n", event.Code, event)
@ -128,21 +123,22 @@ func (irc *Connection) RunCallbacks(event *Event) {
if callbacks, ok := irc.events["*"]; ok { if callbacks, ok := irc.events["*"]; ok {
if irc.VerboseCallbackHandler { if irc.VerboseCallbackHandler {
irc.Log.Printf("Wildcard %v (%v) >> %#v\n", event.Code, len(callbacks), event) irc.Log.Printf("%v (0) >> %#v\n", event.Code, event)
} }
for _, callback := range callbacks { for _, callback := range callbacks {
go callback(event) callback(event)
} }
} }
} }
// Set up some initial callbacks to handle the IRC/CTCP protocol. // Set up some initial callbacks to handle the IRC/CTCP protocol.
func (irc *Connection) setupCallbacks() { func (irc *Connection) setupCallbacks() {
irc.events = make(map[string]map[string]func(*Event)) irc.events = make(map[string]map[int]func(*Event))
//Handle error events //Handle error events. This has to be called in a new thred to allow
irc.AddCallback("ERROR", func(e *Event) { irc.Disconnect() }) //readLoop to exit
irc.AddCallback("ERROR", func(e *Event) { go irc.Disconnect() })
//Handle ping events //Handle ping events
irc.AddCallback("PING", func(e *Event) { irc.SendRaw("PONG :" + e.Message()) }) irc.AddCallback("PING", func(e *Event) { irc.SendRaw("PONG :" + e.Message()) })
@ -223,7 +219,3 @@ func (irc *Connection) setupCallbacks() {
irc.nickcurrent = e.Arguments[0] irc.nickcurrent = e.Arguments[0]
}) })
} }
func init() {
rand.Seed(time.Now().UnixNano())
}

View File

@ -33,7 +33,7 @@ type Connection struct {
nickcurrent string //The nickname we currently have. nickcurrent string //The nickname we currently have.
user string user string
registered bool registered bool
events map[string]map[string]func(*Event) events map[string]map[int]func(*Event)
QuitMessage string QuitMessage string
lastMessage time.Time lastMessage time.Time

View File

@ -184,33 +184,26 @@ func TestConnection(t *testing.T) {
irccon2 := IRC("go-eventirc2", "go-eventirc2") irccon2 := IRC("go-eventirc2", "go-eventirc2")
irccon2.VerboseCallbackHandler = true irccon2.VerboseCallbackHandler = true
irccon2.Debug = true irccon2.Debug = true
err := irccon1.Connect("irc.freenode.net:6667")
if err != nil {
t.Log(err.Error())
t.Fatal("Can't connect to freenode.")
}
err = irccon2.Connect("irc.freenode.net:6667")
if err != nil {
t.Log(err.Error())
t.Fatal("Can't connect to freenode.")
}
irccon1.AddCallback("001", func(e *Event) { irccon1.Join("#go-eventirc") }) irccon1.AddCallback("001", func(e *Event) { irccon1.Join("#go-eventirc") })
irccon2.AddCallback("001", func(e *Event) { irccon2.Join("#go-eventirc") }) irccon2.AddCallback("001", func(e *Event) { irccon2.Join("#go-eventirc") })
con2ok := false con2ok := false
irccon1.AddCallback("366", func(e *Event) { irccon1.AddCallback("366", func(e *Event) {
t := time.NewTicker(1 * time.Second) go func(e *Event) {
i := 10 t := time.NewTicker(1 * time.Second)
for { i := 10
<-t.C for {
irccon1.Privmsgf("#go-eventirc", "Test Message%d\n", i) <-t.C
if con2ok { irccon1.Privmsgf("#go-eventirc", "Test Message%d\n", i)
i -= 1 if con2ok {
i -= 1
}
if i == 0 {
t.Stop()
irccon1.Quit()
}
} }
if i == 0 { }(e)
t.Stop()
irccon1.Quit()
}
}
}) })
irccon2.AddCallback("366", func(e *Event) { irccon2.AddCallback("366", func(e *Event) {
@ -231,6 +224,18 @@ func TestConnection(t *testing.T) {
t.Fatal("Nick change did not work!") t.Fatal("Nick change did not work!")
} }
}) })
err := irccon1.Connect("irc.freenode.net:6667")
if err != nil {
t.Log(err.Error())
t.Fatal("Can't connect to freenode.")
}
err = irccon2.Connect("irc.freenode.net:6667")
if err != nil {
t.Log(err.Error())
t.Fatal("Can't connect to freenode.")
}
go irccon2.Loop() go irccon2.Loop()
irccon1.Loop() irccon1.Loop()
} }
@ -241,11 +246,6 @@ func TestConnectionSSL(t *testing.T) {
irccon.Debug = true irccon.Debug = true
irccon.UseTLS = true irccon.UseTLS = true
irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true} irccon.TLSConfig = &tls.Config{InsecureSkipVerify: true}
err := irccon.Connect("irc.freenode.net:7000")
if err != nil {
t.Log(err.Error())
t.Fatal("Can't connect to freenode.")
}
irccon.AddCallback("001", func(e *Event) { irccon.Join("#go-eventirc") }) irccon.AddCallback("001", func(e *Event) { irccon.Join("#go-eventirc") })
irccon.AddCallback("366", func(e *Event) { irccon.AddCallback("366", func(e *Event) {
@ -254,5 +254,11 @@ func TestConnectionSSL(t *testing.T) {
irccon.Quit() irccon.Quit()
}) })
err := irccon.Connect("irc.freenode.net:7000")
if err != nil {
t.Log(err.Error())
t.Fatal("Can't connect to freenode.")
}
irccon.Loop() irccon.Loop()
} }