@@ -27,30 +27,25 @@ import (
27
27
"sigs.k8s.io/controller-runtime/pkg/predicate"
28
28
)
29
29
30
- const (
31
- // defaultBufferSize is the default number of event notifications that can be buffered.
32
- defaultBufferSize = 1024
33
- )
30
+ // ChannelOptions contains the options for the Channel source.
31
+ type ChannelOptions struct {
32
+ // DestBufferSize is the specified buffer size of dest channels.
33
+ // Default to 1024 if not specified.
34
+ DestBufferSize int
35
+ }
34
36
35
37
// Channel is used to provide a source of events originating outside the cluster
36
38
// (e.g. GitHub Webhook callback). Channel requires the user to wire the external
37
39
// source (eh.g. http handler) to write GenericEvents to the underlying channel.
38
40
type Channel struct {
39
- // once ensures the event distribution goroutine will be performed only once
40
- once sync.Once
41
-
42
- // Source is the source channel to fetch GenericEvents
43
- Source <- chan event.GenericEvent
41
+ Options ChannelOptions
44
42
45
- // dest is the destination channels of the added event handlers
46
- dest []chan event.GenericEvent
47
-
48
- // DestBufferSize is the specified buffer size of dest channels.
49
- // Default to 1024 if not specified.
50
- DestBufferSize int
43
+ // Broadcaster contains the source channel for events.
44
+ Broadcaster * ChannelBroadcaster
51
45
52
- // destLock is to ensure the destination channels are safely added/removed
53
- destLock sync.Mutex
46
+ mu sync.Mutex
47
+ // isStarted is true if the source has been started. A source can only be started once.
48
+ isStarted bool
54
49
}
55
50
56
51
func (cs * Channel ) String () string {
@@ -63,88 +58,70 @@ func (cs *Channel) Start(
63
58
handler handler.EventHandler ,
64
59
queue workqueue.RateLimitingInterface ,
65
60
prct ... predicate.Predicate ) error {
66
- // Source should have been specified by the user.
67
- if cs .Source == nil {
68
- return fmt .Errorf ("must specify Channel.Source " )
61
+ // Broadcaster should have been specified by the user.
62
+ if cs .Broadcaster == nil {
63
+ return fmt .Errorf ("must create Channel with a non-nil Broadcaster " )
69
64
}
70
-
71
- // use default value if DestBufferSize not specified
72
- if cs .DestBufferSize == 0 {
73
- cs .DestBufferSize = defaultBufferSize
65
+ if handler == nil {
66
+ return fmt .Errorf ("must create Channel with a non-nil EventHandler" )
74
67
}
75
68
76
- dst := make (chan event.GenericEvent , cs .DestBufferSize )
77
-
78
- cs .destLock .Lock ()
79
- cs .dest = append (cs .dest , dst )
80
- cs .destLock .Unlock ()
69
+ cs .mu .Lock ()
70
+ defer cs .mu .Unlock ()
71
+ if cs .isStarted {
72
+ return fmt .Errorf ("cannot start an already started Channel source" )
73
+ }
74
+ cs .isStarted = true
81
75
82
- cs . once . Do ( func () {
83
- // Distribute GenericEvents to all EventHandler / Queue pairs Watching this source
84
- go cs .syncLoop ( ctx )
85
- } )
76
+ // Create a destination channel for the event handler
77
+ // and add it to the list of destinations
78
+ destination := make ( chan event. GenericEvent , cs .Options . DestBufferSize )
79
+ cs . Broadcaster . AddListener ( destination )
86
80
87
81
go func () {
88
- for evt := range dst {
89
- shouldHandle := true
90
- for _ , p := range prct {
91
- if ! p .Generic (evt ) {
92
- shouldHandle = false
93
- break
94
- }
95
- }
96
-
97
- if shouldHandle {
98
- func () {
99
- ctx , cancel := context .WithCancel (ctx )
100
- defer cancel ()
101
- handler .Generic (ctx , evt , queue )
102
- }()
103
- }
104
- }
82
+ // Remove the listener and wait for the broadcaster
83
+ // to stop sending events to the destination channel.
84
+ defer cs .Broadcaster .RemoveListener (destination )
85
+
86
+ cs .processReceivedEvents (
87
+ ctx ,
88
+ destination ,
89
+ queue ,
90
+ handler ,
91
+ prct ,
92
+ )
105
93
}()
106
94
107
95
return nil
108
96
}
109
97
110
- func (cs * Channel ) doStop () {
111
- cs .destLock .Lock ()
112
- defer cs .destLock .Unlock ()
113
-
114
- for _ , dst := range cs .dest {
115
- close (dst )
116
- }
117
- }
118
-
119
- func (cs * Channel ) distribute (evt event.GenericEvent ) {
120
- cs .destLock .Lock ()
121
- defer cs .destLock .Unlock ()
122
-
123
- for _ , dst := range cs .dest {
124
- // We cannot make it under goroutine here, or we'll meet the
125
- // race condition of writing message to closed channels.
126
- // To avoid blocking, the dest channels are expected to be of
127
- // proper buffer size. If we still see it blocked, then
128
- // the controller is thought to be in an abnormal state.
129
- dst <- evt
130
- }
131
- }
132
-
133
- func (cs * Channel ) syncLoop (ctx context.Context ) {
98
+ func (cs * Channel ) processReceivedEvents (
99
+ ctx context.Context ,
100
+ destination <- chan event.GenericEvent ,
101
+ queue workqueue.RateLimitingInterface ,
102
+ eventHandler handler.EventHandler ,
103
+ predicates []predicate.Predicate ,
104
+ ) {
105
+ eventloop:
134
106
for {
135
107
select {
136
108
case <- ctx .Done ():
137
- // Close destination channels
138
- cs .doStop ()
139
109
return
140
- case evt , stillOpen := <- cs . Source :
110
+ case event , stillOpen := <- destination :
141
111
if ! stillOpen {
142
- // if the source channel is closed, we're never gonna get
143
- // anything more on it, so stop & bail
144
- cs .doStop ()
145
112
return
146
113
}
147
- cs .distribute (evt )
114
+
115
+ // Check predicates against the event first
116
+ // and continue the outer loop if any of them fail.
117
+ for _ , p := range predicates {
118
+ if ! p .Generic (event ) {
119
+ continue eventloop
120
+ }
121
+ }
122
+
123
+ // Call the event handler with the event.
124
+ eventHandler .Generic (ctx , event , queue )
148
125
}
149
126
}
150
127
}
0 commit comments