github tonikelope/megabasterd v8.57
MegaBasterd 8.57

5 hours ago

TL;DR

  • Fixes #778: enabling SmartProxy while the app is already running never resumed quota-stalled downloads. Symptom: the proxy list refreshes fine ([Smart Proxy] proxy list refreshed (N entries)) and then nothing happens — downloads stay frozen — while the only workaround was to fully relaunch with SmartProxy pre-enabled. This affected every proxy type, not just authenticated ones.

  • Root cause: a Java Memory Model data race. The proxy-routing fields in MainPanel (_use_smart_proxy, _proxy_manager, _use_proxy, the static-proxy host/port/user/pass) were plain non-volatile statics with unsynchronized getters. They are read by long-lived download worker threads and written on the Swing EDT when you toggle Settings. When SmartProxy is enabled at startup the workers are created after the write (so Thread.start() supplies the needed happens-before edge); when it is enabled at runtime the workers already exist, so with no volatile there is no happens-before edge — an already-running worker can keep reading the stale value (false / null) forever, or the JIT can hoist the read out of its hot loop. Either way the worker never enters the proxy-routing branch, keeps hitting MEGA direct, and loops 509 → backoff → 509 with zero proxy activity in the log. The #758 "re-read getProxy_manager() every iteration" comment assumed re-reading made runtime toggles visible; under the JMM it does not without volatile.

  • Fix: the proxy-routing statics and _proxy_manager are now volatile — a one-word fix that restores correct visibility for already-running workers. The same race class was also fixed for the post-509 / post-finish external-command statics (_run_command*).

  • Several related hardening fixes shipped alongside (see below): a stream-download NPE, a download-path NPE in checkMegaDownloadUrl, the getProxy() monitor that could freeze the UI, an unbounded per-worker exclusion list, and the manual Refresh-SmartProxy button now resumes stalled workers too.

What was wrong

ChunkDownloader / ChunkDownloaderMono re-read MainPanel.getProxy_manager() and check MainPanel.isUse_smart_proxy() on every loop iteration so that a runtime SmartProxy-enable is picked up. But those statics were not volatile, so a worker thread started before the toggle had no guarantee of ever observing the new values. The visible effect was exactly the bug report: refresh succeeds, the workers never switch to the proxy pool, downloads stay stalled. Relaunching "fixed" it only because the workers were then recreated after the value was already set.

Two more code paths could fail in the reporter's exact configuration (FORCE mode + a large, flaky public proxy pool):

  • StreamChunkDownloader captured the proxy manager once before its loop and never re-read it, and its routing branch did not null-guard it. A stream started with SmartProxy off kept the manager null for life; the first 509 after a runtime enable threw an NPE at proxy_manager.getProxy().

  • MiscTools.checkMegaDownloadUrl (used on the download path by Download.getMegaFileDownloadUrl and by the streamer) indexed getProxy()[0] unconditionally, and its FORCE-mode pick sits outside the method's try, so an exhausted pool threw an uncaught NPE straight into the caller.

What changes

  • MainPanel_use_smart_proxy, _proxy_manager, _use_proxy, _proxy_host, _proxy_port, _proxy_user, _proxy_pass, and _run_command / _run_command_path / _run_command_{dl,ul}_finish[_path] are now volatile.

  • SmartMegaProxyManager — the JDK-wide proxy Authenticator is now installed in the constructor instead of only on the startup path, so a runtime-enable also authenticates IP:PORT@b64user:b64pass proxies. getProxy() no longer holds the manager monitor while it sleeps and refreshes: the selection moved to a synchronized pickProxy() helper, and the Thread.sleep(30 s) × 5 + blocking refreshProxyList() now run without the lock (the old code serialized every worker behind the monitor for minutes on an exhausted pool, and the on-EDT refreshSmartProxySettings() — also de-synchronized now, it never touched the pool — could freeze the UI).

  • ChunkDownloader / ChunkDownloaderMono / MegaAPI / StreamChunkDownloader / MiscTools — the per-worker excluded-proxy list is now cleared when the pool is exhausted, instead of locking the worker into direct → 509 → exp-backoff forever (timeouts/429s only soft-exclude and were previously never cleared except on a successful chunk; hard failures stay banned by ban_time).

  • StreamChunkDownloader — re-reads the proxy manager every iteration and null-guards the routing branch (no more NPE / no-op on runtime enable).

  • MiscTools.checkMegaDownloadUrl — all three getProxy() results are null-guarded.

  • Manual "Refresh SmartProxy" button — now also wakes stalled workers (breaks 509 exp-backoff and the auto-retry countdown), matching the behaviour of enabling SmartProxy in Settings; a freshly refreshed pool is exactly when stalled downloads should retry immediately.

Other

  • Version bumped to 8.57 in pom.xml and MainPanel.VERSION.
  • No changes to the file-data transfer/decrypt path, the resume queue, the account-login flow, the MEGA error-popup machinery, the upload pipeline, or any i18n bundle.
  • Notes left for a future pass (out of scope, low impact): uploads are not routed through SmartProxy; the pre-worker metadata/URL retry loop in Download.run() is not interruptible by a runtime enable; ChunkDownloaderMono does not participate in the shared post-509 recheck window.

Don't miss a new megabasterd release

NewReleases is sending notifications on new releases.