Why recover() does not work in a nested deferred function? - go

I am testing panic/recover in Golang. This simple program works as expected:
package main
import "fmt"
func printRecover() {
r := recover()
fmt.Println("Recovered:", r)
}
func main() {
defer printRecover()
panic("OMG!")
}
Output:
Recovered: OMG!
However, if I wrap the function printRecover() in a bigger deferred function:
package main
import "fmt"
func printRecover() {
r := recover()
fmt.Println("Recovered:", r)
}
func main() {
defer func() {
printRecover()
}()
panic("OMG!")
}
It does not recover and let the panic go through:
Recovered: <nil>
panic: OMG!
goroutine 1 [running]:
main.main()
/tmp/sandbox898315096/main.go:15 +0x60
Can someone explain the difference?

It is because recover will be nil if not directly called by deferred function
Here is excerpt from golang spec
The return value of recover is nil if any of the following conditions holds:
panic's argument was nil;
the goroutine is not panicking;
recover was not called directly by a deferred function.
For more info check the full spec here

Related

Global Variable Gives SIGSEGV

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.

Why not fini() functions for golang package?

There's built-in init() function for package initialization. Why not fini for destruction ? For example, I initial a goroutine pool inside my package and I want to all goroutines in the pool to finish their task before exiting instead of being forced to exit when the whole program exit.
If there's fini function. I can use sync.Wait there to fulfill my goal.
Another merit for built-in init is that it can and only can be called once,which will be my own concern if I use user-define functions as alternatives for them.
Try the following code:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
defer fini()
wg.Add(1)
go routine()
fmt.Println("... in progress ... ")
}
func fini() {
wg.Wait()
fmt.Println("Done")
}
func init() {
fmt.Println("Hi")
}
func routine() {
fmt.Println("Doing somthing ...")
time.Sleep(1000 * time.Millisecond)
wg.Done()
}
var wg sync.WaitGroup
Run:
$ go run .
Hi
... in progress ...
Doing somthing ...
Done
Doing fini in function main can solves my problem

How to test that a function was called in a goroutine?

I'd like to make sure that we're starting a goroutine by calling a function with the right arguments.
For example:
func MyTest(t *testing.T) {
service.EXPECT().MyMockFunc(1)
service.MyFunc()
}
func MyFunc() {
go MyMockFunc(1)
}
When I run this test, it fails because (I believe) MyMockFunc only gets called after the test has already finished running.
Is there a way to test that I started a goroutine by calling a function with the right arguments?
Note: Ideally, I'd like to keep the arguments I pass to MyMockFunc as is (not add a channel arg for instance).
Using a channel and assuming you can fire the goroutine from the test:
package main
import (
"fmt"
"testing"
"time"
)
func MyMockFunc(n int) int {
fmt.Println("MyMockFunc is called")
time.Sleep(5 * time.Second)
return n + 1
}
func TestMyMockFunc(t *testing.T) {
result := make(chan int)
go func() {
result <- MyMockFunc(1)
}()
if <-result != 2 {
t.Fatalf("Expecting 2")
}
}

How can I prevent a data race when adding handlers in a goroutine?

In my HTTP application written in golang I have a few routes that rely on 3rd party services (and vendored code) to do some work before I am actually able to register the route. This might fail or need to be retried, but I still want the application to respond to other requests while this is process is potentially ongoing.
This means I am registering handlers on http.DefaultServeMux in goroutines that I spawn from my main func. This works as expected, but I will find my tests complaining about data races now.
A minimal case to repro looks like this:
package main
import (
"log"
"net/http"
)
func main() {
go func() {
http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello"))
})
}()
srv := http.Server{
Addr: ":3000",
}
log.Fatal(srv.ListenAndServe())
}
With a test like:
package main
import (
"io/ioutil"
"net/http"
"os"
"testing"
"time"
)
func TestMain(m *testing.M) {
go main()
time.Sleep(time.Second)
os.Exit(m.Run())
}
func TestHello(t *testing.T) {
t.Run("default", func(t *testing.T) {
res, err := http.DefaultClient.Get("http://0.0.0.0:3000/hello")
if err != nil {
t.Fatalf("Calling /hello returned %v", err)
}
if res.StatusCode != http.StatusOK {
b, _ := ioutil.ReadAll(res.Body)
defer res.Body.Close()
t.Errorf("Expected /hello to return 200 response, got %v with body %v", res.StatusCode, string(b))
}
})
}
will show me the following output:
==================
WARNING: DATA RACE
Read at 0x000000a337d8 by goroutine 14:
net/http.(*ServeMux).shouldRedirect()
/usr/local/go/src/net/http/server.go:2239 +0x162
net/http.(*ServeMux).redirectToPathSlash()
/usr/local/go/src/net/http/server.go:2224 +0x64
net/http.(*ServeMux).Handler()
/usr/local/go/src/net/http/server.go:2293 +0x184
net/http.(*ServeMux).ServeHTTP()
/usr/local/go/src/net/http/server.go:2336 +0x6d
net/http.serverHandler.ServeHTTP()
/usr/local/go/src/net/http/server.go:2694 +0xb9
net/http.(*conn).serve()
/usr/local/go/src/net/http/server.go:1830 +0x7dc
Previous write at 0x000000a337d8 by goroutine 8:
net/http.(*ServeMux).Handle()
/usr/local/go/src/net/http/server.go:2357 +0x216
net/http.(*ServeMux).HandleFunc()
/usr/local/go/src/net/http/server.go:2368 +0x62
net/http.HandleFunc()
/usr/local/go/src/net/http/server.go:2380 +0x68
github.com/m90/test.main.func1()
/home/frederik/projects/go/src/github.com/m90/test/main.go:10 +0x4f
Goroutine 14 (running) created at:
net/http.(*Server).Serve()
/usr/local/go/src/net/http/server.go:2795 +0x364
net/http.(*Server).ListenAndServe()
/usr/local/go/src/net/http/server.go:2711 +0xc4
github.com/m90/test.main()
/home/frederik/projects/go/src/github.com/m90/test/main.go:17 +0xb6
Goroutine 8 (finished) created at:
github.com/m90/test.main()
/home/frederik/projects/go/src/github.com/m90/test/main.go:9 +0x46
==================
From what I understand reading the code of package http net/http.(*ServeMux).Handler() in the stacktrace does not lock the mutex that protects the handler map, as it expects this to be done by net/http.(*ServeMux).handler() which in my scenario does not get called.
Am I doing something that is not supposed to be done? Is this an issue with the standard library? Am I doing something wrong in the way I attach the handlers in a goroutine?
This seems to be an issue in package http itself, that is resolved via this Pull Request.
As of April 2018 the patch is not included in go1.10.1, but it's supposed to ship with go1.11

Is deferring a field assignation a race condition?

Consider the following code:
type foo struct {
bar string
}
func f() *foo {
ret := &foo{"before"}
defer func() { ret.bar = "after" }()
return ret
}
func main() {
fmt.Println(f()) // prints "&{after}"
}
The motivation is having to return a struct but some of the fields need to be set only before returning (e.g. a timestamp of when the function completed).
Is deferring the field assignation a race condition?
Is it idiomatic?
Is there a better way?
The main benefit of using defer statement versus invoking something before returning from a function or routine is defer will run the statement even when a panic occurs before returning.
Thus it is commonly used to clean up resources (such as closing a file or network connection) rather than to set state.
The function below will not print or return "hello"
func f() string {
panic("omg")
fmt.Println("hello")
return "hello"
}
This code will print but won't return "hello"
func f() string {
defer fmt.Println("ello")
panic("omg")
return "hello"
}
To answer your question: No, it won't result in a race. Apart from the above difference, it is equivalent to calling something before the return statement.
You mention timestamping when a function completes. In that case you can use defer like this:
package main
import (
"fmt"
"time"
)
func main() {
foo()
}
func foo() {
defer trace("foo")()
time.Sleep(1 * time.Second)
}
func trace(fn string) func() {
start := time.Now()
return func() {
layout := "15:04:05.000"
end := time.Now()
fmt.Printf("%s start at %s, end at %s, total %s", fn, start.Format(layout), end.Format(layout), end.Sub(start))
}
}
Output: foo start at 23:00:00.000, end at 23:00:01.000, total 1s

Resources