This is an automated email from the ASF dual-hosted git repository.

sjanc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-newtmgr.git

commit 2efc393f60de36866db3d05a9bc1cccc7b08b057
Author: Szymon Janc <[email protected]>
AuthorDate: Thu Oct 22 00:25:44 2026 +0200

    nmxact/omp: Make Stop wait for CoAP listener removal
    
    Each OMP listener goroutine deferred RemoveCoapListener() before
    wg.Done(). Deferred calls run in reverse order, so wg.Done() ran first
    and Stop() could return while the CoAP listeners were still
    registered. Messages received right after Stop() were then still
    dispatched to listeners nobody reads anymore, contrary to the intent
    of Stop() waiting for the listener goroutines.
    
    Swap the defers so the CoAP listener is removed before the goroutine
    signals completion.
    
    Assisted-by: Claude:claude-opus-5-5
---
 nmxact/omp/dispatch.go      |  4 +++-
 nmxact/omp/dispatch_test.go | 27 +++++++++++++++++++++++++++
 2 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/nmxact/omp/dispatch.go b/nmxact/omp/dispatch.go
index 62d0be7..9041bd2 100644
--- a/nmxact/omp/dispatch.go
+++ b/nmxact/omp/dispatch.go
@@ -88,8 +88,10 @@ func (d *Dispatcher) addOmpListener(seq uint8) (*Listener, 
error) {
 
        d.wg.Add(1)
        go func() {
-               defer d.RemoveCoapListener(mc)
+               // Deferred calls run in reverse order: remove the CoAP listener
+               // before signalling Stop() that this goroutine is done.
                defer d.wg.Done()
+               defer d.RemoveCoapListener(mc)
 
                // Listen for events.  All feedback is sent to the client via 
the NMP
                // listener channels.  It is done this way so that client code 
can be
diff --git a/nmxact/omp/dispatch_test.go b/nmxact/omp/dispatch_test.go
index 85d819c..97e7e75 100644
--- a/nmxact/omp/dispatch_test.go
+++ b/nmxact/omp/dispatch_test.go
@@ -363,3 +363,30 @@ func TestDispatcherCoapListener(t *testing.T) {
                t.Errorf("RemoveCoapListener returned %p, want %p", got, l)
        }
 }
+
+// When Stop returns, the CoAP listeners owned by the OMP listeners must be
+// gone.  The window is small, so repeat a number of times.
+func TestDispatcherStopRemovesCoapListeners(t *testing.T) {
+       for i := 0; i < 200; i++ {
+               d, err := NewDispatcher(nil, false, 0)
+               if err != nil {
+                       t.Fatal(err)
+               }
+
+               for seq := uint8(30); seq < 34; seq++ {
+                       if _, err := d.AddNmpListener(seq); err != nil {
+                               t.Fatal(err)
+                       }
+               }
+
+               stopWithTimeout(t, d)
+
+               for seq := uint8(30); seq < 34; seq++ {
+                       b := encodeTestOmpRsp(t, coap.Content, seq, "x")
+                       if d.Dispatch(b) {
+                               t.Fatalf("iteration %d: Dispatch for seq %d 
after Stop "+
+                                       "returned true", i, seq)
+                       }
+               }
+       }
+}

Reply via email to