@@ -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
43
+ // Broadcaster contains the source channel for events.
44
+ Broadcaster * ChannelBroadcaster
47
45
48
- // DestBufferSize is the specified buffer size of dest channels.
49
- // Default to 1024 if not specified.
50
- DestBufferSize int
51
-
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,67 @@ 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
65
71
- // use default value if DestBufferSize not specified
72
- if cs .DestBufferSize == 0 {
73
- cs .DestBufferSize = defaultBufferSize
66
+ cs .mu .Lock ()
67
+ defer cs .mu .Unlock ()
68
+ if cs .isStarted {
69
+ return fmt .Errorf ("cannot start an already started Channel source" )
74
70
}
71
+ cs .isStarted = true
75
72
76
- dst := make (chan event.GenericEvent , cs .DestBufferSize )
77
-
78
- cs .destLock .Lock ()
79
- cs .dest = append (cs .dest , dst )
80
- cs .destLock .Unlock ()
81
-
82
- cs .once .Do (func () {
83
- // Distribute GenericEvents to all EventHandler / Queue pairs Watching this source
84
- go cs .syncLoop (ctx )
85
- })
73
+ // Create a destination channel for the event handler
74
+ // and add it to the list of destinations
75
+ destination := make (chan event.GenericEvent , cs .Options .DestBufferSize )
76
+ cs .Broadcaster .AddListener (destination )
86
77
87
78
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
- }
79
+ // Remove the listener and wait for the broadcaster
80
+ // to stop sending events to the destination channel.
81
+ defer cs .Broadcaster .RemoveListener (destination )
82
+
83
+ cs .processReceivedEvents (
84
+ ctx ,
85
+ destination ,
86
+ queue ,
87
+ handler ,
88
+ prct ,
89
+ )
105
90
}()
106
91
107
92
return nil
108
93
}
109
94
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 ) {
95
+ func (cs * Channel ) processReceivedEvents (
96
+ ctx context.Context ,
97
+ destination <- chan event.GenericEvent ,
98
+ queue workqueue.RateLimitingInterface ,
99
+ eventHandler handler.EventHandler ,
100
+ predicates []predicate.Predicate ,
101
+ ) {
102
+ eventloop:
134
103
for {
135
104
select {
136
105
case <- ctx .Done ():
137
- // Close destination channels
138
- cs .doStop ()
139
106
return
140
- case evt , stillOpen := <- cs . Source :
107
+ case event , stillOpen := <- destination :
141
108
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
109
return
146
110
}
147
- cs .distribute (evt )
111
+
112
+ // Check predicates against the event first
113
+ // and continue the outer loop if any of them fail.
114
+ for _ , p := range predicates {
115
+ if ! p .Generic (event ) {
116
+ continue eventloop
117
+ }
118
+ }
119
+
120
+ // Call the event handler with the event.
121
+ eventHandler .Generic (ctx , event , queue )
148
122
}
149
123
}
150
124
}
0 commit comments