diff --git a/whtree/modules/system/whlibs/internal/jobs.whlib b/whtree/modules/system/whlibs/internal/jobs.whlib index 9b7dc098ad..2060ff0521 100644 --- a/whtree/modules/system/whlibs/internal/jobs.whlib +++ b/whtree/modules/system/whlibs/internal/jobs.whlib @@ -90,7 +90,17 @@ OBJECTTYPE MessageWaiter EXTEND __HS_INTERNAL_AsyncCallbackWaitHelperBase UPDATE BOOLEAN FUNCTION HandleCallback(INTEGER handle, RECORD context) { - RECORD rec := this->link->ReceiveMessage(DEFAULT DATETIME); + RECORD rec; + TRY + rec := this->link->ReceiveMessage(DEFAULT DATETIME); + CATCH(OBJECT e) + { + // autothrow and closed-link errors belong to the pending promise, not + // the unrelated caller pumping the event loop that delivered this read. + this->link->asyncmessagewaiter := DEFAULT OBJECT; + this->defer.reject(e); + RETURN TRUE; + } IF (rec.status != "timeout" OR handle = -1) { IF (this->replyto != 0 AND rec.status = "ok" AND rec.replyto != this->replyto) diff --git a/whtree/modules/webhare_testsuite/tests/system/nodejs/test_async_ipc.whscr b/whtree/modules/webhare_testsuite/tests/system/nodejs/test_async_ipc.whscr new file mode 100644 index 0000000000..99c34b2a21 --- /dev/null +++ b/whtree/modules/webhare_testsuite/tests/system/nodejs/test_async_ipc.whscr @@ -0,0 +1,46 @@ +Accept(DEFAULT DATETIME); + client->autothrow := TRUE; + TRY + { + OBJECT request := client->AsyncDoRequest([value := 1]); + RECORD incoming := server->ReceiveMessage(MAX_DATETIME); + server->SendExceptionReply(NEW Exception("synthetic IPC rejection"), incoming.msgid); + VARIANT settled := AWAIT CreatePromiseAllSettled(OBJECT[request, CreateResolvedPromise(42)]); + TestEq("rejected", settled[0].status); + TestEq("synthetic IPC rejection", settled[0].reason->what); + TestEq("fulfilled", settled[1].status); + TestEq(42, settled[1].value); + + // The failed read must release its registration so this link is reusable. + request := client->AsyncDoRequest([value := 2]); + incoming := server->ReceiveMessage(MAX_DATETIME); + server->SendReply([value := 2], incoming.msgid); + RECORD reply := AWAIT request; + TestEq(2, reply.msg.value); + + request := client->AsyncReceiveMessage(MAX_DATETIME); + server->SendExceptionReply(NEW Exception("synthetic async receive rejection"), 0); + settled := AWAIT CreatePromiseAllSettled(OBJECT[request]); + TestEq("rejected", settled[0].status); + TestEq("synthetic async receive rejection", settled[0].reason->what); + } + FINALLY + { + client->Close(); + server->Close(); + port->Close(); + } +} + +RunTestframework([PTR TestAsyncIPCRejectsAutothrownExceptions]); diff --git a/whtree/modules/webhare_testsuite/tests/system/nodejs/testinfo.xml b/whtree/modules/webhare_testsuite/tests/system/nodejs/testinfo.xml index 24954b9f26..22a071e591 100644 --- a/whtree/modules/webhare_testsuite/tests/system/nodejs/testinfo.xml +++ b/whtree/modules/webhare_testsuite/tests/system/nodejs/testinfo.xml @@ -45,6 +45,7 @@ +