@@ -23,7 +23,8 @@ import (
23
23
)
24
24
25
25
const (
26
- defaultSyncTimeout = 10 * time .Second
26
+ defaultAsyncTimeout = 0
27
+ defaultSyncTimeout = 10 * time .Second
27
28
)
28
29
29
30
// SubscriberOption configures subscriber behavior
@@ -33,7 +34,6 @@ type SubscriberOption func(*subscriberConfig)
33
34
type subscriberConfig struct {
34
35
sendInitial bool
35
36
customChannel chan string
36
- syncBroadcast bool
37
37
syncTimeout time.Duration
38
38
}
39
39
@@ -62,7 +62,7 @@ func WithCustomChannel(ch chan string) SubscriberOption {
62
62
// is delivered to the channel, rather than dropping messages for full channels
63
63
func WithSyncBroadcast () SubscriberOption {
64
64
return func (config * subscriberConfig ) {
65
- config .syncBroadcast = true
65
+ config .syncTimeout = defaultSyncTimeout
66
66
}
67
67
}
68
68
@@ -85,7 +85,7 @@ func (fsm *Machine) GetStateChanWithOptions(
85
85
) <- chan string {
86
86
config := & subscriberConfig {
87
87
sendInitial : true ,
88
- syncTimeout : defaultSyncTimeout ,
88
+ syncTimeout : defaultAsyncTimeout ,
89
89
}
90
90
91
91
for _ , opt := range opts {
@@ -147,7 +147,7 @@ func (fsm *Machine) GetStateChanBuffer(ctx context.Context, chanBufferSize int)
147
147
func (fsm * Machine ) AddSubscriber (ch chan string ) func () {
148
148
config := & subscriberConfig {
149
149
sendInitial : true ,
150
- syncTimeout : defaultSyncTimeout ,
150
+ syncTimeout : defaultAsyncTimeout ,
151
151
}
152
152
return fsm .addSubscriberWithConfig (ch , config )
153
153
}
@@ -183,8 +183,9 @@ func (fsm *Machine) unsubscribe(ch chan string) {
183
183
}
184
184
185
185
// broadcast sends the new state to all subscriber channels.
186
- // For async subscribers, if a channel is full, the state change is skipped for that channel, and a warning is logged.
187
- // For sync subscribers, the broadcast blocks until the message is delivered or times out after 10 seconds.
186
+ // Subscribers with negative timeout block indefinitely until delivered.
187
+ // Subscribers with timeout=0 behave asynchronously (drop messages if channel is full).
188
+ // Subscribers with positive timeout block until delivered or timeout.
188
189
// This, and the other subscriber-related methods, use a standard mutex instead of an RWMutex,
189
190
// because the broadcast sends should always be serial, and never concurrent, otherwise the order
190
191
// of state change notifications could be unpredictable.
@@ -208,21 +209,15 @@ func (fsm *Machine) broadcast(state string) {
208
209
return true
209
210
}
210
211
211
- if config .syncBroadcast {
212
- // Handle sync broadcast with timeout in parallel goroutines
212
+ if config .syncTimeout < 0 {
213
+ // Handle infinite blocking broadcast
213
214
wg .Add (1 )
214
215
go func (ch chan string ) {
215
216
defer wg .Done ()
216
- select {
217
- case ch <- state :
218
- logger .Debug ("State delivered to synchronous subscriber" )
219
- case <- time .After (config .syncTimeout ):
220
- logger .Warn ("Synchronous subscriber blocked; state delivery timed out" ,
221
- "timeout" , config .syncTimeout ,
222
- "channel_capacity" , cap (ch ), "channel_length" , len (ch ))
223
- }
217
+ ch <- state
218
+ logger .Debug ("State delivered to blocking subscriber" )
224
219
}(ch )
225
- } else {
220
+ } else if config . syncTimeout == 0 {
226
221
// Handle async broadcast (non-blocking)
227
222
select {
228
223
case ch <- state :
@@ -231,6 +226,20 @@ func (fsm *Machine) broadcast(state string) {
231
226
logger .Debug ("Asynchronous subscriber channel full; state delivery skipped" ,
232
227
"channel_capacity" , cap (ch ), "channel_length" , len (ch ))
233
228
}
229
+ } else {
230
+ // Handle sync broadcast with positive timeout
231
+ wg .Add (1 )
232
+ go func (ch chan string , timeout time.Duration ) {
233
+ defer wg .Done ()
234
+ select {
235
+ case ch <- state :
236
+ logger .Debug ("State delivered to synchronous subscriber" )
237
+ case <- time .After (timeout ):
238
+ logger .Warn ("Synchronous subscriber blocked; state delivery timed out" ,
239
+ "timeout" , timeout ,
240
+ "channel_capacity" , cap (ch ), "channel_length" , len (ch ))
241
+ }
242
+ }(ch , config .syncTimeout )
234
243
}
235
244
return true // continue iteration
236
245
})
0 commit comments