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 eb4fb87e9bc21383c95f8c94e16ccbd4cbba48de
Author: Szymon Janc <[email protected]>
AuthorDate: Thu Oct 22 01:00:17 2026 +0200

    nmxact/xact: Default image upload window size
    
    NewImageUploadCmd() and NewImageUpgradeCmd() leave MaxWinSz at zero,
    which ImageUploadCmd.Run() uses as the capacity of the response and
    error channels.  With sessions which deliver the response from within
    TxRxMgmtAsync() (serial, naked BLE), the send on the unbuffered channel
    blocks before any receiver exists and the upload deadlocks on the
    first chunk.  The CLI is not affected as it always sets MaxWinSz, but
    library users relying on the constructors hang.
    
    Treat a MaxWinSz of zero or less as IMAGE_UPLOAD_DEF_MAX_WS, the same
    default the CLI uses.
    
    Assisted-by: Claude:claude-opus-5-5
---
 nmxact/xact/image.go             |  7 +++++++
 nmxact/xact/image_upload_test.go | 26 ++++++++++++++++++++++++++
 2 files changed, 33 insertions(+)

diff --git a/nmxact/xact/image.go b/nmxact/xact/image.go
index 7548074..f164ef6 100644
--- a/nmxact/xact/image.go
+++ b/nmxact/xact/image.go
@@ -340,6 +340,13 @@ func (t *ImageUploadIntTracker) HandleError(off int, err 
error) bool {
 }
 
 func (c *ImageUploadCmd) Run(s sesn.Sesn) (Result, error) {
+       // The window size is also the capacity of the response channels.
+       // Unbuffered channels deadlock with sessions which deliver the
+       // response before TxRxMgmtAsync() returns.
+       if c.MaxWinSz <= 0 {
+               c.MaxWinSz = IMAGE_UPLOAD_DEF_MAX_WS
+       }
+
        res := newImageUploadResult()
 
        // Signalled (without blocking) whenever a response or error has been
diff --git a/nmxact/xact/image_upload_test.go b/nmxact/xact/image_upload_test.go
index 39e76fc..9918602 100644
--- a/nmxact/xact/image_upload_test.go
+++ b/nmxact/xact/image_upload_test.go
@@ -598,3 +598,29 @@ func TestImageUpgradeRunDeviceError(t *testing.T) {
                t.Errorf("status %d", st)
        }
 }
+
+// Commands created by the constructors must work without setting
+// MaxWinSz explicitly.
+func TestImageUploadRunDefaultWindow(t *testing.T) {
+       data := testImage(3000)
+
+       dev := &fakeImgDev{}
+       s := newFakeSesn(dev.handle)
+       uc := NewImageUploadCmd()
+       uc.Data = data
+       if _, err := runCmd(t, uc, s); err != nil {
+               t.Fatal(err)
+       }
+       checkUploaded(t, dev, data)
+
+       udev := &imgUpgradeDev{}
+       s = newFakeSesn(udev.handle)
+       udev.s = s
+       gc := NewImageUpgradeCmd()
+       gc.Data = data
+       gc.ProgressCb = func(*ImageUploadCmd, *nmp.ImageUploadRsp) {}
+       if _, err := runCmd(t, gc, s); err != nil {
+               t.Fatal(err)
+       }
+       checkUploaded(t, &udev.fakeImgDev, data)
+}

Reply via email to