Browse Source

Fix race in state machine compilation/invalidation

Previously, a state machine invalidation could have raced against an
in-flight routing attempt: if the invalidation occured after the routing
attempt had already completed its nil-check (choosing not to compile a
new state machine) but before the state machine was atomically loaded to
perform routing, the routing goroutine would begin to panic from
dereferencing nil.

The meat of this change is that we now return the state machine that we
compiled (while still holding the lock), and we only ever interact with
the state machine through atomic pointer loads.
Carl Jackson 12 years ago
parent
commit
8344b8a29e
2 changed files with 8 additions and 6 deletions
  1. +2
    -2
      web/atomic.go
  2. +6
    -4
      web/router.go

+ 2
- 2
web/atomic.go View File

@ -5,10 +5,10 @@ import (
"unsafe"
)
func (rt *router) getMachine() routeMachine {
func (rt *router) getMachine() *routeMachine {
ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine))
sm := (*routeMachine)(atomic.LoadPointer(ptr))
return *sm
return sm
}
func (rt *router) setMachine(m *routeMachine) {
ptr := (*unsafe.Pointer)(unsafe.Pointer(&rt.machine))


+ 6
- 4
web/router.go View File

@ -231,7 +231,7 @@ func (rm routeMachine) route(c *C, w http.ResponseWriter, r *http.Request) (meth
// after all the routes have been added, and will be called automatically for
// you (at some performance cost on the first request) if you do not call it
// explicitly.
func (rt *router) Compile() {
func (rt *router) Compile() *routeMachine {
rt.lock.Lock()
defer rt.lock.Unlock()
sm := routeMachine{
@ -239,14 +239,16 @@ func (rt *router) Compile() {
routes: rt.routes,
}
rt.setMachine(&sm)
return &sm
}
func (rt *router) route(c *C, w http.ResponseWriter, r *http.Request) {
if rt.machine == nil {
rt.Compile()
rm := rt.getMachine()
if rm == nil {
rm = rt.Compile()
}
methods, ok := rt.getMachine().route(c, w, r)
methods, ok := rm.route(c, w, r)
if ok {
return
}


Loading…
Cancel
Save