|
18 | 18 | #include <kj/async.h> |
19 | 19 | #include <kj/async-io.h> |
20 | 20 | #include <kj/common.h> |
| 21 | +#include <kj/exception.h> |
21 | 22 | #include <kj/debug.h> |
22 | 23 | #include <kj/memory.h> |
| 24 | +#include <kj/string.h> |
23 | 25 | #include <kj/test.h> |
24 | 26 | #include <memory> |
25 | 27 | #include <mp/proxy.h> |
@@ -481,5 +483,76 @@ KJ_TEST("Make simultaneous IPC calls on single remote thread") |
481 | 483 | KJ_EXPECT(expected == 400); |
482 | 484 | } |
483 | 485 |
|
| 486 | +KJ_TEST("Call async IPC method dispatched to pool thread") |
| 487 | +{ |
| 488 | + TestSetup setup; |
| 489 | + ProxyClient<messages::FooInterface>* foo = setup.client.get(); |
| 490 | + |
| 491 | + // Set up the thread map exchange so the client has the server's ThreadMap, |
| 492 | + // then call makePool to pre-allocate two server threads. |
| 493 | + foo->initThreadMap(); |
| 494 | + setup.server->m_impl->m_int_fn = [](int n) { return n * 2; }; |
| 495 | + |
| 496 | + ThreadContext& tc{g_thread_context}; |
| 497 | + std::atomic<size_t> running{3}; |
| 498 | + std::promise<void> pool_ready; |
| 499 | + foo->m_context.loop->sync([&] { |
| 500 | + auto pool_req = foo->m_context.connection->m_thread_map.makePoolRequest(); |
| 501 | + pool_req.setName("test"); |
| 502 | + pool_req.setCount(2); |
| 503 | + foo->m_context.loop->m_task_set->add( |
| 504 | + pool_req.send().then([&](auto&&) { pool_ready.set_value(); })); |
| 505 | + }); |
| 506 | + pool_ready.get_future().get(); |
| 507 | + |
| 508 | + // Send three callIntFnAsync requests with no context.thread set. |
| 509 | + // The server should dispatch each to a pool thread. |
| 510 | + auto client{foo->m_client}; |
| 511 | + foo->m_context.loop->sync([&] { |
| 512 | + for (size_t i = 0; i < running; ++i) { |
| 513 | + auto request{client.callIntFnAsyncRequest()}; |
| 514 | + request.initContext(); // context present but thread unset |
| 515 | + request.setArg(static_cast<int32_t>(i + 1)); |
| 516 | + foo->m_context.loop->m_task_set->add(request.send().then( |
| 517 | + [&running, &tc, i](auto&& results) { |
| 518 | + assert(results.getResult() == static_cast<int32_t>((i + 1) * 2)); |
| 519 | + running -= 1; |
| 520 | + tc.waiter->m_cv.notify_all(); |
| 521 | + })); |
| 522 | + } |
| 523 | + }); |
| 524 | + { |
| 525 | + Lock lock(tc.waiter->m_mutex); |
| 526 | + tc.waiter->wait(lock, [&running] { return running == 0; }); |
| 527 | + } |
| 528 | +} |
| 529 | + |
| 530 | +KJ_TEST("Call async IPC method without thread or pool errors correctly") |
| 531 | +{ |
| 532 | + TestSetup setup; |
| 533 | + ProxyClient<messages::FooInterface>* foo = setup.client.get(); |
| 534 | + setup.server->m_impl->m_fn = [] {}; |
| 535 | + |
| 536 | + // Send a callFnAsync request with no context.thread and no pool configured. |
| 537 | + // The server should throw the "no thread specified and no pool configured" error. |
| 538 | + std::promise<void> done; |
| 539 | + bool error_thrown{false}; |
| 540 | + foo->m_context.loop->sync([&] { |
| 541 | + auto request{foo->m_client.callFnAsyncRequest()}; |
| 542 | + request.initContext(); |
| 543 | + foo->m_context.loop->m_task_set->add( |
| 544 | + request.send().then( |
| 545 | + [&](auto&&) { done.set_value(); }, |
| 546 | + [&](kj::Exception&& e) { |
| 547 | + error_thrown = true; |
| 548 | + KJ_EXPECT(std::string_view{e.getDescription().cStr()}.find( |
| 549 | + "no thread specified and no pool configured") != std::string_view::npos); |
| 550 | + done.set_value(); |
| 551 | + })); |
| 552 | + }); |
| 553 | + done.get_future().get(); |
| 554 | + KJ_EXPECT(error_thrown); |
| 555 | +} |
| 556 | + |
484 | 557 | } // namespace test |
485 | 558 | } // namespace mp |
0 commit comments