/* Настоящий WinRT mailbox без радио: исключения, изоляция link и отмена поздних callbacks. * Включаем backend для доступа к внутренним событиям; общий transport заменён наблюдателем. */ #include "../ble_winrt.cpp" #include #define CHECK(value) do { if (!(value)) { DEBUG_ERROR(CAT, "CHECK failed line=%d: %s", __LINE__, #value); std::abort(); } } while (0) struct ble_transport { WinState* state = nullptr; unsigned radio_failures = 0, link_failures = 0; ble_link_id closed = 0; std::string reason; }; extern "C" void ble_backend_closed(ble_transport* t, ble_link_id id, const char* reason) { ++t->link_failures; t->closed = id; t->reason = reason; t->state->close(id); } extern "C" void ble_backend_status(ble_transport* t, int ready, const char* reason) { CHECK(!ready); ++t->radio_failures; t->reason = reason; while (!t->state->links.empty()) t->state->close(t->state->links.begin()->first); } /* Эти события не должны возникать в проверке mailbox. */ extern "C" ble_link_id ble_backend_incoming(ble_transport*, const ble_endpoint*) { CHECK(false); return 0; } extern "C" void ble_backend_advertising(ble_transport*, ble_state, const char*) { CHECK(false); } extern "C" void ble_backend_broadcast(ble_transport*, const ble_endpoint*, const uint8_t*, size_t, int) { CHECK(false); } extern "C" void ble_backend_opened(ble_transport*, ble_link_id, unsigned) { CHECK(false); } extern "C" void ble_backend_received(ble_transport*, ble_link_id, ble_wire_channel, const uint8_t*, size_t) { CHECK(false); } extern "C" void ble_backend_sent(ble_transport*, ble_link_id, int) { CHECK(false); } static std::shared_ptr make_state(UASYNC* ua, ble_transport& owner) { auto s = std::make_shared(); s->ua = ua; s->owner = &owner; owner.state = s.get(); for (ble_link_id id : {7, 9}) { auto l = std::make_unique(); l->id = id; s->links.emplace(id, std::move(l)); } return s; } /* Ошибка GATT одного пира не переводит radio в failed и не теряет событие другого пира. */ static void link_exception(UASYNC* ua) { ble_transport owner; auto s = make_state(ua, owner); bool other_processed = false; s->post("test.ValueChanged", 7, [](WinState& state) { state.step("test.CharacteristicValue", 7); throw hresult_error(E_NOINTERFACE, L"test interface absent"); }); s->post("test.other_peer", 9, [&other_processed](WinState&) { other_processed = true; }); uasync_poll(ua, 0); CHECK(owner.link_failures == 1 && owner.closed == 7 && owner.radio_failures == 0); CHECK(!s->failed && !s->link(7) && s->link(9) && other_processed); CHECK(owner.reason.find("phase=test.CharacteristicValue link=7 HRESULT=0x80004002") != std::string::npos); s->shutdown(); DEBUG_INFO(CAT, "WinRT test PASS: link exception preserves other peer and exact HRESULT context"); } /* Общая ошибка сохраняет точную операцию даже при выключенном TRACE и останавливает batch. */ static void radio_exception(UASYNC* ua) { ble_transport owner; auto s = make_state(ua, owner); bool processed = false; s->post("test.Received", 0, [](WinState& state) { state.step("test.BluetoothAddressType"); throw hresult_error(E_NOINTERFACE, L"test interface absent"); }); s->post("test.after_failure", 9, [&processed](WinState&) { processed = true; }); uasync_poll(ua, 0); CHECK(owner.radio_failures == 1 && owner.link_failures == 0 && s->failed); CHECK(s->links.empty() && !processed); CHECK(owner.reason.find("phase=test.BluetoothAddressType link=0 HRESULT=0x80004002") != std::string::npos); s->shutdown(); DEBUG_INFO(CAT, "WinRT test PASS: radio exception stops queued delivery with TRACE disabled"); } /* shutdown отменяет posted_task, освобождает captures и отвергает позднее событие Windows. */ static void pending_shutdown(UASYNC* ua) { ble_transport owner; auto s = make_state(ua, owner); auto payload = std::make_shared(0); std::weak_ptr weak_payload = payload; bool processed = false; s->post("test.pending", 9, [payload, &processed](WinState&) { processed = true; }); payload.reset(); CHECK(!weak_payload.expired()); s->shutdown(); CHECK(weak_payload.expired()); s->post("test.late", 9, [&processed](WinState&) { processed = true; }); CHECK(s->events.empty() && !s->posted); std::weak_ptr weak = s; s.reset(); uasync_poll(ua, 0); CHECK(weak.expired() && !processed && owner.radio_failures == 0 && owner.link_failures == 0); DEBUG_INFO(CAT, "WinRT test PASS: shutdown cancels queued work and rejects late callbacks"); } int main() { debug_config_init(); debug_set_level(DEBUG_LEVEL_INFO); init_apartment(apartment_type::multi_threaded); UASYNC* ua = uasync_create(); CHECK(ua); link_exception(ua); radio_exception(ua); pending_shutdown(ua); uasync_destroy(ua, 0); uninit_apartment(); return 0; }