Skip to content

Commit 4d13ff3

Browse files
committed
improvements of the new ContextWrapper
1 parent ec69670 commit 4d13ff3

File tree

5 files changed

+142
-86
lines changed

5 files changed

+142
-86
lines changed

HISTORY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Developers are not forced to upgrade if they don't really need it. Upgrade whene
2323

2424
Changes apply to `main` branch.
2525

26-
- A new way to customize the handler's parameter among with the `hero` and `mvc` packages. New `iris.NewContextWrapper` method and `iris.DefaultContextPool` struct were added to wrap a handler and use a custom context instead of the iris.Context directly.
26+
- A new way to customize the handler's parameter among with the `hero` and `mvc` packages. New `iris.NewContextWrapper` and `iris.NewContextPool` methods were added to wrap a handler and use a custom context instead of the iris.Context directly. Example at: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context.
2727

2828
- The `cache` sub-package has an update, after 4 years:
2929

_examples/routing/custom-context/main.go

Lines changed: 45 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,21 @@
11
package main
22

33
import (
4+
"sync"
5+
46
"github.com/kataras/iris/v12"
57
)
68

79
func main() {
810
// 1. Create the iris app instance.
911
app := iris.New()
1012

11-
/*
12-
w := iris.NewContextWrapper(&iris.DefaultContextPool[*myCustomContext]{
13-
AcquireFunc: func(ctx iris.Context) *myCustomContext {
14-
return &myCustomContext{
15-
Context: ctx,
16-
// custom fields here...
17-
}
18-
},
19-
ReleaseFunc: func(t *myCustomContext) {
20-
// do nothing
21-
},
22-
})
23-
OR: */
2413
// 2. Create the Context Wrapper which will be used to wrap the handlers
2514
// that expect a *myCustomContext instead of iris.Context.
2615
w := iris.NewContextWrapper(&myCustomContextPool{})
16+
// OR:
17+
// w := iris.NewContextWrapper(iris.NewContextPool[myCustomContext, *myCustomContext]())
18+
// The example custom context pool operates exactly the same as the result of iris.NewContextPool.
2719

2820
// 3. Register the handler(s) which expects a *myCustomContext instead of iris.Context.
2921
// The `w.Handler` will wrap the handler and will call the `Acquire` and `Release`
@@ -38,6 +30,38 @@ func index(ctx *myCustomContext) {
3830
ctx.HTML("<h1>Hello, World!</h1>")
3931
}
4032

33+
/*
34+
Custom Context Pool
35+
*/
36+
// Create the context sync pool for our custom context,
37+
// the pool must implement Acquire() T and Release(T) methods to satisfy the iris.ContextPool interface.
38+
type myCustomContextPool struct {
39+
pool sync.Pool
40+
}
41+
42+
// Acquire returns a new custom context from the pool.
43+
func (p *myCustomContextPool) Acquire(ctx iris.Context) *myCustomContext {
44+
v := p.pool.Get()
45+
if v == nil {
46+
v = &myCustomContext{
47+
Context: ctx,
48+
// custom fields here...
49+
}
50+
}
51+
52+
return v.(*myCustomContext)
53+
}
54+
55+
// Release puts a custom context back to the pool.
56+
func (p *myCustomContextPool) Release(t *myCustomContext) {
57+
// You can take advantage of this method to clear the context
58+
// and re-use it on the Acquire method, use the sync.Pool.
59+
p.pool.Put(t)
60+
}
61+
62+
/*
63+
Custom Context
64+
*/
4165
// Create a custom context.
4266
type myCustomContext struct {
4367
// It's just an embedded field which is set on AcquireFunc,
@@ -47,28 +71,16 @@ type myCustomContext struct {
4771
iris.Context
4872
}
4973

74+
// SetContext sets the original iris.Context,
75+
// should be implemented by custom context type(s) when
76+
// the ContextWrapper uses a context Pool through the iris.NewContextPool function.
77+
// Comment line 15, uncomment line 17 and the method below.
78+
func (c *myCustomContext) SetContext(ctx iris.Context) {
79+
c.Context = ctx
80+
}
81+
5082
func (c *myCustomContext) HTML(format string, args ...interface{}) (int, error) {
5183
c.Application().Logger().Info("HTML was called from custom Context")
5284

5385
return c.Context.HTML(format, args...)
5486
}
55-
56-
// Create the context memory pool for your custom context,
57-
// the pool must contain Acquire() T and Release(T) methods.
58-
type myCustomContextPool struct{}
59-
60-
// Acquire returns a new custom context from the pool.
61-
func (p *myCustomContextPool) Acquire(ctx iris.Context) *myCustomContext {
62-
return &myCustomContext{
63-
Context: ctx,
64-
// custom fields here...
65-
}
66-
}
67-
68-
// Release puts a custom context back to the pool.
69-
func (p *myCustomContextPool) Release(t *myCustomContext) {
70-
// You can take advantage of this method to clear the context
71-
// and re-use it on the Acquire method, use the sync.Pool.
72-
//
73-
// We do nothing for the shake of the exampel.
74-
}

context_wrapper.go

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,101 @@
11
package iris
22

33
import (
4+
"sync"
5+
46
"github.com/kataras/iris/v12/context"
57
)
68

7-
// ContextPool is a pool of T.
8-
//
9-
// See `NewContextWrapper` and `ContextPool` for more.
10-
type ContextPool[T any] interface {
11-
Acquire(ctx Context) T
12-
Release(T)
13-
}
9+
type (
10+
// ContextSetter is an interface which can be implemented by a struct
11+
// to set the iris.Context to the struct.
12+
// The receiver must be a pointer of the struct.
13+
ContextSetter interface {
14+
// SetContext sets the iris.Context to the struct.
15+
SetContext(Context)
16+
}
1417

15-
// DefaultContextPool is a pool of T.
16-
// It's used to acquire and release T.
17-
// The T is acquired from the pool and released back to the pool after the handler's execution.
18-
// The T is passed to the handler as an argument.
19-
// The T is not shared between requests.
20-
type DefaultContextPool[T any] struct {
21-
AcquireFunc func(Context) T
22-
ReleaseFunc func(T)
23-
}
18+
// ContextSetterPtr is a pointer of T which implements the `ContextSetter` interface.
19+
// The T must be a struct.
20+
ContextSetterPtr[T any] interface {
21+
*T
22+
ContextSetter
23+
}
24+
25+
// emptyContextSetter is an empty struct which implements the `ContextSetter` interface.
26+
emptyContextSetter struct{}
27+
)
28+
29+
// SetContext method implements `ContextSetter` interface.
30+
func (*emptyContextSetter) SetContext(Context) {}
2431

25-
// Ensure that DefaultContextPool[T] implements ContextPool[T].
26-
var _ ContextPool[any] = (*DefaultContextPool[any])(nil)
27-
28-
// Acquire returns a new T from the pool's AcquireFunc.
29-
func (p *DefaultContextPool[T]) Acquire(ctx Context) T {
30-
acquire := p.AcquireFunc
31-
if p.AcquireFunc == nil {
32-
acquire = func(ctx Context) T {
33-
var t T
34-
return t
35-
}
32+
// ContextPool is a pool of T. It's used to acquire and release custom context.
33+
// Use of custom implementation or `NewContextPool`.
34+
//
35+
// See `NewContextWrapper` and `NewContextPool` for more.
36+
type (
37+
ContextPool[T any] interface {
38+
// Acquire must return a new T from a pool.
39+
Acquire(ctx Context) T
40+
// Release must put the T back to the pool.
41+
Release(T)
3642
}
3743

38-
return acquire(ctx)
39-
}
44+
// syncContextPool is a sync pool implementation of T.
45+
// It's used to acquire and release T.
46+
// The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.
47+
// The contextPtr is passed to the handler as an argument.
48+
// ThecontextPtr is not shared between requests.
49+
// The contextPtr must implement the `ContextSetter` interface.
50+
// The T must be a struct.
51+
// The contextPtr must be a pointer of T.
52+
syncContextPool[T any, contextPtr ContextSetterPtr[T]] struct {
53+
pool *sync.Pool
54+
}
55+
)
4056

41-
// Release does nothing if the pool's ReleaseFunc is nil.
42-
func (p *DefaultContextPool[T]) Release(t T) {
43-
release := p.ReleaseFunc
44-
if p.ReleaseFunc == nil {
45-
release = func(t T) {}
57+
// Ensure that syncContextPool implements ContextPool.
58+
var _ ContextPool[*emptyContextSetter] = (*syncContextPool[emptyContextSetter, *emptyContextSetter])(nil)
59+
60+
// NewContextPool returns a new ContextPool default implementation which
61+
// uses sync.Pool to implement its Acquire and Release methods.
62+
// The contextPtr is acquired from the sync pool and released back to the sync pool after the handler's execution.
63+
// The contextPtr is passed to the handler as an argument.
64+
// ThecontextPtr is not shared between requests.
65+
// The contextPtr must implement the `ContextSetter` interface.
66+
// The T must be a struct.
67+
// The contextPtr must be a pointer of T.
68+
//
69+
// Example:
70+
// w := iris.NewContextWrapper(iris.NewContextPool[myCustomContext, *myCustomContext]())
71+
func NewContextPool[T any, contextPtr ContextSetterPtr[T]]() ContextPool[contextPtr] {
72+
return &syncContextPool[T, contextPtr]{
73+
pool: &sync.Pool{
74+
New: func() interface{} {
75+
var t contextPtr = new(T)
76+
return t
77+
},
78+
},
4679
}
80+
}
81+
82+
// Acquire returns a new T from the sync pool.
83+
func (p *syncContextPool[T, contextPtr]) Acquire(ctx Context) contextPtr {
84+
// var t contextPtr
85+
// if v := p.pool.Get(); v == nil {
86+
// t = new(T)
87+
// } else {
88+
// t = v.(contextPtr)
89+
// }
90+
91+
t := p.pool.Get().(contextPtr)
92+
t.SetContext(ctx)
93+
return t
94+
}
4795

48-
release(t)
96+
// Release puts the T back to the sync pool.
97+
func (p *syncContextPool[T, contextPtr]) Release(t contextPtr) {
98+
p.pool.Put(t)
4999
}
50100

51101
// ContextWrapper is a wrapper for handlers which expect a T instead of iris.Context.
@@ -60,19 +110,13 @@ type ContextWrapper[T any] struct {
60110
// The default pool's AcquireFunc returns a zero value of T.
61111
// The default pool's ReleaseFunc does nothing.
62112
// The default pool is used when the pool is nil.
63-
// Use the `&iris.DefaultContextPool{...}` to pass a simple context pool.
113+
// Use the `iris.NewContextPool[T, *T]()` to pass a simple context pool.
114+
// Then, use the `Handler` method to wrap custom handlers to iris ones.
64115
//
65-
// See the `Handler` method for more.
66116
// Example: https://github.com/kataras/iris/tree/main/_examples/routing/custom-context
67117
func NewContextWrapper[T any](pool ContextPool[T]) *ContextWrapper[T] {
68118
if pool == nil {
69-
pool = &DefaultContextPool[T]{
70-
AcquireFunc: func(ctx Context) T {
71-
var t T
72-
return t
73-
},
74-
ReleaseFunc: func(t T) {},
75-
}
119+
panic("pool cannot be nil")
76120
}
77121

78122
return &ContextWrapper[T]{

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ require (
3535
github.com/redis/go-redis/v9 v9.3.0
3636
github.com/schollz/closestmatch v2.1.0+incompatible
3737
github.com/shirou/gopsutil/v3 v3.23.10
38-
github.com/tdewolff/minify/v2 v2.20.5
38+
github.com/tdewolff/minify/v2 v2.20.6
3939
github.com/vmihailenco/msgpack/v5 v5.4.1
4040
github.com/yosssi/ace v0.0.5
4141
go.etcd.io/bbolt v1.3.8
@@ -95,7 +95,7 @@ require (
9595
github.com/shoenig/go-m1cpu v0.1.6 // indirect
9696
github.com/sirupsen/logrus v1.8.1 // indirect
9797
github.com/stretchr/testify v1.8.4 // indirect
98-
github.com/tdewolff/parse/v2 v2.7.3-0.20231031132452-e7c20a5d77ab // indirect
98+
github.com/tdewolff/parse/v2 v2.7.4 // indirect
9999
github.com/tklauser/go-sysconf v0.3.12 // indirect
100100
github.com/tklauser/numcpus v0.6.1 // indirect
101101
github.com/valyala/bytebufferpool v1.0.0 // indirect

go.sum

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)