@@ -136,38 +136,21 @@ func (r *router) setDefaultHandler(handler MessageHandler) {
136136// associated callback (or the defaultHandler, if one exists and no other route matched). If
137137// anything is sent down the stop channel the function will end.
138138func (r * router ) matchAndDispatch (messages <- chan * packets.PublishPacket , order bool , client * client ) <- chan * PacketAndToken {
139- var wg sync.WaitGroup
140- ackOutChan := make (chan * PacketAndToken ) // Channel returned to caller; closed when messages channel closed
141- var ackInChan chan * PacketAndToken // ACKs generated by ackFunc get put onto this channel
142-
143- stopAckCopy := make (chan struct {}) // Closure requests stop of go routine copying ackInChan to ackOutChan
144- ackCopyStopped := make (chan struct {}) // Closure indicates that it is safe to close ackOutChan
145- goRoutinesDone := make (chan struct {}) // closed on wg.Done()
146- if order {
147- ackInChan = ackOutChan // When order = true no go routines are used so safe to use one channel and close when done
148- } else {
149- // When order = false ACK messages are sent in go routines so ackInChan cannot be closed until all goroutines done
150- ackInChan = make (chan * PacketAndToken )
151- go func () { // go routine to copy from ackInChan to ackOutChan until stopped
152- for {
153- select {
154- case a := <- ackInChan :
155- ackOutChan <- a
156- case <- stopAckCopy :
157- close (ackCopyStopped ) // Signal main go routine that it is safe to close ackOutChan
158- for {
159- select {
160- case <- ackInChan : // drain ackInChan to ensure all goRoutines can complete cleanly (ACK dropped)
161- DEBUG .Println (ROU , "matchAndDispatch received acknowledgment after processing stopped (ACK dropped)." )
162- case <- goRoutinesDone :
163- close (ackInChan ) // Nothing further should be sent (a panic is probably better than silent failure)
164- DEBUG .Println (ROU , "matchAndDispatch order=false copy goroutine exiting." )
165- return
166- }
167- }
168- }
169- }
170- }()
139+ ackChan := make (chan * PacketAndToken ) // Channel returned to caller; closed when goroutine terminates
140+
141+ // In some cases message acknowledgments may come through after shutdown (connection is down etc). Where this is the
142+ // case we need to accept any such requests and then ignore them. Note that this is not a perfect solution, if we
143+ // have reconnected, and the session is still live, then the Ack really should be sent (see Issus #726)
144+ var ackMutex sync.RWMutex
145+ sendAckChan := ackChan // This will be set to nil before ackChan is closed
146+ sendAck := func (ack * PacketAndToken ) {
147+ ackMutex .RLock ()
148+ defer ackMutex .RUnlock ()
149+ if sendAckChan != nil {
150+ sendAckChan <- ack
151+ } else {
152+ DEBUG .Println (ROU , "matchAndDispatch received acknowledgment after processing stopped (ACK dropped)." )
153+ }
171154 }
172155
173156 go func () { // Main go routine handling inbound messages
@@ -176,20 +159,18 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
176159 // DEBUG.Println(ROU, "matchAndDispatch received message")
177160 sent := false
178161 r .RLock ()
179- m := messageFromPublish (message , ackFunc (ackInChan , client .persist , message ))
162+ m := messageFromPublish (message , ackFunc (sendAck , client .persist , message ))
180163 for e := r .routes .Front (); e != nil ; e = e .Next () {
181164 if e .Value .(* route ).match (message .TopicName ) {
182165 if order {
183166 handlers = append (handlers , e .Value .(* route ).callback )
184167 } else {
185168 hd := e .Value .(* route ).callback
186- wg .Add (1 )
187169 go func () {
188170 hd (client , m )
189171 if ! client .options .AutoAckDisabled {
190172 m .Ack ()
191173 }
192- wg .Done ()
193174 }()
194175 }
195176 sent = true
@@ -200,13 +181,11 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
200181 if order {
201182 handlers = append (handlers , r .defaultHandler )
202183 } else {
203- wg .Add (1 )
204184 go func () {
205185 r .defaultHandler (client , m )
206186 if ! client .options .AutoAckDisabled {
207187 m .Ack ()
208188 }
209- wg .Done ()
210189 }()
211190 }
212191 } else {
@@ -225,18 +204,11 @@ func (r *router) matchAndDispatch(messages <-chan *packets.PublishPacket, order
225204 }
226205 // DEBUG.Println(ROU, "matchAndDispatch handled message")
227206 }
228- if order {
229- close (ackOutChan )
230- } else { // Ensure that nothing further will be written to ackOutChan before closing it
231- close (stopAckCopy )
232- <- ackCopyStopped
233- close (ackOutChan )
234- go func () {
235- wg .Wait () // Note: If this remains running then the user has handlers that are not returning
236- close (goRoutinesDone )
237- }()
238- }
207+ ackMutex .Lock ()
208+ sendAckChan = nil
209+ ackMutex .Unlock ()
210+ close (ackChan ) // as sendAckChan is now nil nothing further will be sent on this
239211 DEBUG .Println (ROU , "matchAndDispatch exiting" )
240212 }()
241- return ackOutChan
213+ return ackChan
242214}
0 commit comments