Skip to content

Commit

Permalink
Create a global pool to share contexts between all routes
Browse files Browse the repository at this point in the history
  • Loading branch information
rouzier committed Nov 22, 2023
1 parent 58ca6d6 commit 52e116f
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 20 deletions.
24 changes: 18 additions & 6 deletions context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"net/http"
"strings"
"sync"
)

// URLParam returns the url parameter from a http.Request object.
Expand Down Expand Up @@ -39,17 +40,29 @@ var (
RouteCtxKey = &contextKey{"RouteContext"}
)

var contextPool = sync.Pool{
New: func() interface{} {
return NewRouteContext()
},
}

func GetRouteContext(mx *Mux) *Context {
rctx := contextPool.Get().(*Context)
rctx.Routes = mx
return rctx
}

func PutRouteContext(rctx *Context) {
rctx.Reset()
contextPool.Put(rctx)
}

// Context is the default routing context set on the root node of a
// request context to track route patterns, URL parameters and
// an optional routing path.
type Context struct {
Routes Routes

// parentCtx is the parent of this one, for using Context as a
// context.Context directly. This is an optimization that saves
// 1 allocation.
parentCtx context.Context

// Routing path/method override used during the route search.
// See Mux#routeHTTP method.
RoutePath string
Expand Down Expand Up @@ -92,7 +105,6 @@ func (x *Context) Reset() {
x.routeParams.Keys = x.routeParams.Keys[:0]
x.routeParams.Values = x.routeParams.Values[:0]
x.methodNotAllowed = false
x.parentCtx = nil
}

// URLParam returns the corresponding URL parameter value from the request
Expand Down
18 changes: 4 additions & 14 deletions mux.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"net/http"
"strings"
"sync"
)

var _ Router = &Mux{}
Expand Down Expand Up @@ -33,9 +32,6 @@ type Mux struct {
// to a parent mux
parent *Mux

// Routing context pool
pool *sync.Pool

// Custom route not found handler
notFoundHandler http.HandlerFunc

Expand All @@ -50,10 +46,7 @@ type Mux struct {
// NewMux returns a newly initialized Mux object that implements the Router
// interface.
func NewMux() *Mux {
mux := &Mux{tree: &node{}, pool: &sync.Pool{}}
mux.pool.New = func() interface{} {
return NewRouteContext()
}
mux := &Mux{tree: &node{}}
return mux
}

Expand All @@ -78,17 +71,14 @@ func (mx *Mux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// mx.handler that is comprised of mx.middlewares + mx.routeHTTP.
// Once the request is finished, reset the routing context and put it back
// into the pool for reuse from another request.
rctx = mx.pool.Get().(*Context)
rctx.Reset()
rctx.Routes = mx
rctx.parentCtx = r.Context()
rctx = GetRouteContext(mx)
defer PutRouteContext(rctx)

// NOTE: r.WithContext() causes 2 allocations and context.WithValue() causes 1 allocation
r = r.WithContext(context.WithValue(r.Context(), RouteCtxKey, rctx))

// Serve the request and once its done, put the request context back in the sync pool
mx.handler.ServeHTTP(w, r)
mx.pool.Put(rctx)
}

// Use appends a middleware handler to the Mux middleware stack.
Expand Down Expand Up @@ -243,7 +233,7 @@ func (mx *Mux) With(middlewares ...func(http.Handler) http.Handler) Router {
mws = append(mws, middlewares...)

im := &Mux{
pool: mx.pool, inline: true, parent: mx, tree: mx.tree, middlewares: mws,
inline: true, parent: mx, tree: mx.tree, middlewares: mws,
notFoundHandler: mx.notFoundHandler, methodNotAllowedHandler: mx.methodNotAllowedHandler,
}

Expand Down

0 comments on commit 52e116f

Please sign in to comment.