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)
}
}
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
}
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
...
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.
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