Global Variable Gives SIGSEGV - go

I am using Fiber to develop a backend.
I have a map that is a global variable that holds the socket connections.
When I use the global variable from the same package, no problem here, everything works fine. But, when I try to use the sockets from a route function, I am getting the error below.
I tried to use mutex.lock but no luck.
I checked the code, the socket is not nil in my sendToAll method but it becomes nil in the helper method( inside the lib: github.com/fasthttp/websocket.(*Conn).WriteMessage )
Any advice is welcome.
Thanks.
type ConnectedSocketsContainerType struct {
M sync.Mutex
ConnectedSockets map[string]*websocket.Conn
}
var ConnectedSocketsContainer = ConnectedSocketsContainerType{ M:sync.Mutex{} , ConnectedSockets: make(map[string]*websocket.Conn) }
In another package in GET request handler calls that method:
func send(socketID string,message string) {
sockethub.ConnectedSocketsContainer.M.Lock()
sendToAll(message)
sockethub.ConnectedSocketsContainer.M.Unlock()
}
func sendToAll(message string) {
for k := range sockethub.SocketsIDs {
k.WriteMessage(1, []byte(message))
}
}
The error:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x10 pc=0x14d27f8]
goroutine 6 [running]:
github.com/fasthttp/websocket.(*Conn).WriteMessage(0xc00006aa78, 0xc00045e115, {0xc0004540f0, 0x29, 0xc00006ab2f})
/Users/emre/go/pkg/mod/github.com/fasthttp/websocket#v1.4.3-rc.10/conn.go:753 +0x38
goserver/controllers/api.sendToAll({0xc00045e115, 0x29})
/Users/emre/Documents/Free/SocketServer/goServer/controllers/api/socket.go:11 +0xac
goserver/controllers/api.send({0xc000456000, 0x15edfe1}, {0xc00045e115, 0x0})
/Users/emre/Documents/Free/SocketServer/goServer/controllers/api/socket.go:22 +0x65
goserver/controllers/api.SendMessageController(0xc000128a50)
/Users/emre/Documents/Free/SocketServer/goServer/controllers/api/socket.go:29 +0x71
github.com/gofiber/fiber/v2.(*App).next(0xc00019cb60, 0xc000456000)
/Users/emre/go/pkg/mod/github.com/gofiber/fiber/v2#v2.23.0/router.go:127 +0x1d8
github.com/gofiber/fiber/v2.(*App).handler(0xc00019cb60, 0x10bb517)
/Users/emre/go/pkg/mod/github.com/gofiber/fiber/v2#v2.23.0/router.go:155 +0xe5
github.com/valyala/fasthttp.(*Server).serveConn(0xc000126000, {0x16c4fa0, 0xc0000106e8})
/Users/emre/go/pkg/mod/github.com/valyala/fasthttp#v1.31.0/server.go:2278 +0x122d
github.com/valyala/fasthttp.(*workerPool).workerFunc(0xc00014c000, 0xc00022dba0)
/Users/emre/go/pkg/mod/github.com/valyala/fasthttp#v1.31.0/workerpool.go:223 +0xa9
github.com/valyala/fasthttp.(*workerPool).getCh.func1()
/Users/emre/go/pkg/mod/github.com/valyala/fasthttp#v1.31.0/workerpool.go:195 +0x38
created by github.com/valyala/fasthttp.(*workerPool).getCh
/Users/emre/go/pkg/mod/github.com/valyala/fasthttp#v1.31.0/workerpool.go:194 +0x1b5
exit status 2
Full example for go server. Please see two comments that specify working and not working code blocks.
package main
import (
"fmt"
"sync"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
)
func main() {
app := fiber.New()
ListenSocket(app)
app.Get("/socket/send", SendMessageController )
app.Listen(":3000")
}
const websocketHeaderKey = "Sec-Websocket-Key"
var ConnectedIDSockets sync.Map
func SendMessageController( c *fiber.Ctx ) error {
ConnectedIDSockets.Range(func(key, value interface{}) bool {
c := value.(*websocket.Conn)
if c == nil {
// that line is not printed, c is not nil.
fmt.Println("c is nil.")
return true
}
// we have crash at that line, even we read the err.
err := c.WriteMessage(websocket.TextMessage, []byte("message"))
// program does not runs to here since it crashed.
println("err:", err)
return true
})
return nil
}
func ListenSocket(app *fiber.App) {
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) {
c.Locals("allowed", true)
c.Locals(websocketHeaderKey, string(c.Request().Header.Peek(websocketHeaderKey)))
return c.Next()
}
return fiber.ErrUpgradeRequired
})
app.Get("/ws/:projectKEY", websocket.New(func(c *websocket.Conn) {
socketID := c.Locals(websocketHeaderKey).(string)
ConnectedIDSockets.Store(socketID, c)
// that works.
conn, _ := ConnectedIDSockets.Load(socketID)
socketmap := conn.(*websocket.Conn)
socketmap.WriteMessage(1, []byte(socketID))
}))
}

This panic is confusing because there are actually two packages called websocket. One in github.com/gofiber/websocket/v2 and another one in github.com/fasthttp/websocket, and both have their own *websocket.Conn. However the websocket.Conn in github.com/gofiber/websocket actually embeds the websocket.Conn from github.com/fasthttp/websocket (I know, terrible design) making what's going on unclear.
Your call to c.WriteMessage is actually going to c.Conn.WriteMessage, and c.Conn is what's nil. So in your nil check, you actually need to do if c == nil || c.Conn == nil { to check the embedded struct as well.

Related

Print message if Semaphore blocks for too long, but don't unblock caller when message is printed

I have the following code in Go using the semaphore library just as an example:
package main
import (
"fmt"
"context"
"time"
"golang.org/x/sync/semaphore"
)
// This protects the lockedVar variable
var lock *semaphore.Weighted
// Only one go routine should be able to access this at once
var lockedVar string
func acquireLock() {
err := lock.Acquire(context.TODO(), 1)
if err != nil {
panic(err)
}
}
func releaseLock() {
lock.Release(1)
}
func useLockedVar() {
acquireLock()
fmt.Printf("lockedVar used: %s\n", lockedVar)
releaseLock()
}
func causeDeadLock() {
acquireLock()
// calling this from a function that's already
// locked the lockedVar should cause a deadlock.
useLockedVar()
releaseLock()
}
func main() {
lock = semaphore.NewWeighted(1)
lockedVar = "this is the locked var"
// this is only on a separate goroutine so that the standard
// go "deadlock" message doesn't print out.
go causeDeadLock()
// Keep the primary goroutine active.
for true {
time.Sleep(time.Second)
}
}
Is there a way to get the acquireLock() function call to print a message after a timeout indicating that there is a potential deadlock but without unblocking the call? I would want the deadlock to persist, but a log message to be written in the event that a timeout is reached. So a TryAcquire isn't exactly what I want.
An example of what I want in psuedo code:
afterFiveSeconds := func() {
fmt.Printf("there is a potential deadlock\n")
}
lock.Acquire(context.TODO(), 1, afterFiveSeconds)
The lock.Acquire call in this example would call the afterFiveSeconds callback if the Acquire call blocked for more than 5 seconds, but it would not unblock the caller. It would continue to block.
I think I've found a solution to my problem.
func acquireLock() {
timeoutChan := make(chan bool)
go func() {
select {
case <-time.After(time.Second * time.Duration(5)):
fmt.Printf("potential deadlock while acquiring semaphore\n")
case <-timeoutChan:
break
}
}()
err := lock.Acquire(context.TODO(), 1)
close(timeoutChan)
if err != nil {
panic(err)
}
}

Posting Slackbot response in Golang

I am following this tutorial trying to get the go slack lib working. I am able to receive messages from slack, stored in ev variable but can not reply
func replyToUser(ev *slack.MessageEvent) { //change this to the channel for the actual app
fmt.Printf(ev.User)
slackClient.PostMessage(ev.User,
slack.MsgOptionText("hello world", false),
slack.MsgOptionUser(ev.User),
slack.MsgOptionAsUser(true),
slack.MsgOptionUsername("songbot"),
)
}
I am passing the event i receive from slack to this function and hoping to reply with a hello world. I am using a classic slackbot.
Currently getting the error:
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x127abb7]
Any help much appreciated !
really sorry I incorrectly defined the slack client. Full answer here: https://github.com/slack-go/slack/issues/796
Running code is:
package main
import (
"fmt"
"log"
"os"
"github.com/slack-go/slack"
)
var (
slackClient *slack.Client //initialise the slack event
)
func main() {
slackClient = slack.New(os.Getenv("SLACK_ACCESS_TOKEN"))
rtm := slackClient.NewRTM() //create the realtime messaging objext
go rtm.ManageConnection() // set it up in a go routine
for msg := range rtm.IncomingEvents {
switch ev := msg.Data.(type) {
case *slack.MessageEvent:
go handleMessage(ev)
}
}
}
func handleMessage(ev *slack.MessageEvent) {
fmt.Printf("%v\n", ev)
fmt.Printf("Text Received: %v\n", ev.Msg.Text)
_, _, err := slackClient.PostMessage(ev.Channel, slack.MsgOptionText("message", false), slack.MsgOptionAsUser(true))
if err != nil {
log.Println("error sending to slack: " + err.Error())
}
return
}

When to use sync.Mutex with net/http and gorilla/mux

As far as I know, the net/http package uses goroutines for the handlers. Is it necessary that I lock even a map with sync.Mutex in order to prevent possible bugs in the nextId function cause the function could count an old state of the map?
Here is my example code:
package main
import (
"net/http"
"github.com/gorilla/mux"
"io/ioutil"
"fmt"
)
var testData = map[int]string {
1: "foo",
2: "bar",
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/data", getData).Methods("GET")
r.HandleFunc("/data", addData).Methods("POST")
http.ListenAndServe(":3000", r)
}
func getData(writer http.ResponseWriter, request *http.Request) {
for k, v := range testData {
fmt.Fprintf(writer, "Key: %d\tValue: %v\n", k, v)
}
}
func addData(writer http.ResponseWriter, request *http.Request) {
if data, err := ioutil.ReadAll(request.Body); err == nil {
if len(data) == 0 {
writer.WriteHeader(http.StatusBadRequest)
return
}
id := nextId()
testData[id] = string(data)
url := request.URL.String()
writer.Header().Set("Location", fmt.Sprintf("%s", url))
writer.WriteHeader(http.StatusCreated)
} else {
writer.WriteHeader(http.StatusBadRequest)
}
}
func nextId() int {
id := 1
for k, _ := range testData {
if k >= id {
id = k + 1;
}
}
return id
}
Since the HTTP server of the standard lib calls handlers on their own goroutine, you must synchronzie access to all variables that are defined outside of the handlers (and where one of the access is a write). You have to do this whenever you use the stdlib's HTTP server. It doesn't matter if you use the standard lib's multiplexer or Gorilla's. The goroutine launch happens outside of the multiplexer (before the multiplexer is called).
Failing to do so (like in your example), data race occurs which you can verify by running it with the -race option:
WARNING: DATA RACE
Write at 0x00c000090c30 by goroutine 21:
runtime.mapassign_fast64()
/usr/local/go/src/runtime/map_fast64.go:92 +0x0
main.addData()
/home/icza/gows/src/play/play.go:47 +0x191
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:2007 +0x51
github.com/gorilla/mux.(*Router).ServeHTTP()
/home/icza/gows/pkg/mod/github.com/gorilla/mux#v1.7.3/mux.go:212 +0x13e
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2802 +0xce
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1890 +0x837
Previous read at 0x00c000090c30 by goroutine 7:
runtime.mapiternext()
/usr/local/go/src/runtime/map.go:851 +0x0
main.getData()
/home/icza/gows/src/play/play.go:32 +0x194
net/http.HandlerFunc.ServeHTTP()
/usr/local/go/src/net/http/server.go:2007 +0x51
...

How can I instantiate a nil error using Golang's reflect?

I am writing a function using reflect.MakeFunc. That function can return an error. When it succeeds, I want it to return nil for its error-typed return value. How can I do that using reflect? Currently I have this:
package main
import (
"fmt"
"reflect"
"errors"
)
func main() {
fmt.Println("Hello, playground")
f := func() error {return nil}
fn := reflect.MakeFunc(reflect.TypeOf(f), func(args []reflect.Value) []reflect.Value {
return []reflect.Value{reflect.New(reflect.TypeOf(errors.New("")))}
}).Interface().(func() error)
fmt.Printf("err: %v", fn())
}
I get panic: reflect: function created by MakeFunc using closure returned wrong type: have **errors.errorString for error. I also tried adding a .Elem() after reflect.New(reflect.TypeOf(errors.New(""))), but I got panic: reflect: function created by MakeFunc using closure returned wrong type: have *errors.errorString for error. I tried .Elem().Elem(), and I got a segmentation fault.
How can I get a reflect.Value representing a nil error?
Use the following:
var nilError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())
func main() {
fmt.Println("Hello, playground")
f := func() error { return nil }
fn := reflect.MakeFunc(reflect.TypeOf(f), func(args []reflect.Value) []reflect.Value {
return []reflect.Value{nilError}
}).Interface().(func() error)
fmt.Printf("err: %v", fn())
}
Let's break this down. The first step is to get a reflect.Type for error: reflect.TypeOf((*error)(nil)).Elem(). The simpler reflect.TypeOf((error)(nil)) does not work because the concrete value of the argument is nil. There's no type for nil and it's not the type we want anyway. The workaround is to pass a pointer to error and then call Elem() on the type to get the relfect.Type for error.
The second step is to create a zero value for the type.
I found one way - reflect.ValueOf(f).Call(nil). Maybe there's a better one.

Combine multiple error strings

I am new to golang, my application needs to return multiple errors in a loop, later requires to be combined and returned as a single error string. I am not able to use the string functions to combine the error messages. What methods can be use to combine these errors into a single error before returning ?
package main
import (
"fmt"
"strings"
)
func Servreturn() (err error) {
err1 = fmt.Errorf("Something else occured")
err2 = fmt.Errorf("Something else occured again")
// concatenate both the error
return err3
}
UPDATE for Go 1.13:
As of Go version 1.13, the language's errors package now supports error wrapping directly.
You can wrap an error by using the %w verb in fmt.Errorf:
err := errors.New("Original error")
err = fmt.Errorf("%w; Second error", err)
Use Unwrap to remove the last error added, and return what remains: previousErrors := errors.Unwrap(err)
Playground Example for errors.Unwrap
Two more functions, errors.Is and errors.As provide ways to check for and retrieve a specific type of error.
Playground Example for errors.As and errors.Is
Dave Cheney's excellent errors package (https://github.com/pkg/errors) include a Wrap function for this purpose:
package main
import "fmt"
import "github.com/pkg/errors"
func main() {
err := errors.New("error")
err = errors.Wrap(err, "open failed")
err = errors.Wrap(err, "read config failed")
fmt.Println(err) // "read config failed: open failed: error"
}
This also allows additional functionality, such as unpacking the cause of the error:
package main
import "fmt"
import "github.com/pkg/errors"
func main() {
err := errors.New("original error")
err = errors.Wrap(err, "now this")
fmt.Println(errors.Cause(err)) // "original error"
}
As well as the option to output a stack trace when specifying fmt.Printf("%+v\n", err).
You can find additional information about the package on his blog: here and here.
String functions don't work on errors because error is really an interface that implements the function Error() string.
You can use string functions on err1.Error() and err2.Error()
but not on the "err1" reference itself.
Some errors are structs, like the ones you get from database drivers.
So there's no natural way to use string functions on errors since they may not actually be strings underneath.
As for combining two errors:
Easy, just use fmt.Errorf again.
fmt.Errorf("Combined error: %v %v", err1, err2)
Alternatively:
errors.New(err1.Error() + err2.Error())
You could use the strings.Join() and append() function to acheive this slice.
example: golang playgorund
package main
import (
"fmt"
"strings"
"syscall"
)
func main() {
// create a slice for the errors
var errstrings []string
// first error
err1 := fmt.Errorf("First error:server error")
errstrings = append(errstrings, err1.Error())
// do something
err2 := fmt.Errorf("Second error:%s", syscall.ENOPKG.Error())
errstrings = append(errstrings, err2.Error())
// do something else
err3 := fmt.Errorf("Third error:%s", syscall.ENOTCONN.Error())
errstrings = append(errstrings, err3.Error())
// combine and print all the error
fmt.Println(fmt.Errorf(strings.Join(errstrings, "\n")))
}
This would output a single string which you can send back to the client.
First error:server1
Second error:Package not installed
Third error:Socket is not connected
hope this helps!
To expand on what #WillC had mentioned in a comment it is possible to define your own error type as error is an interface type. Any type that implements a Error() string function implements the error interface. Therefore, you could create a CollectionError which aggregates errors and returns a concatenated error string.
type ErrorCollector []error
func (c *ErrorCollector) Collect(e error) { *c = append(*c, e) }
func (c *ErrorCollector) Error() (err string) {
err = "Collected errors:\n"
for i, e := range *c {
err += fmt.Sprintf("\tError %d: %s\n", i, e.Error())
}
return err
}
This provides a collection function that appends a given error to a slice. Upon calling Error() string it iterates over the slice and creates a concatenated error string.
func main() {
collector := new(ErrorCollector)
for i := 0; i < 10; i++ {
collector.Collect(errors.New(fmt.Sprintf("%d Error", i)))
}
fmt.Println(collector)
}
There is a great golang.org blog post going over errors in more detail. A full example of the example is available on The Go Playground.
Uber has a multierr package for this use case:
return multierr.Combine(err1, err2)
People may be interested in https://github.com/hashicorp/go-multierror which describes itself as "A Go (golang) package for representing a list of errors as a single error.".
As of Go 1.20, we'll be able to wrap multiple errors using errors.Join.
See this proposal for more details.
First Option
You can print the errors; they will be separated by a newline character when you do that.
var (
ErrIncorrectUsername = errors.New("incorrect username")
ErrIncorrectPassword = errors.New("incorrect password")
)
func main() {
err := validate("ruster", "4321")
// You can print multi-line errors
// Each will be separated by a newline character (\n).
if err != nil {
fmt.Println(err)
// incorrect username
// incorrect password
}
}
func validate(username, password string) error {
var errs []error
// errors.Join the errors into a single error
if username != "gopher" {
errs = append(errs, ErrIncorrectUsername)
}
if password != "1234" {
errs = append(errs, ErrIncorrectPassword)
}
// Join returns a single `error`.
// Underlying, the error contains all the errors we add.
return errors.Join(errs...)
}
Second Option
errors.Join returns an error that contains each error you add. So you can use errors.Is and errors.As to check for individual errors for finer granularity.
// ...
func main() {
err := validate("ruster", "4321")
// You can detect each one:
if errors.Is(err, ErrIncorrectUsername) {
// handle the error here
}
// Or detect the other one:
if errors.Is(err, ErrIncorrectPassword) {
// handle the error here
}
}
func validate(username, password string) error {
// ...
}
Note: This naive validate example is here to convey the idea. Instead of chaining errors, think errors like a tree. Join allows you to do that when combined with other Joins.
Run both on Go Playground.
This seems to work well for me (space separated errors):
Put all your errors in a error slice/list/array, ie: var errors [] error
fmt.Sprintf("%s", errors)
var errors []error
errors = append(errors, fmt.Errorf("error 1"))
errors = append(errors, fmt.Errorf("error 2"))
errors = append(errors, fmt.Errorf("and yet another error"))
s := fmt.Sprintf("%s", errors)
fmt.Println(s)
Sometimes i need the way to detect if there some error in the chain. the standard way, provided with https://pkg.go.dev/errors is pretty convenient:
someErr:=errors.New("my error")
fmt.Errorf("can't process request: %w",someErr)
...
err:=f()
if errors.Is(err,someErr){...}
But it could be applied only in the case of detection of the last error in the chain. You can't wrap someErr1 and then someErr2 and then get true from both of checks: errors.Is(err,someErr1) and errors.Is(err,someErr2)
I solved this problem with following type:
func NewJoinedErrors(err1 error, err2 error) JoinedErrors {
return JoinedErrors{err1: err1, err2: err2}
}
type JoinedErrors struct {
err1 error
err2 error
}
func (e JoinedErrors) Error() string {
return fmt.Sprintf("%s: %s", e.err1, e.err2)
}
func (e JoinedErrors) Unwrap() error {
return e.err2
}
func (e JoinedErrors) Is(target error) bool {
return errors.Is(e.err1, target)
}
It uses the fact, that
An error is considered to match a target if it is equal to that target
or if it implements a method Is(error) bool such that Is(target)
returns true.
So you can join two errors and get positive result on both checks:
someErr1:=errors.New("my error 1")
someErr2:=errors.New("my error 2")
err:=NewJoinedErrors(someErr1, someErr2)
// this will be true because
// (e JoinedErrors) Is(target error)
// will return true
if errors.Is(err, someErr1){...}
// this will be true because
// (e JoinedErrors) Unwrap() error
// will return err2
if errors.Is(err, someErr2){...}
you can check it out here: https://play.golang.org/p/W7NGyfvr0v_N
func condenseErrors(errs []error) error {
switch len(errs) {
case 0:
return nil
case 1:
return errs[0]
}
err := errs[0]
for _, e := range errs[1:] {
err = errors.Wrap(err, e.Error())
}
return err
}
Use this function:
func JoinErrs(errs ...error) error {
var joinErrsR func(string, int, ...error) error
joinErrsR = func(soFar string, count int, errs ...error) error {
if len(errs) == 0 {
if count == 0 {
return nil
}
return fmt.Errorf(soFar)
}
current := errs[0]
next := errs[1:]
if current == nil {
return joinErrsR(soFar, count, next...)
}
count++
if count == 1 {
return joinErrsR(fmt.Sprintf("%s", current), count, next...)
} else if count == 2 {
return joinErrsR(fmt.Sprintf("1: %s\n2: %s", soFar, current), count, next...)
}
return joinErrsR(fmt.Sprintf("%s\n%d: %s", soFar, count, current), count, next...)
}
return joinErrsR("", 0, errs...)
}
It will give you nil when all errors are nil, will give you the same error when just one non-nil error, will give you numbered list of non-nil errors when multiple non-nil errors
Try it out here: https://play.golang.org/p/WttztCr-xHG

Resources