mirror of https://github.com/nocturn9x/giambio.git
Added initial support for nested pools and added related tests. Added a couple more tests and separated the debugger class in a separate module. Unified want_read and want_write into a unique read_or_write method
This commit is contained in:
parent
2429cbb863
commit
899e12ead7
|
@ -18,8 +18,8 @@ limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
import types
|
import types
|
||||||
from .core import AsyncScheduler
|
|
||||||
from .objects import Task
|
from .objects import Task
|
||||||
|
from .core import AsyncScheduler
|
||||||
|
|
||||||
|
|
||||||
class TaskManager:
|
class TaskManager:
|
||||||
|
@ -40,7 +40,7 @@ class TaskManager:
|
||||||
Spawns a child task
|
Spawns a child task
|
||||||
"""
|
"""
|
||||||
|
|
||||||
task = Task(func(*args), func.__name__ or str(func))
|
task = Task(func(*args), func.__name__ or str(func), self)
|
||||||
task.joiners = [self.loop.current_task]
|
task.joiners = [self.loop.current_task]
|
||||||
self.loop.tasks.append(task)
|
self.loop.tasks.append(task)
|
||||||
self.loop.debugger.on_task_spawn(task)
|
self.loop.debugger.on_task_spawn(task)
|
||||||
|
@ -53,7 +53,7 @@ class TaskManager:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert n >= 0, "The time delay can't be negative"
|
assert n >= 0, "The time delay can't be negative"
|
||||||
task = Task(func(*args), func.__name__ or str(func))
|
task = Task(func(*args), func.__name__ or str(func), self)
|
||||||
task.joiners = [self.loop.current_task]
|
task.joiners = [self.loop.current_task]
|
||||||
task.sleep_start = self.loop.clock()
|
task.sleep_start = self.loop.clock()
|
||||||
self.loop.paused.put(task, n)
|
self.loop.paused.put(task, n)
|
||||||
|
|
108
giambio/core.py
108
giambio/core.py
|
@ -71,6 +71,8 @@ class AsyncScheduler:
|
||||||
self.to_send = None
|
self.to_send = None
|
||||||
# Have we ever ran?
|
# Have we ever ran?
|
||||||
self.has_ran = False
|
self.has_ran = False
|
||||||
|
# The current pool
|
||||||
|
self.current_pool = None
|
||||||
|
|
||||||
def done(self):
|
def done(self):
|
||||||
"""
|
"""
|
||||||
|
@ -100,7 +102,7 @@ class AsyncScheduler:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
if self.done():
|
if self.done():
|
||||||
# If we're done, which means there is no
|
# If we're done, which means there is no
|
||||||
# sleeping tasks, no events to deliver,
|
# sleeping tasks, no events to deliver,
|
||||||
# no I/O to do and no running tasks, we
|
# no I/O to do and no running tasks, we
|
||||||
# simply tear us down and return to self.start
|
# simply tear us down and return to self.start
|
||||||
|
@ -111,14 +113,17 @@ class AsyncScheduler:
|
||||||
# we try to schedule the asleep ones
|
# we try to schedule the asleep ones
|
||||||
if self.paused:
|
if self.paused:
|
||||||
self.awake_sleeping()
|
self.awake_sleeping()
|
||||||
# The next step is checking for I/O
|
if self.selector.get_map():
|
||||||
self.check_io()
|
# The next step is checking for I/O
|
||||||
|
self.check_io()
|
||||||
# Try to awake event-waiting tasks
|
# Try to awake event-waiting tasks
|
||||||
self.check_events()
|
if self.events:
|
||||||
|
self.check_events()
|
||||||
# Otherwise, while there are tasks ready to run, well, run them!
|
# Otherwise, while there are tasks ready to run, well, run them!
|
||||||
while self.tasks:
|
while self.tasks:
|
||||||
# Sets the currently running task
|
# Sets the currently running task
|
||||||
self.current_task = self.tasks.pop(0)
|
self.current_task = self.tasks.pop(0)
|
||||||
|
self.current_pool = self.current_task.pool
|
||||||
self.debugger.before_task_step(self.current_task)
|
self.debugger.before_task_step(self.current_task)
|
||||||
if self.current_task.cancel_pending:
|
if self.current_task.cancel_pending:
|
||||||
# We perform the deferred cancellation
|
# We perform the deferred cancellation
|
||||||
|
@ -151,7 +156,6 @@ class AsyncScheduler:
|
||||||
self.current_task.cancel_pending = False
|
self.current_task.cancel_pending = False
|
||||||
self.debugger.after_cancel(self.current_task)
|
self.debugger.after_cancel(self.current_task)
|
||||||
self.join(self.current_task)
|
self.join(self.current_task)
|
||||||
# TODO: Do we need to join?
|
|
||||||
except StopIteration as ret:
|
except StopIteration as ret:
|
||||||
# Coroutine ends
|
# Coroutine ends
|
||||||
self.current_task.status = "end"
|
self.current_task.status = "end"
|
||||||
|
@ -163,21 +167,22 @@ class AsyncScheduler:
|
||||||
# Coroutine raised
|
# Coroutine raised
|
||||||
self.current_task.exc = err
|
self.current_task.exc = err
|
||||||
self.current_task.status = "crashed"
|
self.current_task.status = "crashed"
|
||||||
|
self.debugger.on_exception_raised(self.current_task, err)
|
||||||
self.join(self.current_task) # This propagates the exception
|
self.join(self.current_task) # This propagates the exception
|
||||||
|
|
||||||
def do_cancel(self, task: Task = None):
|
def do_cancel(self, task: Task = None):
|
||||||
"""
|
"""
|
||||||
Performs task cancellation by throwing CancelledError inside the current
|
Performs task cancellation by throwing CancelledError inside the given
|
||||||
task in order to stop it from running. The loop continues to execute
|
task in order to stop it from running. The loop continues to execute
|
||||||
as tasks are independent
|
as tasks are independent
|
||||||
"""
|
"""
|
||||||
|
|
||||||
task = task or self.current_task
|
task = task or self.current_task
|
||||||
if not task.cancelled:
|
if not task.cancelled and not task.exc:
|
||||||
self.debugger.before_cancel(task)
|
self.debugger.before_cancel(task)
|
||||||
task.throw(CancelledError())
|
task.throw(CancelledError())
|
||||||
|
|
||||||
def get_running(self):
|
def get_current(self):
|
||||||
"""
|
"""
|
||||||
Returns the current task to an async caller
|
Returns the current task to an async caller
|
||||||
"""
|
"""
|
||||||
|
@ -218,8 +223,8 @@ class AsyncScheduler:
|
||||||
Checks and schedules task to perform I/O
|
Checks and schedules task to perform I/O
|
||||||
"""
|
"""
|
||||||
|
|
||||||
before_time = self.clock()
|
before_time = self.clock() # Used for the debugger
|
||||||
if self.tasks or self.events and not self.selector.get_map():
|
if self.tasks or self.events:
|
||||||
# If there are either tasks or events and no I/O, never wait
|
# If there are either tasks or events and no I/O, never wait
|
||||||
timeout = 0.0
|
timeout = 0.0
|
||||||
elif self.paused:
|
elif self.paused:
|
||||||
|
@ -227,13 +232,12 @@ class AsyncScheduler:
|
||||||
timeout = max(0.0, self.paused[0][0] - self.clock())
|
timeout = max(0.0, self.paused[0][0] - self.clock())
|
||||||
else:
|
else:
|
||||||
# If there is *only* I/O, we wait a fixed amount of time
|
# If there is *only* I/O, we wait a fixed amount of time
|
||||||
timeout = 1
|
timeout = 1.0
|
||||||
self.debugger.before_io(timeout)
|
self.debugger.before_io(timeout)
|
||||||
if self.selector.get_map():
|
io_ready = self.selector.select(timeout)
|
||||||
io_ready = self.selector.select(timeout)
|
# Get sockets that are ready and schedule their tasks
|
||||||
# Get sockets that are ready and schedule their tasks
|
for key, _ in io_ready:
|
||||||
for key, _ in io_ready:
|
self.tasks.append(key.data) # Resource ready? Schedule its task
|
||||||
self.tasks.append(key.data) # Resource ready? Schedule its task
|
|
||||||
self.debugger.after_io(self.clock() - before_time)
|
self.debugger.after_io(self.clock() - before_time)
|
||||||
|
|
||||||
def start(self, func: types.FunctionType, *args):
|
def start(self, func: types.FunctionType, *args):
|
||||||
|
@ -241,7 +245,7 @@ class AsyncScheduler:
|
||||||
Starts the event loop from a sync context
|
Starts the event loop from a sync context
|
||||||
"""
|
"""
|
||||||
|
|
||||||
entry = Task(func(*args), func.__name__ or str(func))
|
entry = Task(func(*args), func.__name__ or str(func), None)
|
||||||
self.tasks.append(entry)
|
self.tasks.append(entry)
|
||||||
self.debugger.on_start()
|
self.debugger.on_start()
|
||||||
self.run()
|
self.run()
|
||||||
|
@ -267,32 +271,49 @@ class AsyncScheduler:
|
||||||
|
|
||||||
def cancel_all(self):
|
def cancel_all(self):
|
||||||
"""
|
"""
|
||||||
Cancels all tasks in preparation for the exception
|
Cancels all tasks in the current pool,
|
||||||
throwing from self.join
|
preparing for the exception throwing
|
||||||
|
from self.join
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
to_reschedule = []
|
||||||
for to_cancel in chain(self.tasks, self.paused):
|
for to_cancel in chain(self.tasks, self.paused):
|
||||||
try:
|
try:
|
||||||
self.cancel(to_cancel)
|
if to_cancel.pool is self.current_pool:
|
||||||
|
self.cancel(to_cancel)
|
||||||
|
elif to_cancel.status == "sleep":
|
||||||
|
deadline = to_cancel.next_deadline - self.clock()
|
||||||
|
to_reschedule.append((to_cancel, deadline))
|
||||||
|
else:
|
||||||
|
to_reschedule.append((to_cancel, None))
|
||||||
except CancelledError:
|
except CancelledError:
|
||||||
to_cancel.status = "cancelled"
|
to_cancel.status = "cancelled"
|
||||||
to_cancel.cancelled = True
|
to_cancel.cancelled = True
|
||||||
to_cancel.cancel_pending = False
|
to_cancel.cancel_pending = False
|
||||||
self.debugger.after_cancel(to_cancel)
|
self.debugger.after_cancel(to_cancel)
|
||||||
self.tasks.remove(to_cancel)
|
self.tasks.remove(to_cancel)
|
||||||
|
for task, deadline in to_reschedule:
|
||||||
|
if deadline is not None:
|
||||||
|
self.paused.put(task, deadline)
|
||||||
|
else:
|
||||||
|
self.tasks.append(task)
|
||||||
|
# If there is other work to do (nested pools)
|
||||||
|
# we tell so to our caller
|
||||||
|
return bool(to_reschedule)
|
||||||
|
|
||||||
def join(self, task: Task):
|
def join(self, task: Task):
|
||||||
"""
|
"""
|
||||||
Handler for the 'join' event, does some magic to tell the scheduler
|
Joins a task to its callers (implicitly, the parent
|
||||||
to wait until the current coroutine ends
|
task, but also every other task who called await
|
||||||
|
task.join() on the task object)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
task.joined = True
|
task.joined = True
|
||||||
if task.finished or task.cancelled:
|
if task.finished or task.cancelled:
|
||||||
self.reschedule_joinee(task)
|
self.reschedule_joinee(task)
|
||||||
elif task.exc:
|
elif task.exc:
|
||||||
self.cancel_all()
|
if not self.cancel_all():
|
||||||
self.reschedule_joinee(task)
|
self.reschedule_joinee(task)
|
||||||
|
|
||||||
def sleep(self, seconds: int or float):
|
def sleep(self, seconds: int or float):
|
||||||
"""
|
"""
|
||||||
|
@ -300,16 +321,17 @@ class AsyncScheduler:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.debugger.before_sleep(self.current_task, seconds)
|
self.debugger.before_sleep(self.current_task, seconds)
|
||||||
if seconds:
|
if seconds: # if seconds == 0, this acts as a switch!
|
||||||
self.current_task.status = "sleep"
|
self.current_task.status = "sleep"
|
||||||
self.current_task.sleep_start = self.clock()
|
self.current_task.sleep_start = self.clock()
|
||||||
self.paused.put(self.current_task, seconds)
|
self.paused.put(self.current_task, seconds)
|
||||||
|
self.current_task.next_deadline = self.clock() + seconds
|
||||||
else:
|
else:
|
||||||
self.tasks.append(self.current_task)
|
self.tasks.append(self.current_task)
|
||||||
|
|
||||||
def cancel(self, task: Task = None):
|
def cancel(self, task: Task = None):
|
||||||
"""
|
"""
|
||||||
Handler for the 'cancel' event, schedules the task to be cancelled later
|
Schedules the task to be cancelled later
|
||||||
or does so straight away if it is safe to do so
|
or does so straight away if it is safe to do so
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
@ -336,44 +358,30 @@ class AsyncScheduler:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
event.waiters.append(self.current_task)
|
event.waiters.append(self.current_task)
|
||||||
|
# Since we don't reschedule the task, it will
|
||||||
|
# not execute until check_events is called
|
||||||
|
|
||||||
# TODO: More generic I/O rather than just sockets
|
# TODO: More generic I/O rather than just sockets
|
||||||
def want_read(self, sock: socket.socket):
|
# Best way to do so? Probably threads
|
||||||
|
def read_or_write(self, sock: socket.socket, evt_type: str):
|
||||||
"""
|
"""
|
||||||
Handler for the 'want_read' event, registers the socket inside the
|
Registers the given socket inside the
|
||||||
selector to perform I/0 multiplexing
|
selector to perform I/0 multiplexing
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self.current_task.status = "io"
|
self.current_task.status = "io"
|
||||||
if self.current_task.last_io:
|
if self.current_task.last_io:
|
||||||
if self.current_task.last_io == ("READ", sock):
|
if self.current_task.last_io == (evt_type, sock):
|
||||||
# Socket is already scheduled!
|
|
||||||
return
|
|
||||||
self.selector.unregister(sock)
|
|
||||||
self.current_task.last_io = "READ", sock
|
|
||||||
try:
|
|
||||||
self.selector.register(sock, EVENT_READ, self.current_task)
|
|
||||||
except KeyError:
|
|
||||||
# The socket is already registered doing something else
|
|
||||||
raise ResourceBusy("The given resource is busy!") from None
|
|
||||||
|
|
||||||
def want_write(self, sock: socket.socket):
|
|
||||||
"""
|
|
||||||
Handler for the 'want_write' event, registers the socket inside the
|
|
||||||
selector to perform I/0 multiplexing
|
|
||||||
"""
|
|
||||||
|
|
||||||
self.current_task.status = "io"
|
|
||||||
if self.current_task.last_io:
|
|
||||||
if self.current_task.last_io == ("WRITE", sock):
|
|
||||||
# Socket is already scheduled!
|
# Socket is already scheduled!
|
||||||
return
|
return
|
||||||
# TODO: Inspect why modify() causes issues
|
# TODO: Inspect why modify() causes issues
|
||||||
self.selector.unregister(sock)
|
self.selector.unregister(sock)
|
||||||
self.current_task.last_io = "WRITE", sock
|
self.current_task.last_io = evt_type, sock
|
||||||
|
evt = EVENT_READ if evt_type == "read" else EVENT_WRITE
|
||||||
try:
|
try:
|
||||||
self.selector.register(sock, EVENT_WRITE, self.current_task)
|
self.selector.register(sock, evt, self.current_task)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
# 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):
|
def wrap_socket(self, sock):
|
||||||
|
|
|
@ -32,6 +32,7 @@ class Task:
|
||||||
|
|
||||||
coroutine: types.CoroutineType
|
coroutine: types.CoroutineType
|
||||||
name: str
|
name: str
|
||||||
|
pool: "giambio.context.TaskManager"
|
||||||
cancelled: bool = False
|
cancelled: bool = False
|
||||||
exc: BaseException = None
|
exc: BaseException = None
|
||||||
result: object = None
|
result: object = None
|
||||||
|
@ -43,6 +44,7 @@ class Task:
|
||||||
joined: bool = False
|
joined: bool = False
|
||||||
cancel_pending: bool = False
|
cancel_pending: bool = False
|
||||||
sleep_start: float = 0.0
|
sleep_start: float = 0.0
|
||||||
|
next_deadline: float = 0.0
|
||||||
|
|
||||||
def run(self, what=None):
|
def run(self, what=None):
|
||||||
"""
|
"""
|
||||||
|
@ -164,4 +166,4 @@ class TimeQueue:
|
||||||
Gets the first task that is meant to run
|
Gets the first task that is meant to run
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return heappop(self.container)[2]
|
return heappop(self.container)[2]
|
||||||
|
|
|
@ -62,7 +62,7 @@ async def current_task():
|
||||||
Gets the currently running task
|
Gets the currently running task
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return await create_trap("get_running")
|
return await create_trap("get_current")
|
||||||
|
|
||||||
|
|
||||||
async def join(task):
|
async def join(task):
|
||||||
|
@ -90,7 +90,7 @@ async def cancel(task):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await create_trap("cancel", task)
|
await create_trap("cancel", task)
|
||||||
assert task.cancelled, f"Coroutine ignored CancelledError"
|
assert task.cancelled, f"Task ignored CancelledError"
|
||||||
|
|
||||||
|
|
||||||
async def want_read(stream):
|
async def want_read(stream):
|
||||||
|
@ -101,7 +101,7 @@ async def want_read(stream):
|
||||||
:param stream: The resource that needs to be read
|
:param stream: The resource that needs to be read
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await create_trap("want_read", stream)
|
await create_trap("read_or_write", stream, "read")
|
||||||
|
|
||||||
|
|
||||||
async def want_write(stream):
|
async def want_write(stream):
|
||||||
|
@ -112,7 +112,7 @@ async def want_write(stream):
|
||||||
:param stream: The resource that needs to be written
|
:param stream: The resource that needs to be written
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await create_trap("want_write", stream)
|
await create_trap("read_or_write", stream, "write")
|
||||||
|
|
||||||
|
|
||||||
async def event_set(event):
|
async def event_set(event):
|
||||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
||||||
"""
|
"""
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from giambio.objects import Task
|
from giambio.objects import Task
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
|
|
||||||
class BaseDebugger(ABC):
|
class BaseDebugger(ABC):
|
||||||
|
@ -44,7 +43,7 @@ class BaseDebugger(ABC):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def on_task_schedule(self, task: Task, delay: Union[int, float]):
|
def on_task_schedule(self, task: Task, delay: float):
|
||||||
"""
|
"""
|
||||||
This method is called when a new task is
|
This method is called when a new task is
|
||||||
scheduled (not spawned)
|
scheduled (not spawned)
|
||||||
|
@ -54,7 +53,7 @@ class BaseDebugger(ABC):
|
||||||
:type task: :class: giambio.objects.Task
|
:type task: :class: giambio.objects.Task
|
||||||
:param delay: The delay, in seconds, after which
|
:param delay: The delay, in seconds, after which
|
||||||
the task will start executing
|
the task will start executing
|
||||||
:type delay: int
|
:type delay: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -111,7 +110,7 @@ class BaseDebugger(ABC):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def before_sleep(self, task: Task, seconds: Union[int, float]):
|
def before_sleep(self, task: Task, seconds: float):
|
||||||
"""
|
"""
|
||||||
This method is called before a task goes
|
This method is called before a task goes
|
||||||
to sleep
|
to sleep
|
||||||
|
@ -127,7 +126,7 @@ class BaseDebugger(ABC):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def after_sleep(self, task: Task, seconds: Union[int, float]):
|
def after_sleep(self, task: Task, seconds: float):
|
||||||
"""
|
"""
|
||||||
This method is called after a tasks
|
This method is called after a tasks
|
||||||
awakes from sleeping
|
awakes from sleeping
|
||||||
|
@ -137,13 +136,13 @@ class BaseDebugger(ABC):
|
||||||
:type task: :class: giambio.objects.Task
|
:type task: :class: giambio.objects.Task
|
||||||
:param seconds: The amount of seconds the
|
:param seconds: The amount of seconds the
|
||||||
task actually slept
|
task actually slept
|
||||||
:type seconds: int
|
:type seconds: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def before_io(self, timeout: Union[int, float]):
|
def before_io(self, timeout: float):
|
||||||
"""
|
"""
|
||||||
This method is called right before
|
This method is called right before
|
||||||
the event loop checks for I/O events
|
the event loop checks for I/O events
|
||||||
|
@ -151,13 +150,13 @@ class BaseDebugger(ABC):
|
||||||
:param timeout: The max. amount of seconds
|
:param timeout: The max. amount of seconds
|
||||||
that the loop will hang when using the select()
|
that the loop will hang when using the select()
|
||||||
system call
|
system call
|
||||||
:type timeout: int
|
:type timeout: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def after_io(self, timeout: Union[int, float]):
|
def after_io(self, timeout: float):
|
||||||
"""
|
"""
|
||||||
This method is called right after
|
This method is called right after
|
||||||
the event loop has checked for I/O events
|
the event loop has checked for I/O events
|
||||||
|
@ -165,7 +164,7 @@ class BaseDebugger(ABC):
|
||||||
:param timeout: The actual amount of seconds
|
:param timeout: The actual amount of seconds
|
||||||
that the loop has hung when using the select()
|
that the loop has hung when using the select()
|
||||||
system call
|
system call
|
||||||
:type timeout: int
|
:type timeout: float
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -196,3 +195,18 @@ class BaseDebugger(ABC):
|
||||||
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def on_exception_raised(self, task: Task, exc: BaseException):
|
||||||
|
"""
|
||||||
|
This method is called right after a task
|
||||||
|
has raised an exception
|
||||||
|
|
||||||
|
:param task: The Task object representing a
|
||||||
|
giambio Task and wrapping a coroutine
|
||||||
|
:type task: :class: giambio.objects.Task
|
||||||
|
:param exc: The exception that was raised
|
||||||
|
:type exc: BaseException
|
||||||
|
"""
|
||||||
|
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
import giambio
|
||||||
|
from debugger import Debugger
|
||||||
|
|
||||||
|
|
||||||
|
async def child():
|
||||||
|
print("[child] Child spawned!! Sleeping for 2 seconds")
|
||||||
|
await giambio.sleep(2)
|
||||||
|
print("[child] Had a nice nap!")
|
||||||
|
|
||||||
|
|
||||||
|
async def child1():
|
||||||
|
print("[child 1] Child spawned!! Sleeping for 2 seconds")
|
||||||
|
await giambio.sleep(2)
|
||||||
|
print("[child 1] Had a nice nap!")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
start = giambio.clock()
|
||||||
|
async with giambio.create_pool() as pool:
|
||||||
|
pool.spawn(child)
|
||||||
|
task = pool.spawn(child1)
|
||||||
|
await task.cancel()
|
||||||
|
print("[main] Children spawned, awaiting completion")
|
||||||
|
print(f"[main] Children execution complete in {giambio.clock() - start:.2f} seconds")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
giambio.run(main)
|
|
@ -0,0 +1,50 @@
|
||||||
|
import giambio
|
||||||
|
|
||||||
|
|
||||||
|
class Debugger(giambio.debug.BaseDebugger):
|
||||||
|
"""
|
||||||
|
A simple debugger for giambio
|
||||||
|
"""
|
||||||
|
|
||||||
|
def on_start(self):
|
||||||
|
print("## Started running")
|
||||||
|
|
||||||
|
def on_exit(self):
|
||||||
|
print("## Finished running")
|
||||||
|
|
||||||
|
def on_task_schedule(self, task, delay: int):
|
||||||
|
print(f">> A task named '{task.name}' was scheduled to run in {delay:.2f} seconds")
|
||||||
|
|
||||||
|
def on_task_spawn(self, task):
|
||||||
|
print(f">> A task named '{task.name}' was spawned")
|
||||||
|
|
||||||
|
def on_task_exit(self, task):
|
||||||
|
print(f"<< Task '{task.name}' exited")
|
||||||
|
|
||||||
|
def before_task_step(self, task):
|
||||||
|
print(f"-> About to run a step for '{task.name}'")
|
||||||
|
|
||||||
|
def after_task_step(self, task):
|
||||||
|
print(f"<- Ran a step for '{task.name}'")
|
||||||
|
|
||||||
|
def before_sleep(self, task, seconds):
|
||||||
|
print(f"# About to put '{task.name}' to sleep for {seconds:.2f} seconds")
|
||||||
|
|
||||||
|
def after_sleep(self, task, seconds):
|
||||||
|
print(f"# Task '{task.name}' slept for {seconds:.2f} seconds")
|
||||||
|
|
||||||
|
def before_io(self, timeout):
|
||||||
|
print(f"!! About to check for I/O for up to {timeout:.2f} seconds")
|
||||||
|
|
||||||
|
def after_io(self, timeout):
|
||||||
|
print(f"!! Done I/O check (waited for {timeout:.2f} seconds)")
|
||||||
|
|
||||||
|
def before_cancel(self, task):
|
||||||
|
print(f"// About to cancel '{task.name}'")
|
||||||
|
|
||||||
|
def after_cancel(self, task):
|
||||||
|
print(f"// Cancelled '{task.name}'")
|
||||||
|
|
||||||
|
def on_exception_raised(self, task, exc):
|
||||||
|
print(f"== '{task.name}' raised {repr(exc)}")
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import giambio
|
||||||
|
from debugger import Debugger
|
||||||
|
|
||||||
|
|
||||||
|
async def child():
|
||||||
|
print("[child] Child spawned!! Sleeping for 2 seconds")
|
||||||
|
await giambio.sleep(2)
|
||||||
|
print("[child] Had a nice nap!")
|
||||||
|
raise TypeError("rip") # Watch the exception magically propagate!
|
||||||
|
|
||||||
|
|
||||||
|
async def child1():
|
||||||
|
print("[child 1] Child spawned!! Sleeping for 2 seconds")
|
||||||
|
await giambio.sleep(2)
|
||||||
|
print("[child 1] Had a nice nap!")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
start = giambio.clock()
|
||||||
|
try:
|
||||||
|
async with giambio.create_pool() as pool:
|
||||||
|
pool.spawn(child)
|
||||||
|
pool.spawn(child1)
|
||||||
|
print("[main] Children spawned, awaiting completion")
|
||||||
|
except Exception as error:
|
||||||
|
# Because exceptions just *work*!
|
||||||
|
print(f"[main] Exception from child caught! {repr(error)}")
|
||||||
|
print(f"[main] Children execution complete in {giambio.clock() - start:.2f} seconds")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
giambio.run(main, debugger=Debugger())
|
|
@ -0,0 +1,42 @@
|
||||||
|
import giambio
|
||||||
|
from debugger import Debugger
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async def child():
|
||||||
|
print("[child] Child spawned!! Sleeping for 2 seconds")
|
||||||
|
await giambio.sleep(2)
|
||||||
|
print("[child] Had a nice nap!")
|
||||||
|
raise TypeError("rip") # Watch the exception magically propagate!
|
||||||
|
|
||||||
|
|
||||||
|
async def child1():
|
||||||
|
print("[child 1] Child spawned!! Sleeping for 2 seconds")
|
||||||
|
await giambio.sleep(2)
|
||||||
|
print("[child 1] Had a nice nap!")
|
||||||
|
|
||||||
|
|
||||||
|
async def child2():
|
||||||
|
print("[child 2] Child spawned!! Sleeping for 4 seconds")
|
||||||
|
await giambio.sleep(4)
|
||||||
|
print("[child 2] Had a nice nap!")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
start = giambio.clock()
|
||||||
|
try:
|
||||||
|
async with giambio.create_pool() as pool:
|
||||||
|
pool.spawn(child)
|
||||||
|
pool.spawn(child1)
|
||||||
|
print("[main] Children spawned, awaiting completion")
|
||||||
|
async with giambio.create_pool() as new_pool:
|
||||||
|
new_pool.spawn(child2)
|
||||||
|
print("[main] 3rd child spawned")
|
||||||
|
except Exception as error:
|
||||||
|
# Because exceptions just *work*!
|
||||||
|
print(f"[main] Exception from child caught! {repr(error)}")
|
||||||
|
print(f"[main] Children execution complete in {giambio.clock() - start:.2f} seconds")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
giambio.run(main, debugger=None)
|
|
@ -1,56 +1,10 @@
|
||||||
import giambio
|
import giambio
|
||||||
|
|
||||||
|
|
||||||
class Debugger(giambio.debug.BaseDebugger):
|
|
||||||
"""
|
|
||||||
A simple debugger for this test
|
|
||||||
"""
|
|
||||||
|
|
||||||
def on_start(self):
|
|
||||||
print("## Started running")
|
|
||||||
|
|
||||||
def on_exit(self):
|
|
||||||
print("## Finished running")
|
|
||||||
|
|
||||||
def on_task_schedule(self, task, delay: int):
|
|
||||||
print(f">> A task named '{task.name}' was scheduled to run in {delay:.2f} seconds")
|
|
||||||
|
|
||||||
def on_task_spawn(self, task):
|
|
||||||
print(f">> A task named '{task.name}' was spawned")
|
|
||||||
|
|
||||||
def on_task_exit(self, task):
|
|
||||||
print(f"<< Task '{task.name}' exited")
|
|
||||||
|
|
||||||
def before_task_step(self, task):
|
|
||||||
print(f"-> About to run a step for '{task.name}'")
|
|
||||||
|
|
||||||
def after_task_step(self, task):
|
|
||||||
print(f"<- Ran a step for '{task.name}'")
|
|
||||||
|
|
||||||
def before_sleep(self, task, seconds):
|
|
||||||
print(f"# About to put '{task.name}' to sleep for {seconds:.2f} seconds")
|
|
||||||
|
|
||||||
def after_sleep(self, task, seconds):
|
|
||||||
print(f"# Task '{task.name}' slept for {seconds:.2f} seconds")
|
|
||||||
|
|
||||||
def before_io(self, timeout):
|
|
||||||
print(f"!! About to check for I/O for up to {timeout:.2f} seconds")
|
|
||||||
|
|
||||||
def after_io(self, timeout):
|
|
||||||
print(f"!! Done I/O check (waited for {timeout:.2f} seconds)")
|
|
||||||
|
|
||||||
def before_cancel(self, task):
|
|
||||||
print(f"// About to cancel '{task.name}'")
|
|
||||||
|
|
||||||
def after_cancel(self, task):
|
|
||||||
print(f"// Cancelled '{task.name}'")
|
|
||||||
|
|
||||||
|
|
||||||
async def child():
|
async def child():
|
||||||
print("[child] Child spawned!! Sleeping for 2 seconds")
|
print("[child] Child spawned!! Sleeping for 2 seconds")
|
||||||
await giambio.sleep(2)
|
await giambio.sleep(2)
|
||||||
print("[child] Had a nice nap!")
|
print("[child] Had a nice nap!")
|
||||||
# raise TypeError("rip") # Uncomment this line and watch the exception magically propagate!
|
|
||||||
|
|
||||||
|
|
||||||
async def child1():
|
async def child1():
|
||||||
|
@ -61,14 +15,10 @@ async def child1():
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
start = giambio.clock()
|
start = giambio.clock()
|
||||||
try:
|
async with giambio.create_pool() as pool:
|
||||||
async with giambio.create_pool() as pool:
|
pool.spawn(child)
|
||||||
pool.spawn(child)
|
pool.spawn(child1)
|
||||||
pool.spawn(child1)
|
print("[main] Children spawned, awaiting completion")
|
||||||
print("[main] Children spawned, awaiting completion")
|
|
||||||
except Exception as error:
|
|
||||||
# Because exceptions just *work*!
|
|
||||||
print(f"[main] Exception from child caught! {repr(error)}")
|
|
||||||
print(f"[main] Children execution complete in {giambio.clock() - start:.2f} seconds")
|
print(f"[main] Children execution complete in {giambio.clock() - start:.2f} seconds")
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue