Inside that coroutine get the loop and await loop.sock_accept(sock)
And then asyncio.create_task(on_connection_data(connection)).
The only gotcha is you need to keep a reference to that task so it doesn't get garbage collected.
"The default build implementation is a generational collector. The free-threaded build is non-generational; each collection scans the entire heap."
https://devguide.python.org/internals/garbage-collector/
https://docs.python.org/3/library/gc.html
(And as someone else pointed out, asyncio's event loop keeps only weak references to tasks, so the GC implementation doesn't really matter here.)
Consider organizing the code using TaskGroup.
Inside that coroutine get the loop and await loop.sock_accept(sock)
And then asyncio.create_task(on_connection_data(connection)).
The only gotcha is you need to keep a reference to that task so it doesn't get garbage collected.