diff --git a/mercurial/worker.py b/mercurial/worker.py
index 4338f87dbf6fddb38c3ae08d91bf518525fae0b7_bWVyY3VyaWFsL3dvcmtlci5weQ==..86cd09bc13ba66386ad5f8fc0279168b282e5fd5_bWVyY3VyaWFsL3dvcmtlci5weQ== 100644
--- a/mercurial/worker.py
+++ b/mercurial/worker.py
@@ -15,6 +15,7 @@
 from .i18n import _
 from . import (
     error,
+    scmutil,
     util,
 )
 
@@ -132,7 +133,8 @@
         if pid == 0:
             signal.signal(signal.SIGINT, oldhandler)
             signal.signal(signal.SIGCHLD, oldchldhandler)
-            try:
+
+            def workerfunc():
                 os.close(rfd)
                 for i, item in func(*(staticargs + (pargs,))):
                     os.write(wfd, '%d %s\n' % (i, item))
@@ -136,6 +138,11 @@
                 os.close(rfd)
                 for i, item in func(*(staticargs + (pargs,))):
                     os.write(wfd, '%d %s\n' % (i, item))
-                os._exit(0)
+
+            # make sure we use os._exit in all code paths. otherwise the worker
+            # may do some clean-ups which could cause surprises like deadlock.
+            # see sshpeer.cleanup for example.
+            try:
+                scmutil.callcatch(ui, workerfunc)
             except KeyboardInterrupt:
                 os._exit(255)
@@ -140,7 +147,12 @@
             except KeyboardInterrupt:
                 os._exit(255)
-                # other exceptions are allowed to propagate, we rely
-                # on lock.py's pid checks to avoid release callbacks
+            except: # never return, therefore no re-raises
+                try:
+                    ui.traceback()
+                finally:
+                    os._exit(255)
+            else:
+                os._exit(0)
         pids.add(pid)
     os.close(wfd)
     fp = os.fdopen(rfd, 'rb', 0)