Commit 8cc525ba authored by Xavier Thompson's avatar Xavier Thompson

Implement GIL-free socket API

parent 7ea35116
...@@ -3,7 +3,7 @@ cdef extern from "<sys/types.h>" nogil: ...@@ -3,7 +3,7 @@ cdef extern from "<sys/types.h>" nogil:
cdef extern from "<sys/socket.h>" nogil: cdef extern from "<sys/socket.h>" namespace "" nogil:
ctypedef long socklen_t ctypedef long socklen_t
...@@ -55,6 +55,11 @@ cdef extern from "<sys/socket.h>" nogil: ...@@ -55,6 +55,11 @@ cdef extern from "<sys/socket.h>" nogil:
int socketpair(int, int, int, int [2]) int socketpair(int, int, int, int [2])
cdef extern from "<unistd.h>" namespace "" nogil:
int close(int fd)
cdef extern from "<netdb.h>" nogil: cdef extern from "<netdb.h>" nogil:
......
cimport stdlib._socket as _socket cimport stdlib._socket as _socket
from stdlib._string cimport string, move_string
from libc.string cimport memset, memcpy from libc.errno cimport errno
from libc.string cimport memset, memcpy, strerror
from libc.stdlib cimport malloc, free from libc.stdlib cimport malloc, free
from stdlib.string cimport Str from stdlib.string cimport Str
from libcythonplus.list cimport cyplist from libcythonplus.list cimport cyplist
...@@ -121,7 +123,7 @@ cdef cypclass Addrinfo: ...@@ -121,7 +123,7 @@ cdef cypclass Addrinfo:
self.sockaddr = sockaddr self.sockaddr = sockaddr
cdef inline cyplist[Addrinfo] getaddrinfo(Str host, Str port, int family, int socktype, int protocol, int flags) nogil except NULL: cdef inline cyplist[Addrinfo] getaddrinfo(Str host, Str port, int family=0, int socktype=0, int protocol=0, int flags=0) nogil except NULL:
cdef _socket.addrinfo hints cdef _socket.addrinfo hints
cdef _socket.addrinfo *ai cdef _socket.addrinfo *ai
cdef _socket.addrinfo *p cdef _socket.addrinfo *p
...@@ -152,4 +154,190 @@ cdef inline cyplist[Addrinfo] getaddrinfo(Str host, Str port, int family, int so ...@@ -152,4 +154,190 @@ cdef inline cyplist[Addrinfo] getaddrinfo(Str host, Str port, int family, int so
return result return result
cdef cypclass Socket
cdef inline Socket socket(int family, int socktype, int protocol=0) nogil except NULL:
cdef int sockfd = _socket.socket(family, socktype, protocol)
if sockfd == -1:
with gil:
raise OSError('failed to open socket: ' + strerror(errno).decode())
s = Socket()
s.sockfd = sockfd
s.family = family
s.socktype = socktype
s.protocol = protocol
s.address = NULL
return s
cdef cypclass Socket:
int sockfd
int family
int socktype
int protocol
Sockaddr address
int setsockopt(self, int optname, int value) except -1:
cdef int status
status = _socket.setsockopt(self.sockfd, SOL_SOCKET, optname, &value, sizeof(value))
if status == -1:
with gil:
raise OSError('failed to set socket option: ' + strerror(errno).decode())
return status
int bind(self, Sockaddr address) except -1:
cdef int status
status = _socket.bind(self.sockfd, address.addr, address.addrlen)
if status == -1:
with gil:
raise OSError('failed to bind socket: ' + strerror(errno).decode())
self.address = address
return status
int connect(self, Sockaddr address) except -1:
cdef int status
status = _socket.connect(self.sockfd, address.addr, address.addrlen)
if status == -1:
with gil:
raise OSError('failed to connect socket: ' + strerror(errno).decode())
self.address = address
return status
int listen(self, int backlog) except -1:
cdef int status
status = _socket.listen(self.sockfd, backlog)
if status == -1:
with gil:
raise OSError('failed to listen to socket: ' + strerror(errno).decode())
return status
Socket accept(self) except NULL:
cdef _socket.sockaddr_storage addr
cdef _socket.socklen_t addrlen
cdef int status
cdef Socket socket
status = _socket.accept(self.sockfd, <_socket.sockaddr *> &addr, &addrlen)
if status == -1:
with gil:
raise OSError('failed to accept from socket: ' + strerror(errno).decode())
socket = Socket()
socket.sockfd = status
socket.family = self.family
socket.socktype = self.socktype
socket.protocol = self.protocol
socket.address = Sockaddr(<_socket.sockaddr *> &addr, addrlen)
return socket
int send(self, Str msg, int flags=0) except -1:
cdef int status
if msg is NULL:
with gil:
raise ValueError('cannot send NULL to socket')
status = _socket.send(self.sockfd, Str.to_c_str(msg), msg.__len__(), flags)
if status == -1:
with gil:
raise OSError('failed to send to socket: ' + strerror(errno).decode())
return status
int sendsubstr(self, Str msg, int flags=0, size_t start=0, size_t stop=0) except -1:
cdef int status
if msg is NULL:
with gil:
raise ValueError('cannot send NULL to socket')
end = msg.__len__()
if stop == 0:
stop = end
elif stop > end:
with gil:
raise ValueError('slice bounds out of range')
if start >= stop:
with gil:
raise ValueError('slice bounds out of order')
size = stop - start
status = _socket.send(self.sockfd, Str.to_c_str(msg) + start, size, flags)
if status == -1:
with gil:
raise OSError('failed to send to socket: ' + strerror(errno).decode())
return status
int sendall(self, Str msg, int flags=0) except -1:
cdef int sent = self.send(msg, flags)
cdef int size = msg.__len__()
while sent < size:
sent += self.sendsubstr(msg, flags, sent)
return sent
iso Str recv(self, int bufsize, int flags=0) except NULL:
cdef int status
# unnecessary zero-initialisation but unavoidable with C++<23 strings
cdef string buf = string(bufsize, <char> 0)
cdef iso Str result
status = _socket.recv(self.sockfd, &buf[0], bufsize, flags)
if status == -1:
with gil:
raise OSError('failed to recv from socket: ' + strerror(errno).decode())
buf.resize(status)
#if status < bufsize:
# buf.shrink_to_fit()
result = new Str()
result._str = move_string(buf)
return consume result
iso Str recvinto(self, iso Str result, int bufsize, int flags=0) except NULL:
cdef int status
cdef int oldsize = result._str.size()
# unnecessary zero-initialisation but unavoidable with C++<23 strings
result._str.resize(oldsize + bufsize)
status = _socket.recv(self.sockfd, &(result._str[oldsize]), bufsize, flags)
if status == -1:
result._str.resize(oldsize)
with gil:
raise OSError('failed to recv from socket: ' + strerror(errno).decode())
result._str.resize(oldsize + status)
# if status < bufsize:
# result._str.shrink_to_fit()
return consume result
iso Str recvuntil(self, Str sentinel, int bufsize, int flags=0) except NULL:
cdef size_t oldsize = 0
cdef Str result = self.recv(bufsize, flags)
cdef size_t newsize = result._str.size()
while not result.find(sentinel, oldsize):
oldsize = newsize
result = self.recvinto(consume result, bufsize, flags)
newsize = result._str.size()
if newsize == oldsize:
break
return consume result
iso Str recvall(self, int flags=0) except NULL:
cdef int bufsize = 96
cdef iso Str result = new Str()
result._str = string()
cdef size_t oldsize = result._str.size()
while True:
result = self.recvinto(consume result, bufsize, flags)
if result._str.size() == oldsize:
break
oldsize = result._str.size()
if bufsize < 4096:
bufsize = bufsize * 2
return consume result
int shutdown(self, int how) except -1:
cdef int status
status = _socket.shutdown(self.sockfd, how)
if status == -1:
with gil:
raise OSError('failed to shutdown socket: ' + strerror(errno).decode())
return status
int close(self) except -1:
cdef int status
status = _socket.close(self.sockfd)
if status == -1:
with gil:
raise OSError('failed to close socket: ' + strerror(errno).decode())
return status
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment