atomic.AddInt64 Causes invalid memory address or nil pointer dereference

atomic, go, memory-management, pointers

Solution

The fault is caused in the first case by the atomically updated field not being properly aligned.

On both ARM and x86-32, it is the caller's responsibility to arrange for 64-bit alignment of 64-bit words accessed atomically. The first word in a global variable or in an allocated struct or slice can be relied upon to be 64-bit aligned.

Problem

Calling atomic.AddInt64 on field of a struct panics `invalid memory address or nil pointer dereference`, but not when we re-arrange fields order; why? Using this type: ``` type CountHandler struct { c *RequestContext count int64 } ``` And calling `atomic.AddInt64(&countHandler.count, 1)` (field `c` is nil at this point) panics. But not when we rewrite it as: ``` type CountHandler struct { count int64 c *RequestContext } ``` Error goes away. I guess it should be so, because Go keeps data in memory in a sequential manner and reaching a `nil` value breaks this sequence (of bytes); yet I wonder why is that so again because a pointer should have a fixed size `nil` or other value. This is Go x86 1.4.2 on Windows & complete error message is: ``` 2015/02/23 12:56:44 http: panic serving [::1]:51886: runtime error: invalid memory address or nil pointer dereference goroutine 5 [running]: net/http.func·011() c:/go/src/net/http/server.go:1130 +0xa8 sync/atomic.AddUint64(0x731144, 0x1, 0x0, 0x0, 0x263168) c:/go/src/sync/atomic/asm_386.s:118 +0xc main.(*CountHandler).ServeHTTP(0x731140, 0x263180, 0x122f6380, 0x122f62a0) C:/Workshop/Devox/Workshop-Go/src/geoho/web/app/app.go:62 +0x42 github.com/julienschmidt/httprouter.func·001(0x263180, 0x122f6380, 0x122f62a0, 0x0, 0x0, 0x0) C:/Workshop/Devox/Workshop-Go/src/github.com/julienschmidt/httprouter/router.go:232 +0x4c github.com/julienschmidt/httprouter.(*Router).ServeHTTP(0x122d5d20, 0x263180, 0x122f6380, 0x122f62a0) C:/Workshop/Devox/Workshop-Go/src/github.com/julienschmidt/httprouter/router.go:298 +0x141 net/http.serverHandler.ServeHTTP(0x122d2280, 0x263180, 0x122f6380, 0x122f62a0) c:/go/src/net/http/server.go:1703 +0x145 net/http.(*conn).serve(0x122e01e0) c:/go/src/net/http/server.go:1204 +0x9d8 created by net/http.(*Server).Serve c:/go/src/net/http/server.go:1751 +0x2ce ``` Whole source code is (this code is wrong, I was just about studying `alice`): ``` package main import ( "fmt" "github.com/julienschmidt/httprouter" "github.com/justinas/alice" "net/http" "os" "sync/atomic" ) // play with alice func main() { c1 := alice.New(Counter, Texter).Then(nil) router := httprouter.New() router.Handler("GET", "/", c1) router.GET("/kill", kill) http.ListenAndServe(":27007", router) } func kill(rw http.ResponseWriter, rq *http.Request, pl httprouter.Params) { os.Exit(0) } var ch CountHandler // constructors: func Counter(h http.Handler) http.Handler { return &ch } func Texter(h http.Handler) http.Handler { var t TextHandler switch x := h.(type) { case *CountHandler: t.c = x.c t.text = fmt.Sprintf("called so far %d", atomic.LoadInt64(&x.count)) } return &t } // handlers: type RequestContext struct { val int } type CountHandler struct { c *RequestContext count int64 } func (c *CountHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { atomic.AddInt64(&c.count, 1) } type TextHandler struct { c *RequestContext text string } func (t *TextHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { rw.Write([]byte(t.text)) } ```

Original source