I/O seems to be working now, but further investigation is needed

This commit is contained in:
nocturn9x 2020-11-29 12:46:08 +01:00
parent 2661a153e9
commit 7a840d88be
5 changed files with 46 additions and 52 deletions

View File

@ -20,10 +20,11 @@ __author__ = "Nocturn9x aka Isgiambyy"
__version__ = (1, 0, 0) __version__ = (1, 0, 0)
from . import exceptions from . import exceptions, socket
from .socket import wrap_socket
from .traps import sleep, current_task from .traps import sleep, current_task
from .objects import Event from .objects import Event
from .run import run, clock, wrap_socket, create_pool, get_event_loop, new_event_loop from .run import run, clock, create_pool, get_event_loop, new_event_loop
from .util import debug from .util import debug
__all__ = [ __all__ = [

View File

@ -22,11 +22,9 @@ import socket
from time import sleep as wait from time import sleep as wait
from timeit import default_timer from timeit import default_timer
from .objects import Task, TimeQueue from .objects import Task, TimeQueue
from socket import SOL_SOCKET, SO_ERROR
from .traps import want_read, want_write from .traps import want_read, want_write
from .util.debug import BaseDebugger from .util.debug import BaseDebugger
from itertools import chain from itertools import chain
from .socket import AsyncSocket, WantWrite, WantRead
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
from .exceptions import (InternalError, from .exceptions import (InternalError,
CancelledError, CancelledError,
@ -34,6 +32,9 @@ from .exceptions import (InternalError,
) )
IOInterrupt = (BlockingIOError, InterruptedError)
class AsyncScheduler: class AsyncScheduler:
""" """
A simple asynchronous scheduler implementation. Tries to mimic the threaded A simple asynchronous scheduler implementation. Tries to mimic the threaded
@ -158,6 +159,7 @@ class AsyncScheduler:
self.debugger.on_task_exit(self.current_task) self.debugger.on_task_exit(self.current_task)
self.join(self.current_task) self.join(self.current_task)
except BaseException as err: except BaseException as err:
raise
# Task raised an exception # Task raised an exception
self.current_task.exc = err self.current_task.exc = err
self.current_task.status = "crashed" self.current_task.status = "crashed"
@ -307,7 +309,14 @@ class AsyncScheduler:
""" """
for to_cancel in chain(self.tasks, self.paused, self.get_event_tasks()): for to_cancel in chain(self.tasks, self.paused, self.get_event_tasks()):
self.cancel(to_cancel) try:
self.cancel(to_cancel)
except CancelledError:
# Task was cancelled
self.current_task.status = "cancelled"
self.current_task.cancelled = True
self.current_task.cancel_pending = False
self.debugger.after_cancel(self.current_task)
def close(self): def close(self):
""" """
@ -315,9 +324,8 @@ class AsyncScheduler:
inside it and tearing down any extra machinery inside it and tearing down any extra machinery
""" """
# self.cancel_all() self.cancel_all()
# self.shutdown() self.shutdown()
...
def join(self, task: Task): def join(self, task: Task):
""" """
@ -404,13 +412,6 @@ class AsyncScheduler:
# The socket is already registered doing something else # The socket is already registered doing something else
raise ResourceBusy("The given resource is busy!") from None raise ResourceBusy("The given resource is busy!") from None
def wrap_socket(self, sock):
"""
Wraps a standard socket into an AsyncSocket object
"""
return AsyncSocket(sock, self)
async def read_sock(self, sock: socket.socket, buffer: int): async def read_sock(self, sock: socket.socket, buffer: int):
""" """
Reads from a socket asynchronously, waiting until the resource is Reads from a socket asynchronously, waiting until the resource is
@ -426,8 +427,17 @@ class AsyncScheduler:
is available and returning the result of the accept() call is available and returning the result of the accept() call
""" """
await want_read(sock) # TODO: Is this ok?
return sock.accept() # This does not feel right because the loop will only
# exit when the socket has been accepted, preventing other
# stuff from running
while True:
try:
return sock.accept()
except IOInterrupt: # Do we need this exception thingy everywhere?
# Some methods have never errored out, but this did and doing
# so seemed to fix the issue, needs investigation
await want_read(sock)
async def sock_sendall(self, sock: socket.socket, data: bytes): async def sock_sendall(self, sock: socket.socket, data: bytes):
""" """
@ -446,19 +456,14 @@ class AsyncScheduler:
""" """
await want_write(sock) await want_write(sock)
sock.close()
self.selector.unregister(sock) self.selector.unregister(sock)
self.current_task.last_io = () self.current_task.last_io = ()
sock.close()
async def connect_sock(self, sock: socket.socket, addr: tuple): async def connect_sock(self, sock: socket.socket, addr: tuple):
""" """
Connects a socket asynchronously Connects a socket asynchronously
""" """
try: # "Borrowed" from curio await want_write(sock)
return sock.connect(addr) return sock.connect(addr)
except WantWrite:
await want_write(sock)
err = sock.getsockopt(SOL_SOCKET, SO_ERROR)
if err != 0:
raise OSError(err, f"Connect call failed: {addr}")

View File

@ -16,14 +16,12 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
""" """
import socket
import inspect import inspect
import threading import threading
from .core import AsyncScheduler from .core import AsyncScheduler
from .exceptions import GiambioError from .exceptions import GiambioError
from .context import TaskManager from .context import TaskManager
from timeit import default_timer from timeit import default_timer
from .socket import AsyncSocket
from .util.debug import BaseDebugger from .util.debug import BaseDebugger
from types import FunctionType from types import FunctionType
@ -87,14 +85,6 @@ def clock():
return get_event_loop().clock() return get_event_loop().clock()
def wrap_socket(sock: socket.socket) -> AsyncSocket:
"""
Wraps a synchronous socket into a giambio.socket.AsyncSocket
"""
return get_event_loop().wrap_socket(sock)
def create_pool(): def create_pool():
""" """
Creates an async pool Creates an async pool

View File

@ -16,30 +16,19 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
""" """
from .run import get_event_loop
import socket import socket
from .exceptions import ResourceClosed from .exceptions import ResourceClosed
from .traps import sleep
# Stolen from curio class AsyncSocket:
try:
from ssl import SSLWantReadError, SSLWantWriteError
WantRead = (BlockingIOError, InterruptedError, SSLWantReadError)
WantWrite = (BlockingIOError, InterruptedError, SSLWantWriteError)
except ImportError:
WantRead = (BlockingIOError, InterruptedError)
WantWrite = (BlockingIOError, InterruptedError)
class AsyncSocket(object):
""" """
Abstraction layer for asynchronous TCP sockets Abstraction layer for asynchronous sockets
""" """
def __init__(self, sock: socket.socket, loop): def __init__(self, sock: socket.socket):
self.sock = sock self.sock = sock
self.loop = loop self.loop = get_event_loop()
self._closed = False self._closed = False
self.sock.setblocking(False) self.sock.setblocking(False)
@ -60,7 +49,7 @@ class AsyncSocket(object):
if self._closed: if self._closed:
raise ResourceClosed("I/O operation on closed socket") raise ResourceClosed("I/O operation on closed socket")
to_wrap = await self.loop.accept_sock(self.sock) to_wrap = await self.loop.accept_sock(self.sock)
return self.loop.wrap_socket(to_wrap[0]), to_wrap[1] return wrap_socket(to_wrap[0]), to_wrap[1]
async def send_all(self, data: bytes): async def send_all(self, data: bytes):
""" """
@ -98,3 +87,11 @@ class AsyncSocket(object):
def __repr__(self): def __repr__(self):
return f"giambio.socket.AsyncSocket({self.sock}, {self.loop})" return f"giambio.socket.AsyncSocket({self.sock}, {self.loop})"
def wrap_socket(sock: socket.socket) -> AsyncSocket:
"""
Wraps a standard socket into an async socket
"""
return AsyncSocket(sock)

View File

@ -47,6 +47,7 @@ if __name__ == "__main__":
try: try:
giambio.run(serve, ("localhost", port)) giambio.run(serve, ("localhost", port))
except (Exception, KeyboardInterrupt) as error: # Exceptions propagate! except (Exception, KeyboardInterrupt) as error: # Exceptions propagate!
raise
if isinstance(error, KeyboardInterrupt): if isinstance(error, KeyboardInterrupt):
logging.info("Ctrl+C detected, exiting") logging.info("Ctrl+C detected, exiting")
else: else: