Skip to content

EC client: defer modal popups out of OnPacketReceived call stack - #760

Merged
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:fix/ec-handler-defer-modal
May 28, 2026
Merged

EC client: defer modal popups out of OnPacketReceived call stack#760
mrjimenez merged 1 commit into
amule-project:masterfrom
got3nks:fix/ec-handler-defer-modal

Conversation

@got3nks

@got3nks got3nks commented May 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Companion to #758. CAddLinkHandler::HandlePacket and CCatHandler::HandlePacket popped wxMessageBox synchronously inside the EC packet dispatch chain. The modal spins a nested wx event loop while it's up, which dispatches CoreNotify_LibSocket* events that re-enter CECSocket::OnInput on the same socket. The outer OnInput still owns m_curr_rx_data / m_bytes_needed / m_in_header; when it unwinds and resets those, the rx state machine is left mid-frame and the next read mis-parses a header into one of the protocol-error CloseSocket paths in ReadHeader / ReadPacket.

This is the most likely mechanism behind the link-add half of #757 (the "wedge" half is fixed in #758). The report: pasted batch of 12 links with 6 duplicates, amulegui pops the aggregated error popup, amuled logs the per-link adding link '...' lines, then immediately logs External connection closed. Failure is volume-sensitive: with an idle peer the modal usually races through cleanly, but a heavy-shareset seedbox feeding INC_UPDATE notifications between the EC_OP_FAILED reply and the modal dismissal trips the race much more reliably.

Fix

Route both modals through wxTheApp->CallAfter so the dialog opens after OnInput unwinds. The packet contents are extracted into local wxStrings before the lambda capture, so the deferred callback doesn't reference the borrowed packet pointer.

CCatHandler previously did cs->path = ... and UpdateCategory / downloadlistctrl->Refresh() after its modal returned; those now run synchronously and the user sees the updates committed before the deferred dialog appears, which is consistent with what the "keeping directory '%s'" message already told the user.

Test plan

CAddLinkHandler::HandlePacket and CCatHandler::HandlePacket popped
wxMessageBox synchronously inside the EC dispatch chain:

  HandleRead -> CECSocket::OnInput -> ReadPacket -> OnPacketReceived
    -> handler->HandlePacket -> wxMessageBox(modal)

The modal spins a nested wx event loop. That loop dispatches
CoreNotify_LibSocket* events, re-entering CECSocket::OnInput on
the same socket while the outer call still owns m_curr_rx_data /
m_bytes_needed / m_in_header. When the outer unwinds and resets
those, the rx state machine is left mid-frame; the next read
mis-parses a header and trips a protocol-error CloseSocket.

Failure is volume-sensitive: an idle client usually races through
the modal cleanly. A heavy shareset feeding INC_UPDATE traffic
between the EC_OP_FAILED reply and the modal dismissal makes the
race much more likely -- matching the Stoatwblr seedbox report in
amule-project#757 where a batched ADD_LINK with half-duplicates triggered
"External connection closed" on amuled right after the per-link
log lines.

Route both popups through wxTheApp->CallAfter so the dialog opens
after OnInput unwinds. CCatHandler's existing post-modal data
updates (path overwrite + downloadlistctrl refresh) now run before
the deferred dialog appears, which matches what the "keeping
directory '%s'" message already advertised.
@got3nks got3nks mentioned this pull request May 28, 2026
got3nks added a commit to got3nks/amule that referenced this pull request May 28, 2026
… error

ReadHeader / ReadPacket call CloseSocket() on six different
protocol-error paths (oversize header, unauthorized resize, bad
packet flags, zlib init/free, ReadFromSocket failure). On amulegui
that route looks like:

  asio HandleRead -> CECSocket::OnInput -> ReadPacket
    -> CloseSocket() -> CAsioSocketImpl::Close()

CAsioSocketImpl::Close sets m_closed = true *before* the asio close,
so when HandleRead later fires with operation_aborted the
PostLostEvent gate at LibSocketAsio.cpp:695 is closed and OnLost
never bubbles up. The wrapper-OnLost dispatch fix landed in this
branch only covered the kernel-FIN leg; locally-initiated aborts
sit in the same wedge state amule-project#757 was about.

Stoatwblr's seedbox repro caught exactly this: the link-add
re-entrancy in amule-project#757/amule-project#760 corrupts the rx state machine, ReadPacket
logs "ReadPacket: error in packet read", we CloseSocket ourselves,
amuled sees the FIN and logs "External connection closed", and
amulegui sits silent on stale data because OnLost was suppressed.

Add CECSocket::CloseAndDispatchLost() (inline in ECSocket.h) that
follows InternalClose with a virtual OnLost call, and route all six
ReadHeader / ReadPacket protocol-error sites through it. The new
helper bypasses the m_closed gate by dispatching OnLost on the EC
layer directly -- the same shape DispatchSyncLost already uses for
sync clients. ProcessAuthPacket keeps using plain CloseSocket since
it already fires wxEVT_EC_CONNECTION itself.

The double-fire concern (asio's later operation_aborted hitting
PostLostEvent again) doesn't materialise: by the time the asio
HandleRead fires after our InternalClose, m_closed is true and
PostLostEvent skips -- the same gate that was suppressing the
useful first dispatch is exactly what suppresses the redundant
second one.

amuled side gets the same fix for free: CECServerSocket::OnLost
now runs on protocol-error closes too, so the m_ec_notifier
reference and per-client state are cleaned up immediately instead
of leaking until the connection times out.
mrjimenez pushed a commit that referenced this pull request May 28, 2026
… error

ReadHeader / ReadPacket call CloseSocket() on six different
protocol-error paths (oversize header, unauthorized resize, bad
packet flags, zlib init/free, ReadFromSocket failure). On amulegui
that route looks like:

  asio HandleRead -> CECSocket::OnInput -> ReadPacket
    -> CloseSocket() -> CAsioSocketImpl::Close()

CAsioSocketImpl::Close sets m_closed = true *before* the asio close,
so when HandleRead later fires with operation_aborted the
PostLostEvent gate at LibSocketAsio.cpp:695 is closed and OnLost
never bubbles up. The wrapper-OnLost dispatch fix landed in this
branch only covered the kernel-FIN leg; locally-initiated aborts
sit in the same wedge state #757 was about.

Stoatwblr's seedbox repro caught exactly this: the link-add
re-entrancy in #757/#760 corrupts the rx state machine, ReadPacket
logs "ReadPacket: error in packet read", we CloseSocket ourselves,
amuled sees the FIN and logs "External connection closed", and
amulegui sits silent on stale data because OnLost was suppressed.

Add CECSocket::CloseAndDispatchLost() (inline in ECSocket.h) that
follows InternalClose with a virtual OnLost call, and route all six
ReadHeader / ReadPacket protocol-error sites through it. The new
helper bypasses the m_closed gate by dispatching OnLost on the EC
layer directly -- the same shape DispatchSyncLost already uses for
sync clients. ProcessAuthPacket keeps using plain CloseSocket since
it already fires wxEVT_EC_CONNECTION itself.

The double-fire concern (asio's later operation_aborted hitting
PostLostEvent again) doesn't materialise: by the time the asio
HandleRead fires after our InternalClose, m_closed is true and
PostLostEvent skips -- the same gate that was suppressing the
useful first dispatch is exactly what suppresses the redundant
second one.

amuled side gets the same fix for free: CECServerSocket::OnLost
now runs on protocol-error closes too, so the m_ec_notifier
reference and per-client state are cleaned up immediately instead
of leaking until the connection times out.
@mrjimenez
mrjimenez merged commit 345b3be into amule-project:master May 28, 2026
7 checks passed
@got3nks
got3nks deleted the fix/ec-handler-defer-modal branch June 3, 2026 14:16
got3nks added a commit to got3nks/amule that referenced this pull request Jun 4, 2026
…ndex

Adds 55+ merged PRs to the 3.0.0 changelog since the last update
(amule-project#747, 2026-05-27). Narrative additions cover:

- Packaging: expanded the top list to include the macOS per-arch .app
  bundles and the Windows NSIS installer alongside the existing
  AppImage / Flatpak / .dmg / .zip entries. New bullets for amule-project#785
  (alc/alcc/cas/wxcas everywhere + Windows amuleweb), amule-project#794 (.dmg
  amuleweb path), amule-project#789 (<OS>-<arch> artifact naming), amule-project#780 / amule-project#796
  (Windows DPI + comctl32 manifest), amule-project#784 (FHS share/amule paths).

- Bug Fixes & Stability: post-amule-project#744 fixes including EC notification
  leak (amule-project#797), big-library scaling (amule-project#736, amule-project#840 superseding amule-project#728),
  amulegui ghost entries (amule-project#810, amule-project#819, amule-project#841, amule-project#824, amule-project#830, amule-project#760),
  PartFile early hash (amule-project#762), server protocol fixes (amule-project#835, amule-project#788,
  amule-project#721, amule-project#787), crypto stream UB (amule-project#779), UAF prevention (amule-project#756),
  Kad rotation (amule-project#795, amule-project#799/amule-project#805), GTK warning silencing (amule-project#833,
  amule-project#826/amule-project#836), and the clang-tidy worklist (amule-project#770, amule-project#772-amule-project#774).

- Translations: late-cycle wave covering French/Turkish manpages
  (amule-project#753/amule-project#754/amule-project#776), Galician (amule-project#763), Slovenian (amule-project#771), pt-BR
  (amule-project#768/amule-project#775/amule-project#812), French (amule-project#811), plus man-page tooling for
  date+version drift (amule-project#802).

- Contributors: added ngosang for UX feedback on the late-3.0
  cycle (amule-project#817/amule-project#818/amule-project#821/amule-project#828/amule-project#844) and ongoing work on the
  user-facing manual at amule-org.github.io.

- Merged PRs flat index: extended with amule-project#746-amule-project#845 + amule-project#841.
got3nks added a commit to got3nks/amule that referenced this pull request Jun 4, 2026
…ndex

Adds 55+ merged PRs to the 3.0.0 changelog since the last update
(amule-project#747, 2026-05-27). Narrative additions cover:

- Packaging: expanded the top list to include the macOS per-arch .app
  bundles and the Windows NSIS installer alongside the existing
  AppImage / Flatpak / .dmg / .zip entries. New bullets for amule-project#785
  (alc/alcc/cas/wxcas everywhere + Windows amuleweb), amule-project#794 (.dmg
  amuleweb path), amule-project#789 (<OS>-<arch> artifact naming), amule-project#780 / amule-project#796
  (Windows DPI + comctl32 manifest), amule-project#784 (FHS share/amule paths).

- Bug Fixes & Stability: post-amule-project#744 fixes including EC notification
  leak (amule-project#797), big-library scaling (amule-project#736, amule-project#840 superseding amule-project#728),
  amulegui ghost entries (amule-project#810, amule-project#819, amule-project#841, amule-project#824, amule-project#830, amule-project#760),
  PartFile early hash (amule-project#762), server protocol fixes (amule-project#835, amule-project#788,
  amule-project#721, amule-project#787), crypto stream UB (amule-project#779), UAF prevention (amule-project#756),
  Kad rotation (amule-project#795, amule-project#799/amule-project#805), GTK warning silencing (amule-project#833,
  amule-project#826/amule-project#836), and the clang-tidy worklist (amule-project#770, amule-project#772-amule-project#774).

- Translations: late-cycle wave covering French/Turkish manpages
  (amule-project#753/amule-project#754/amule-project#776), Galician (amule-project#763), Slovenian (amule-project#771), pt-BR
  (amule-project#768/amule-project#775/amule-project#812), French (amule-project#811), plus man-page tooling for
  date+version drift (amule-project#802).

- Contributors: added ngosang for UX feedback on the late-3.0
  cycle (amule-project#817/amule-project#818/amule-project#821/amule-project#828/amule-project#844) and ongoing work on the
  user-facing manual at amule-org.github.io.

- Merged PRs flat index: extended with amule-project#746-amule-project#845 + amule-project#841.
mrjimenez pushed a commit that referenced this pull request Jun 4, 2026
Adds 55+ merged PRs to the 3.0.0 changelog since the last update
(#747, 2026-05-27). Narrative additions cover:

- Packaging: expanded the top list to include the macOS per-arch .app
  bundles and the Windows NSIS installer alongside the existing
  AppImage / Flatpak / .dmg / .zip entries. New bullets for #785
  (alc/alcc/cas/wxcas everywhere + Windows amuleweb), #794 (.dmg
  amuleweb path), #789 (<OS>-<arch> artifact naming), #780 / #796
  (Windows DPI + comctl32 manifest), #784 (FHS share/amule paths).

- Bug Fixes & Stability: post-#744 fixes including EC notification
  leak (#797), big-library scaling (#736, #840 superseding #728),
  amulegui ghost entries (#810, #819, #841, #824, #830, #760),
  PartFile early hash (#762), server protocol fixes (#835, #788,
  #721, #787), crypto stream UB (#779), UAF prevention (#756),
  Kad rotation (#795, #799/#805), GTK warning silencing (#833,
  #826/#836), and the clang-tidy worklist (#770, #772-#774).

- Translations: late-cycle wave covering French/Turkish manpages
  (#753/#754/#776), Galician (#763), Slovenian (#771), pt-BR
  (#768/#775/#812), French (#811), plus man-page tooling for
  date+version drift (#802).

- Contributors: added ngosang for UX feedback on the late-3.0
  cycle (#817/#818/#821/#828/#844) and ongoing work on the
  user-facing manual at amule-org.github.io.

- Merged PRs flat index: extended with #746-#845 + #841.
mrjimenez pushed a commit to mrjimenez/amule that referenced this pull request Aug 4, 2026
…ect#760)

Startup is slow and silent on a large share. The main window was created
before the local work ran, but that work holds the main thread, so nothing
painted until it finished: a rendered window that ignores every click, long
enough that the OS offers to force-quit it. On a first run the wait extends
well past the scan, into the hashing of everything the scan found unknown.

Show a splash for the whole of it, and hold the main window back until it
closes, so the window appears ready rather than appearing dead.

The bar is driven by real totals rather than a spinner: part-file counts are
exact, and the shared scan is sized against known.met, last session's view of
the same tree, since counting first would walk it twice -- expensive exactly
where it hurts, on network storage. With no known.met it is a first run, so
the scan holds its band and the hashing phase spends it: that is where the
time actually goes. Band weights come from measurements on a 10 000-file
share (network 10 ms, 400 part files 130 ms, scan 6010 ms), and the phase
timings stay in the log at normal level so a "startup is slow" report carries
the numbers with it.

Both list controls are batched for the duration. Each individually-sorted
insert rebuilds the row index, so a burst of thousands is quadratic; this is
the same BeginBatchUpdate the remote GUI uses for its startup EC reply.

The splash is monolithic-only. amulegui's startup is an EC round trip with no
local scan to wait on, and the daemon has no GUI: SplashScreen.cpp is kept out
of its source list, and the app-side members and phases are compiled out.

The backdrop is drawn rather than shipped -- a gradient plus the icon already
embedded in the art provider -- so no new file enters the bundle.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants