mirror of https://github.com/nocturn9x/giambio.git
Many fixes, still exceptions do not propagate properly, but tasks are cancelled when a task dies
This commit is contained in:
parent
f9eb329ec9
commit
d2d43235c9
|
@ -10,7 +10,7 @@ What works and what does not (25th March 2020 20:35)
|
|||
- Join mechanism: V
|
||||
- Sleep mechanism: V
|
||||
- Cancellation mechanism: V
|
||||
- Exception propagation: V
|
||||
- Exception propagation: X
|
||||
- Concurrent I/O: V
|
||||
- Return values of coroutines: V
|
||||
- Scheduling tasks for future execution: V
|
||||
|
@ -23,6 +23,8 @@ async def countdown(n):
|
|||
while n > 0:
|
||||
print(f"Down {n}")
|
||||
n -= 1
|
||||
if n <= 2: # Test an exception that triggers only sometimes
|
||||
raise ValueError
|
||||
await giambio.sleep(1)
|
||||
print("Countdown over")
|
||||
return "Count DOWN over"
|
||||
|
@ -44,16 +46,13 @@ async def count(stop, step=1):
|
|||
|
||||
|
||||
async def main():
|
||||
try:
|
||||
print("Spawning countdown immediately, scheduling count for 4 secs from now")
|
||||
async with giambio.TaskManager(loop) as manager:
|
||||
task = manager.spawn(countdown(4))
|
||||
task = manager.spawn(countdown(8))
|
||||
manager.schedule(count(8, 2), 4)
|
||||
await task.cancel()
|
||||
for task, ret in manager.values.items():
|
||||
print(f"Function '{task.coroutine.__name__}' at {hex(id(task.coroutine))} returned an object of type '{type(ret).__name__}': {repr(ret)}")
|
||||
except: # TODO: Fix this, see above
|
||||
pass
|
||||
|
||||
|
||||
loop.start(main)
|
||||
|
||||
|
|
Binary file not shown.
|
@ -10,7 +10,7 @@ class Result:
|
|||
self.exc = exc
|
||||
|
||||
def __repr__(self):
|
||||
return f"giambio.core.Result({self.val}, {self.exc})"
|
||||
return f"giambio.core.Result({self.val}, {repr(self.exc)})"
|
||||
|
||||
|
||||
class Task:
|
||||
|
@ -19,7 +19,6 @@ class Task:
|
|||
|
||||
def __init__(self, coroutine: types.coroutine, loop):
|
||||
self.coroutine = coroutine
|
||||
self.status = False # Not ran yet
|
||||
self.joined = False # True if the task is joined
|
||||
self.result = None # Updated when the coroutine execution ends
|
||||
self.loop = loop # The EventLoop object that spawned the task
|
||||
|
@ -30,9 +29,11 @@ class Task:
|
|||
def run(self):
|
||||
"""Simple abstraction layer over the coroutines ``send`` method"""
|
||||
|
||||
self.status = True
|
||||
self.steps += 1
|
||||
self.execution = "RUNNING"
|
||||
return self.coroutine.send(None)
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"giambio.core.Task({self.coroutine}, {self.status}, {self.joined}, {self.result})"
|
||||
|
||||
|
@ -53,12 +54,4 @@ class Task:
|
|||
return await _join(self, silent)
|
||||
|
||||
def get_result(self, silenced=False):
|
||||
if self.result:
|
||||
if not silenced:
|
||||
if self.result.exc:
|
||||
raise self.result.exc
|
||||
else:
|
||||
return self.result.val
|
||||
return self.result.val if self.result.val else self.result.exc
|
||||
|
||||
|
||||
return self.result
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
import types
|
||||
from collections import deque, defaultdict
|
||||
from selectors import DefaultSelector, EVENT_READ, EVENT_WRITE
|
||||
|
@ -58,10 +59,13 @@ class EventLoop:
|
|||
self.to_run.extend(self.joined.pop(self.running, ())) # Reschedules the parent task
|
||||
except RuntimeError:
|
||||
self.to_run.extend(self.joined.pop(self.running, ())) # Reschedules the parent task
|
||||
except Exception as has_raised:
|
||||
except CancelledError:
|
||||
self.running.execution = "CANCELLED"
|
||||
self.to_run.extend(self.joined.pop(self.running, ()))
|
||||
except Exception as err:
|
||||
self.running.execution = "ERRORED"
|
||||
self.running.result = Result(None, err)
|
||||
self.to_run.extend(self.joined.pop(self.running, ())) # Reschedules the parent task
|
||||
self.running.result = Result(None, has_raised) # Save the exception
|
||||
raise
|
||||
except KeyboardInterrupt:
|
||||
self.running.throw(KeyboardInterrupt)
|
||||
|
||||
|
@ -139,7 +143,6 @@ class EventLoop:
|
|||
def want_cancel(self, task):
|
||||
self.to_run.extend(self.joined.pop(self.running, ()))
|
||||
self.to_run.append(self.running) # Reschedules the parent task
|
||||
# task.cancelled = True
|
||||
task.throw(CancelledError())
|
||||
|
||||
|
||||
|
@ -151,4 +154,4 @@ class EventLoop:
|
|||
await _want_write(sock)
|
||||
err = sock.getsockopt(SOL_SOCKET, SO_ERROR)
|
||||
if err != 0:
|
||||
raise OSError(err, f'Connect call failed {addr}')
|
||||
raise OSError(err, f'Connect call failed: {addr}')
|
||||
|
|
|
@ -15,19 +15,21 @@ class TaskManager:
|
|||
self.loop = loop # The event loop that spawned the TaskManager
|
||||
self.silent = silent # Make exceptions silent? (not recommended)
|
||||
|
||||
|
||||
async def _cancel_and_raise(self, exc):
|
||||
for task in self.tasks:
|
||||
await task.cancel()
|
||||
|
||||
async def __aenter__(self):
|
||||
return self
|
||||
|
||||
async def __aexit__(self, type, value, traceback):
|
||||
if type:
|
||||
# TODO: Handle exceptions here
|
||||
...
|
||||
else:
|
||||
for task in self.tasks:
|
||||
if not task.cancelled:
|
||||
while self.tasks:
|
||||
task = self.tasks.popleft()
|
||||
self.values[task] = await task.join()
|
||||
else:
|
||||
self.values[task] = CancelledError()
|
||||
if task.result.exc:
|
||||
if task.result.exc != CancelledError:
|
||||
await self._cancel_and_raise(task.result.exc)
|
||||
|
||||
def spawn(self, coroutine: types.coroutine):
|
||||
"""Schedules a task for execution, appending it to the call stack"""
|
||||
|
|
Loading…
Reference in New Issue