1
0
Fork 0
mirror of https://gitee.com/fantix/kloop.git synced 2024-11-22 10:21:25 +00:00

Extract TCP connect

Fixes #I5ANZE, but not in nogil for simplicity for now
This commit is contained in:
Fantix King 2022-06-18 20:19:33 -04:00
parent db84313627
commit 47636fbf04
No known key found for this signature in database
GPG key ID: 95304B04071CCDB4
5 changed files with 64 additions and 76 deletions

View file

@ -43,6 +43,7 @@ cdef class KLoopImpl:
bint closed bint closed
object thread_id object thread_id
Loop loop Loop loop
Resolver resolver
cpdef create_future(self) cpdef create_future(self)
cdef inline check_closed(self) cdef inline check_closed(self)

View file

@ -514,24 +514,11 @@ cdef class KLoopImpl:
interleave=None, interleave=None,
): ):
cdef: cdef:
TCPTransport transport int fd
Resolve resolve
object waiter
size_t i
resolve = await self.resolver.lookup_ip(host, port) fd = await tcp_connect(self, host, port)
if not resolve.r.result_len: protocol = protocol_factory()
raise RuntimeError(f"Cannot resolve host: {host!r}") return TCPTransport.new(fd, protocol, self), protocol
transport = TCPTransport.new(protocol_factory, self)
exceptions = []
for i in range(resolve.r.result_len):
try:
waiter = transport.connect(resolve.r.result + i)
return transport, await waiter
except OSError as e:
exceptions.append(e)
raise exceptions[0]
class KLoop(KLoopImpl, asyncio.AbstractEventLoop): class KLoop(KLoopImpl, asyncio.AbstractEventLoop):

View file

@ -116,6 +116,9 @@ cdef class Resolver:
cdef init_cb(self): cdef init_cb(self):
cdef int res = self.resolver.res cdef int res = self.resolver.res
if self.waiter.done():
return
if res < 0: if res < 0:
try: try:
errno.errno = -res errno.errno = -res
@ -128,6 +131,7 @@ cdef class Resolver:
cdef err_cb(self, exc): cdef err_cb(self, exc):
waiter, self.waiter = self.waiter, None waiter, self.waiter = self.waiter, None
if waiter is not None: if waiter is not None:
if not waiter.done():
waiter.set_exception(exc) waiter.set_exception(exc)
async def lookup_ip(self, host, port): async def lookup_ip(self, host, port):

View file

@ -10,8 +10,6 @@
cdef struct TCPConnect: cdef struct TCPConnect:
int fd
libc.sockaddr* addr
RingCallback ring_cb RingCallback ring_cb
Loop* loop Loop* loop
Callback* cb Callback* cb
@ -21,13 +19,7 @@ cdef class TCPTransport:
cdef: cdef:
KLoopImpl loop KLoopImpl loop
int fd int fd
TCPConnect connector object protocol
object waiter
object protocol_factory
Handle handle
@staticmethod @staticmethod
cdef TCPTransport new(object protocol_factory, KLoopImpl loop) cdef TCPTransport new(int fd, object protocol, KLoopImpl loop)
cdef connect(self, libc.sockaddr* addr)
cdef connect_cb(self)

View file

@ -9,13 +9,54 @@
# See the Mulan PSL v2 for more details. # See the Mulan PSL v2 for more details.
cdef int tcp_connect(TCPConnect* connector) nogil: async def tcp_connect(KLoopImpl loop, host, port):
return ring_sq_submit_connect( cdef:
&connector.loop.ring.sq, Resolve resolve
connector.fd, TCPConnect connector
connector.addr, int fd, res
libc.sockaddr * addr
Handle handle
size_t i
resolve = await loop.resolver.lookup_ip(host, port)
if not resolve.r.result_len:
raise RuntimeError(f"Cannot resolve host: {host!r}")
connector.loop = &loop.loop
connector.ring_cb.callback = tcp_connect_cb
connector.ring_cb.data = &connector
exceptions = []
for i in range(resolve.r.result_len):
addr = resolve.r.result + i
fd = libc.socket(addr.sa_family, libc.SOCK_STREAM, 0)
if fd == -1:
raise IOError("Cannot create socket")
try:
waiter = loop.create_future()
handle = Handle(waiter.set_result, (None,), loop, None)
connector.cb = &handle.cb
if not ring_sq_submit_connect(
&loop.loop.ring.sq,
fd,
addr,
&connector.ring_cb, &connector.ring_cb,
) ):
raise ValueError("Submission queue is full!")
await waiter
res = abs(connector.ring_cb.res)
if res != 0:
raise IOError(res, string.strerror(res))
return fd
except Exception as e:
os.close(fd)
exceptions.append(e)
raise exceptions[0]
cdef int tcp_connect_cb(RingCallback* cb) nogil except 0: cdef int tcp_connect_cb(RingCallback* cb) nogil except 0:
@ -25,50 +66,13 @@ cdef int tcp_connect_cb(RingCallback* cb) nogil except 0:
cdef class TCPTransport: cdef class TCPTransport:
@staticmethod @staticmethod
cdef TCPTransport new(object protocol_factory, KLoopImpl loop): cdef TCPTransport new(int fd, object protocol, KLoopImpl loop):
cdef TCPTransport rv = TCPTransport.__new__(TCPTransport) cdef TCPTransport rv = TCPTransport.__new__(TCPTransport)
rv.protocol_factory = protocol_factory rv.fd = fd
rv.protocol = protocol
rv.loop = loop rv.loop = loop
rv.connector.loop = &loop.loop loop.call_soon(protocol.connection_made, rv)
rv.connector.ring_cb.callback = tcp_connect_cb
rv.connector.ring_cb.data = &rv.connector
return rv return rv
cdef connect(self, libc.sockaddr* addr):
cdef:
int fd
TCPConnect* c = &self.connector
fd = libc.socket(addr.sa_family, libc.SOCK_STREAM, 0)
if fd == -1:
PyErr_SetFromErrno(IOError)
return
c.addr = addr
c.fd = self.fd = fd
self.handle = Handle(self.connect_cb, (self,), self.loop, None)
c.cb = &self.handle.cb
if not tcp_connect(c):
raise ValueError("Submission queue is full!")
self.waiter = self.loop.create_future()
return self.waiter
cdef connect_cb(self):
if self.connector.ring_cb.res != 0:
if not ring_sq_submit_close(
&self.loop.loop.ring.sq, self.fd, NULL
):
# TODO: fd not closed?
pass
try:
errno.errno = abs(self.connector.ring_cb.res)
PyErr_SetFromErrno(IOError)
except IOError as e:
self.waiter.set_exception(e)
return
protocol = self.protocol_factory()
self.waiter.set_result(protocol)
self.loop.call_soon(protocol.connection_made, self)
def get_extra_info(self, x): def get_extra_info(self, x):
return None return None