Commit 0460c260 authored by Eric Huss's avatar Eric Huss

Rewrite docs for Sphinx and compile into a reference manual.

parent a55712d0
...@@ -5,5 +5,7 @@ build/ ...@@ -5,5 +5,7 @@ build/
coro/_coro.[ch] coro/_coro.[ch]
coro/oserrors.[ch] coro/oserrors.[ch]
coro/clocks/tsc_time.c coro/clocks/tsc_time.c
coro/dns/packet.c
coro/event_queue.cpp
*.pyc *.pyc
*.so *.so
...@@ -20,113 +20,7 @@ ...@@ -20,113 +20,7 @@
# $Header: //prod/main/ap/shrapnel/coro/__init__.py#31 $ # $Header: //prod/main/ap/shrapnel/coro/__init__.py#31 $
"""Coroutine threading library. """Coroutine threading library."""
Introduction
============
Shrapnel is a cooperative threading library.
Getting Started
===============
When your process starts up, you must spawn a thread to do some work, and then
start the event loop. The event loop runs forever processing events until the
process exits. An example::
import coro
def main():
print 'Hello world!'
# This will cause the process to exit.
coro.set_exit(0)
coro.spawn(main)
coro.event_loop()
Coroutines
==========
Every coroutine thread is created with either the `new` function (which does
NOT automatically start the thread) or the `spawn` function (which DOES
automatically start it).
Every thread has a unique numeric ID. You may also set the name of the thread
when you create it.
Timeouts
========
The shrapnel timeout facility allows you to execute a function which will be
interrupted if it does not finish within a specified period of time. The
`coro.TimeoutError` exception will be raised if the timeout expires. See the
`with_timeout` docstring for more detail.
If the event loop is not running (such as in a non-coro process), a custom
version of `with_timeout` is installed that will operate using SIGALRM so that
you may use `with_timeout` in code that needs to run in non-coro processes
(though this is not recommended and should be avoided if possible).
Thread Local Storage
====================
There is a thread-local storage interface available for storing global data that
is thread-specific. You instantiate a `ThreadLocal` instance and you can
assign attributes to it that will be specific to that thread. See the
`ThreadLocal` docs for more detail.
Signal Handlers
===============
By default when you start the event loop, two signal handlers are installed
(for SIGTERM and SIGINT). The default signal handler will exit the event loop.
You can change this behavior by setting `install_signal_handlers` to False
before starting the event loop.
See `coro.signal_handler` for more detail on setting coro signal handlers.
Selfishness
===========
Certain socket operations are allowed to try to execute without blocking if
they are able to (such as send/receiving data on a local socket or on a
high-speed network). However, there is a limit to the number of times a thread
is allowed to do this. The default is 4. The default may be changed
(`set_selfishness`) and the value on a per-thread may be changed
(`coro.coro.set_max_selfish_acts`).
Time
====
Shrapnel uses the `tsc_time` module for handling time. It uses the TSC
value for a stable and high-resolution unit of time. See that module's
documentation for more detail.
A thread is always created when you start the event loop that will
resynchronize the TSC relationship to accomodate any clock drift (see
`tick_updater` and `tsc_time.update_time_relation`).
Exception Notifier
==================
When a thread exits due to an exception, by default a stack trace is printed to
stderr. You may install your own callback to handle this situation. See the
`set_exception_notifier` function for more detail.
Debug Output
============
The shrapnel library provides a mechanism for printing debug information to
stderr. The `print_stderr` function will print a string with a timestamp
and the thread number. The `write_stderr` function writes the string verbatim.
Shrapnel keeps a reference to the "real" stderr (in `saved_stderr`) and the
`print_stderr` and `write_stderr` functions always use the real stderr value. A
particular reason for doing this is the backdoor module replaces sys.stderr and
sys.stdout, but we do not want debug output to go to the interactive session.
Profiling
=========
Shrapnel has its own profiler that is coro-aware. See `coro.profiler` for
details on how to run the profiler.
:Variables:
- `all_threads`: A dictionary of all live coroutine objects. The key is
the coroutine ID, and the value is the coroutine object.
- `saved_stderr`: The actual stderr object for the process. This normally
should not be used. An example of why this exists is because the
backdoor replaces sys.stderr while executing user code.
"""
from coro._coro import * from coro._coro import *
from coro._coro import _yield from coro._coro import _yield
...@@ -166,13 +60,12 @@ set_exception_notifier (default_exception_notifier) ...@@ -166,13 +60,12 @@ set_exception_notifier (default_exception_notifier)
class InParallelError (Exception): class InParallelError (Exception):
"""An error occurred in the `in_parallel` function. """An error occurred in the :func:`in_parallel` function.
:IVariables: :ivar result_list: A list of ``(status, result)`` tuples. ``status`` is
- `result_list`: A list of ``(status, result)`` tuples. ``status`` is either :data:`SUCCESS` or :data:`FAILURE`. For success, the result is the return
either `SUCCESS` or `FAILURE`. For success, the result is the return
value of the function. For failure, it is the output from value of the function. For failure, it is the output from
`sys.exc_info`. ``sys.exc_info``.
""" """
def __init__(self, result_list): def __init__(self, result_list):
...@@ -195,17 +88,14 @@ def in_parallel (fun_arg_list): ...@@ -195,17 +88,14 @@ def in_parallel (fun_arg_list):
This will block until all functions have returned or raised an exception. This will block until all functions have returned or raised an exception.
If one or more functions raises an exception, then the `InParallelError` If one or more functions raises an exception, then the :exc:`InParallelError`
exception will be raised. exception will be raised.
:Parameters: :param fun_arg_list: A list of ``(fun, args)`` tuples.
- `fun_arg_list`: A list of ``(fun, args)`` tuples.
:Return: :returns: A list of return values from the functions.
Returns a list of return values from the functions.
:Exceptions: :raises InParallelError: One or more of the functions raised an exception.
- `InParallelError`: One or more of the functions raised an exception.
""" """
# InParallelError, [(SUCCESS, result0), (FAILURE, exc_info1), ...] # InParallelError, [(SUCCESS, result0), (FAILURE, exc_info1), ...]
...@@ -257,14 +147,11 @@ def tick_updater(): ...@@ -257,14 +147,11 @@ def tick_updater():
def waitpid (pid): def waitpid (pid):
"""Wait for a process to exit. """Wait for a process to exit.
:Parameters: :param pid: The process ID to wait for.
- `pid`: The process ID to wait for.
:Return: :returns: A tuple ``(pid, status)`` of the process.
Returns a tuple ``(pid, status)`` of the process.
:Exceptions: :raises SimultaneousError: Something is already waiting for this process
- `SimultaneousError`: Something is already waiting for this process
ID. ID.
""" """
if UNAME == "Linux": if UNAME == "Linux":
...@@ -290,14 +177,11 @@ def waitpid (pid): ...@@ -290,14 +177,11 @@ def waitpid (pid):
def get_thread_by_id (thread_id): def get_thread_by_id (thread_id):
"""Get a coro thread by ID. """Get a coro thread by ID.
:Parameters: :param thread_id: The thread ID.
- `thread_id`: The thread ID.
:Return: :returns: The coroutine object.
Returns the coroutine object.
:Exceptions: :raises KeyError: The coroutine does not exist.
- `KeyError`: The coroutine does not exist.
""" """
return all_threads[thread_id] return all_threads[thread_id]
...@@ -305,11 +189,9 @@ def where (co): ...@@ -305,11 +189,9 @@ def where (co):
"""Return a string indicating where the given coroutine thread is currently """Return a string indicating where the given coroutine thread is currently
running. running.
:Parameters: :param co: The coroutine object.
- `co`: The coroutine object.
:Return: :returns: A string displaying where the coro thread is currently
Returns a string displaying where the coro thread is currently
executing. executing.
""" """
f = co.get_frame() f = co.get_frame()
...@@ -318,8 +200,7 @@ def where (co): ...@@ -318,8 +200,7 @@ def where (co):
def where_all(): def where_all():
"""Get a dictionary of where all coroutines are currently executing. """Get a dictionary of where all coroutines are currently executing.
:Return: :returns: A dictionary mapping the coroutine ID to a tuple of ``(name,
Returns a dictionary mapping the coroutine ID to a tuple of ``(name,
coro, where)`` where ``where`` is a string representing where the coro, where)`` where ``where`` is a string representing where the
coroutine is currently running. coroutine is currently running.
""" """
...@@ -339,13 +220,11 @@ def spawn (fun, *args, **kwargs): ...@@ -339,13 +220,11 @@ def spawn (fun, *args, **kwargs):
Additional arguments and keyword arguments will be passed to the given function. Additional arguments and keyword arguments will be passed to the given function.
:Parameters: :param fun: The function to call when the coroutine starts.
- `fun`: The function to call when the coroutine starts. :param thread_name: The name of the thread. Defaults to the name of the
- `thread_name`: The name of the thread. Defaults to the name of the
function. function.
:Return: :returns: The new coroutine object.
Returns the new coroutine object.
""" """
if kwargs.has_key('thread_name'): if kwargs.has_key('thread_name'):
thread_name = kwargs['thread_name'] thread_name = kwargs['thread_name']
...@@ -364,13 +243,11 @@ def new (fun, *args, **kwargs): ...@@ -364,13 +243,11 @@ def new (fun, *args, **kwargs):
This will not start the coroutine. Call the ``start`` method on the This will not start the coroutine. Call the ``start`` method on the
coroutine to schedule it to run. coroutine to schedule it to run.
:Parameters: :param fun: The function to call when the coroutine starts.
- `fun`: The function to call when the coroutine starts. :param thread_name: The name of the thread. Defaults to the name of the
- `thread_name`: The name of the thread. Defaults to the name of the
function. function.
:Return: :returns: The new coroutine object.
Returns the new coroutine object.
""" """
if kwargs.has_key('thread_name'): if kwargs.has_key('thread_name'):
thread_name = kwargs['thread_name'] thread_name = kwargs['thread_name']
...@@ -457,8 +334,7 @@ event_loop_is_running = False ...@@ -457,8 +334,7 @@ event_loop_is_running = False
def coro_is_running(): def coro_is_running():
"""Determine if the coro event loop is running. """Determine if the coro event loop is running.
:Return: :returns: True if the event loop is running, otherwise False.
Returns True if the event loop is running, otherwise False.
""" """
return event_loop_is_running return event_loop_is_running
...@@ -468,8 +344,7 @@ def sigterm_handler (*_unused_args): ...@@ -468,8 +344,7 @@ def sigterm_handler (*_unused_args):
def event_loop (timeout=30): def event_loop (timeout=30):
"""Start the event loop. """Start the event loop.
:Parameters: :param timeout: The amount of time to wait for kevent to return
- `timeout`: The amount of time to wait for kevent to return
events. You should probably *not* set this value. events. You should probably *not* set this value.
""" """
global event_loop_is_running, with_timeout, sleep_relative global event_loop_is_running, with_timeout, sleep_relative
......
This diff is collapsed.
...@@ -25,17 +25,18 @@ ...@@ -25,17 +25,18 @@
Introduction Introduction
============ ============
This profiler is coro-aware. It produces output to a binary file on disk. You This profiler is coro-aware. It produces output to a binary file on disk. You
then use the `coro.print_profile` module to convert it to an HTML file. then use the :mod:`coro.print_profile` module to convert it to an HTML file.
Using The Profiler Using The Profiler
================== ==================
There are two ways to run the profiler. One is to use the `go` function where There are two ways to run the profiler. One is to use the
you give it a python function to run. Profiling will start and call the :func:`coro.profiler.go` function where you give it a python function to run.
function, and then the profiler will automatically stop when the function Profiling will start and call the function, and then the profiler will
exits. automatically stop when the function exits.
The other method is to call `start` to start the profiler and `stop` when you The other method is to call :func:`coro.profiler.start` to start the profiler
want to stop profiling. This can be conveniently done from the backdoor. and :func:`coro.profiler.stop` when you want to stop profiling. This can be
conveniently done from the backdoor.
Rendering Output Rendering Output
================ ================
...@@ -49,12 +50,13 @@ Then view the profile output in your web browser. ...@@ -49,12 +50,13 @@ Then view the profile output in your web browser.
Profiler Types Profiler Types
============== ==============
The profiler supports different ways of gathering statistics. This is done by The profiler supports different ways of gathering statistics. This is done by
specifying the "bench" object to use (see `go` and `start`). They default to specifying the "bench" object to use (see :func:`go` and :func:`start`). They
the "rusage" method of gathering statistics about every function call (see the default to the "rusage" method of gathering statistics about every function
getrusage man page for more detail). If you want a higher performance profile, call (see the getrusage man page for more detail). If you want a higher
you can use the `coro.bench` object instead which simply records TSC values for performance profile, you can use the :class:`coro.bench` object instead which
every function call. If you want to define your own method of gathering simply records TSC values for every function call. If you want to define your
statistics, subclass `coro.bench` and implement your own techniques. own method of gathering statistics, subclass :class:`coro.bench` and implement
your own techniques.
""" """
...@@ -98,14 +100,12 @@ def go (fun, *args, **kwargs): ...@@ -98,14 +100,12 @@ def go (fun, *args, **kwargs):
This will display the results to stdout after the function is finished. This will display the results to stdout after the function is finished.
:Parameters: :param fun: The function to call.
- `fun`: The function to call.
:Keywords: :keyword profile_filename: The name of the file to save the profile data.
- `profile_filename`: The name of the file to save the profile data.
Defaults to '/tmp/coro_profile.bin'. Defaults to '/tmp/coro_profile.bin'.
- `profile_bench`: The bench object type to use. Defaults to :keyword profile_bench: The bench object type to use. Defaults to
`coro.rusage_bench`. :class:`coro.rusage_bench`.
""" """
if kwargs.has_key('profile_filename'): if kwargs.has_key('profile_filename'):
profile_filename = kwargs['profile_filename'] profile_filename = kwargs['profile_filename']
......
This diff is collapsed.
This diff is collapsed.
...@@ -91,12 +91,12 @@ pygments_style = 'sphinx' ...@@ -91,12 +91,12 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
html_theme = 'sphinxdoc' html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme # Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the # further. For a list of options available for each theme, see the
# documentation. # documentation.
#html_theme_options = {} html_theme_options = {'collapsiblesidebar': True}
# Add any paths that contain custom themes here, relative to this directory. # Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = [] #html_theme_path = []
...@@ -240,3 +240,5 @@ texinfo_documents = [ ...@@ -240,3 +240,5 @@ texinfo_documents = [
# How to display URL addresses: 'footnote', 'no', or 'inline'. # How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote' #texinfo_show_urls = 'footnote'
autodoc_default_flags = ['members', 'show-inheritance']
...@@ -8,11 +8,9 @@ Welcome to Shrapnel's documentation! ...@@ -8,11 +8,9 @@ Welcome to Shrapnel's documentation!
Contents: Contents:
.. toctree:: * :doc:`Installation <installation>`
:maxdepth: 1 * :doc:`Tutorial <tutorial>`
* :doc:`Reference Manual <ref/index>`
tutorial
Indices and tables Indices and tables
......
============
Installation
============
Supported Platforms
===================
Shrapnel currently works on FreeBSD, Linux, and Mac OS X with x86 32- or 64-bit platforms.
It supports Python 2.7 (TODO: and 2.6?).
Prerequisites
=============
pip
---
To make installation easy, you can use `pip <http://www.pip-installer.org/>`_.
This is a tool which will fetch Python packages from `PyPi
<http://pypi.python.org/>`_ and install them.
Visit http://www.pip-installer.org/en/latest/installing.html for information
on how to install pip if you don't already have it installed.
Cython
------
You need version 0.12.1 or newer of `Cython <http://cython.org/>`_. If you
already have Cython installed, you can check your current version by running
``cython -V``.
To install Cython, run:
pip install cython
Distribute
----------
You need version 0.6.16 or newer of `distribute <http://pypi.python.org/pypi/distribute>`_.
Distribute is a build and packaging tool for Python (a replacement for setuptools).
To install distribute, run:
pip install distribute
Shrapnel
--------
Finally, you can install Shrapnel, run:
pip install shrapnel
Alternatively you can download it from https://github.com/ironport/shrapnel
and do the usual ``python setup.py install`` procedure.
======
Clocks
======
Shrapnel needs to keep track of time to manage scheduling of sleeps and
timeouts. Because Shrapnel is intended to support thousands of coroutines,
and each coroutine may be making many timeout calls per second, Shrapnel needs
to use a timing facility that is relatively high performance. It also needs
one that is monotonic, so it does not need to deal with system clock changes.
The ``clocks`` subpackage is intended to provide a variety of different time
facilities. Currently it only supports using the x86 TSC timer. This is a
timer built in to the CPU, and thus is very fast.
TSC Time
========
Support for TSC time is implemented in the ``coro.clocks.tsc_time`` module.
.. automodule:: coro.clocks.tsc_time
==========
Coroutines
==========
The central concept of Shrapnel is the coroutine. You can think of a coroutine
like it is a thread. When it runs out of work to do, it yields and allows other
coroutines to run. Scheduling of coroutines is handled by the scheduler which
runs an "event loop".
Event Loop
==========
The event loop is a loop that runs forever until the program ends. Every
Shrapnel program needs to start the event loop as one of the first things it
does. A typical example would be::
import coro
def main():
print 'Hello world!'
# This will cause the process to exit.
coro.set_exit(0)
if __name__ == '__main__':
coro.spawn(main)
coro.event_loop()
Coroutines
==========
Every coroutine thread is created with either the :func:`new` function (which
does NOT automatically start the thread) or the :func:`spawn` function (which
DOES automatically start it).
Every thread has a unique numeric ID. You may also set the name of the thread
when you create it.
.. autoclass:: coro.coro
Timeouts
========
The shrapnel timeout facility allows you to execute a function which will be
interrupted if it does not finish within a specified period of time. The
:class:`TimeoutError` exception will be raised if the timeout expires. See the
:func:`with_timeout` docstring for more detail.
If the event loop is not running (such as in a non-coro process), a custom
version of `with_timeout` is installed that will operate using SIGALRM so that
you may use `with_timeout` in code that needs to run in non-coro processes
(though this is not recommended and should be avoided if possible).
.. autofunction:: coro.with_timeout
Parallel Execution
==================
XXX
.. autofunction:: coro.in_parallel
.. autoexception:: coro.InParallelError
Thread Local Storage
====================
There is a thread-local storage interface available for storing global data that
is thread-specific. You instantiate a :class:`ThreadLocal` instance and you can
assign attributes to it that will be specific to that thread. From a design
perspective, it is generally discouraged to use thread-local storage. But
nonetheless, it can be useful at times.
.. autoclass:: coro.ThreadLocal
Functions
=========
The coro module defines the following functions:
.. autofunction:: coro.get_thread_by_id
.. autofunction:: coro.coro_is_running
.. autofunction:: coro.event_loop
.. autofunction:: coro.new
.. autofunction:: coro.spawn
.. autofunction:: coro.waitpid
.. autofunction:: coro.yield_slice
.. autofunction:: coro.schedule
.. autofunction:: coro.current
.. autofunction:: coro.set_exit
.. autofunction:: coro.set_print_exit_string
.. autofunction:: coro.sleep_absolute
.. autofunction:: coro.sleep_relative
Variables
=========
.. py:data:: coro.all_threads
A dictionary of all live coroutine objects. The key is the coroutine ID,
and the value is the coroutine object.
Exceptions
==========
The coro module defines the following exceptions:
.. autoexception:: coro.ScheduleError
.. autoexception:: coro.DeadCoroutine
.. autoexception:: coro.ClosedError
.. autoexception:: coro.NotStartedError
.. autoexception:: coro.TimeoutError
.. autoexception:: coro.SimultaneousError
.. autoexception:: coro.Shutdown
.. autoexception:: coro.WakeUp
=========
Debugging
=========
There are a variety of features available to help with debugging in Shrapnel.
Backdoor
========
A very powerful feature of Shrapnel is the ability to access a running process
via a backdoor. You can telnet to a socket (typically a unix-domain socket)
and get a Python prompt. At this point, you can interact with anything in
your Shrapnel process.
As an example of something you can do in the backdoor is call
:func:`coro.where_all`. This will return a dictionary of every coroutine that
is running with a string describing the call stack of where that coroutine is
currently blocked.
To enable the backdoor, you typically start a backdoor coroutine before starting
the event loop with the following code:
.. sourcecode:: python
import coro.backdoor
coro.spawn(coro.backdoor.serve)
By default this will listen on all IP's on the lowest port available from 8023
to 8033. This isn't a very safe or secure thing to do. It's best to specify a
unix-domain socket with the ``unix_path`` parameter. See
:func:`coro.backdoor.serve` for details.
By default, the globals available in a backdoor session is a copy of the
globals from your applications ``__main__`` module.
.. autofunction:: coro.backdoor.serve
Stderr Output
=============
Shrapnel provides some functions for printing debug information to stderr. The
:func:`coro.print_stderr` function will print a string with a timestamp and
the thread number. The :func:`coro.write_stderr` function writes the string
verbatim with no newline.
Shrapnel keeps a reference to the "real" stderr (in ``saved_stderr``) and the
``print_stderr`` and ``write_stderr`` functions always use the real stderr
value. A particular reason for doing this is the backdoor module replaces
sys.stderr and sys.stdout, but we do not want debug output to go to the
interactive session.
.. autofunction:: coro.write_stderr
.. autofunction:: coro.print_stderr
Exceptions
==========
Tracebacks
----------
As a convenience, Shrapnel has a module for printing stack traces in a
condensed format. The ``coro.tb`` module has the :func:`coro.tb.stack_string`
function for printing the current stack, and :func:`coro.tb.traceback_string`
for getting a traceback in an exception handler.
.. autofunction:: coro.tb.stack_string
.. autofunction:: coro.tb.traceback_string
Exception Notifications
-----------------------
If an exception is raised in a coroutine and is never caught, then Shrapnel
will by default display the exception to stderr. If you want to change this
behavior, use :func:`coro.set_exception_notifier`.
.. autofunction:: coro.set_exception_notifier
Latency
=======
Shrapnel will keep track of how long a coroutine runs before it yields.
This is helpful to track down coroutines which are running for too long, or are
potentially calling blocking calls. Here is an example of the output that would
be sent to stderr when this happens::
Sat Apr 14 20:55:39 2012 High Latency: (3.884s)
for <coro #1 name='<function my_func at 0x800fd32a8>'
dead=0 started=1 scheduled=0 at 0x801424720>
You can change the threshold that will trigger this warning with the
:func:`coro.set_latency_warning` function. However, doing this to silence
warnings isn't a good idea. It is best to fix whatever code is causing the
warnings. You can either call :func:`coro.yield_slice` periodically to let
other coroutines run, or make sure you are not calling any blocking
operations.
.. autofunction:: coro.set_latency_warning
Functions
=========
The ``coro`` module defines the following functions:
.. autofunction:: coro.where
.. autofunction:: coro.where_all
.. autofunction:: coro.get_live_coros
===
DNS
===
TODO
.. autofunction:: coro.set_resolver
=========
Emulation
=========
Because Shrapnel is essentially its own threading system, code written with
the intention of using Python's standard threads will not work. Things like
Python's socket class will block and hang the entire program. To solve this
problem, Shrapnel includes some code that will monkeypatch some of Python's
standard classes to work with Shrapnel. You must manually enable this
behavior by calling :func:`coro.install_thread_emulation`.
.. autofunction:: coro.install_thread_emulation
*************************
Shrapnel Reference Manaul
*************************
This reference manual describes all of the basic concepts of Shrapnel along with all of the APIs.
.. toctree::
:maxdepth: 1
:numbered:
coroutines.rst
sockets.rst
synchronization.rst
clocks.rst
dns.rst
emulation.rst
debugging.rst
signals.rst
selfishness.rst
profiling.rst
oserrors.rst
========
OSErrors
========
As a convenience, Shrapnel wraps all OSError exceptions that it raises with a
subclass that is specific to the errno code. For example, an OSError with an
errno of ENOENT will be raised as the ENOENT exception. All exceptions derive
from OSError, so it is compatible with regular OSError handling.
All of the exceptions are defined in the ``coro.oserrors`` module.
For example, instead of doing this:
.. sourcecode:: python
try:
data = sock.recv(1024)
except OSError, e:
if e.errno == errno.ECONNRESET:
# Handle connection reset.
else:
# Handle other unknown error.
You can do this:
.. sourcecode:: python
try:
data = sock.recv(1024):
except ECONNRESET:
# Handle connection reset.
Profiling
=========
.. automodule:: coro.profiler
===========
Selfishness
===========
Shrapnel maintains a concept called "selfishness". This mechanism is used to
prevent a coroutine from yielding too often (or from running for too long).
This is currently only relevant to socket objects and socket I/O.
Each coroutine is given a set number of "free passes" each time it tries to do
I/O on a socket. If there is data immediately available on the socket, then
the coroutine may immediately receive that data. If Shrapnel did not
implement any "selfishness" limits, and that coroutine is in a loop repeatedly
calling ``read`` and there is always data available to the socket, then that
coroutine would run continuously without letting its fellow coroutines a
chance to run.
By default, every coroutine has a selfishness limit of 4. That means it is
allowed to do 4 I/O operations before it is forced to yield. Of course, if it
attempts to do an I/O operation that would block (such as if there is no data
available on a socket), then it will yield immediately.
You can set the default selfishness limit for all new coroutines with the
:func:`coro.set_selfishness` function. You can also change a coroutine's
limit with the :meth:`coro.coro.set_max_selfish_acts` method.
Functions
=========
The following functions are available in the ``coro`` module:
.. autofunction:: coro.set_selfishness
=======
Signals
=======
Shrapnel provides a way to handle signals. Youn can register a function to
receive signals with :func:`coro.signal_handler.register`.
By default when you start the event loop, two signal handlers are installed
(for SIGTERM and SIGINT). The default signal handler will exit the event loop.
You can change this behavior by setting ``coro.install_signal_handlers`` to False
before starting the event loop.
Additionally, there is a signal handler installed for SIGINFO. It prints the
name of the coroutine that is currently running. On a typical terminal, you
can trigger this with CTRL-T.
.. autofunction:: coro.signal_handler.register
=======
Sockets
=======
Most Shrapnel programs make heavy use of sockets. The ``coro`` package
implements its own socket class, which is nearly identical to the socket class
in Python. Indeed, if you use :func:`coro.install_thread_emulation` then the
socket class will be monkey-patched into Python's socket module.
Creating Sockets
================
Though you are free to directly instantiate the :class:`coro.sock` object, there are
a variety of functions to assist in creating socket objects with a little more clarity.
.. autofunction:: coro.tcp6_sock
.. autofunction:: coro.tcp_sock
.. autofunction:: coro.udp6_sock
.. autofunction:: coro.udp_sock
.. autofunction:: coro.unix_sock
.. autofunction:: coro.socketpair
.. autofunction:: coro.has_ipv6
Socket Classes
==============
.. autoclass:: coro.sock
.. autoclass:: coro.file_sock
.. autoclass:: coro.fd_sock
Socket Functions
================
The coro module offers the following functions related to sockets.
.. autofunction:: coro.get_live_sockets
Socket Constants
================
The following classes provide a variety of constants often used with sockets.
.. autoclass:: coro.AF
.. autoclass:: coro.PF
.. autoclass:: coro.SHUT
.. autoclass:: coro.SO
.. autoclass:: coro.SOCK
.. autoclass:: coro.SOL
===============
Synchronization
===============
You typically do not need to use synchronization primitives with Shrapnel
because coroutines are cooperative. However, there are situations where they
can be useful. For example, if you manipulate multiple shared data structures
that need to remain consistent, and you have potentially context-switch calls
interspersed (such as socket I/O).
Synchronization Classes
=======================
.. autoclass:: coro.condition_variable
.. autoclass:: coro.fifo
.. autoclass:: coro.inverted_semaphore
.. autoclass:: coro.LockError
.. autoclass:: coro.mutex
.. autoclass:: coro.semaphore
...@@ -111,7 +111,7 @@ setup ( ...@@ -111,7 +111,7 @@ setup (
], ],
packages=['coro', 'coro.clocks', 'coro.http', 'coro.dns'], packages=['coro', 'coro.clocks', 'coro.http', 'coro.dns'],
package_dir = { package_dir = {
'': 'coroutine', # '': 'coroutine',
'coro': 'coro', 'coro': 'coro',
'coro.clocks': 'coro/clocks', 'coro.clocks': 'coro/clocks',
'coro.dns': 'coro/dns', 'coro.dns': 'coro/dns',
......
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