So additional testing with closing handle without any thread termination showed that actually missing bit here has nothing to do with thread termination. It is just closing the last handle in a process triggers async cancellation, but that happens under specific conditions only and those conditions are reverse to the conditions which lead to not canceling async when thread that queued it exits. That's why I missed that in my earlier testing, I didn't reproduce those conditions when closing handle, but now when I tried integrating that into the test which has all the combinations of related async options it became clear.
In fact, it makes more sense. If the thread which queued async terminated and all the object handles in process are closed, the async is guaranteed to be canceled. Depending on apc_context, event and completion port presence that happens either during thread exit or in close_handle.
I also tested that with named pipes and those seem to behave the same. I opened a new !3339 instead of this one.