diff -r 960a73507d4d Lib/asyncio/futures.py --- a/Lib/asyncio/futures.py Sat Sep 10 19:05:32 2016 -0700 +++ b/Lib/asyncio/futures.py Sun Sep 11 12:34:25 2016 +0900 @@ -120,29 +120,7 @@ return getattr(obj, '_asyncio_future_blocking', None) is not None -class Future: - """This class is *almost* compatible with concurrent.futures.Future. - - Differences: - - - result() and exception() do not take a timeout argument and - raise an exception when the future isn't done yet. - - - Callbacks registered with add_done_callback() are always called - via the event loop's call_soon_threadsafe(). - - - This class is not compatible with the wait() and as_completed() - methods in the concurrent.futures package. - - (In Python 3.4 or later we may be able to unify the implementations.) - """ - - # Class variables serving as defaults for instance variables. - _state = _PENDING - _result = None - _exception = None - _loop = None - _source_traceback = None +class _BaseFuture: # This field is used for a dual purpose: # - Its presence is a marker to declare that a class implements @@ -154,24 +132,6 @@ # `yield Future()` (incorrect). _asyncio_future_blocking = False - _log_traceback = False # Used for Python 3.4 and later - _tb_logger = None # Used for Python 3.3 only - - def __init__(self, *, loop=None): - """Initialize the future. - - The optional event_loop argument allows explicitly setting the event - loop object used by the future. If it's not provided, the future uses - the default event loop. - """ - if loop is None: - self._loop = events.get_event_loop() - else: - self._loop = loop - self._callbacks = [] - if self._loop.get_debug(): - self._source_traceback = traceback.extract_stack(sys._getframe(1)) - def __format_callbacks(self): cb = self._callbacks size = len(cb) @@ -232,6 +192,52 @@ context['source_traceback'] = self._source_traceback self._loop.call_exception_handler(context) + +class Future(_BaseFuture): + """This class is *almost* compatible with concurrent.futures.Future. + + Differences: + + - result() and exception() do not take a timeout argument and + raise an exception when the future isn't done yet. + + - Callbacks registered with add_done_callback() are always called + via the event loop's call_soon_threadsafe(). + + - This class is not compatible with the wait() and as_completed() + methods in the concurrent.futures package. + + (In Python 3.4 or later we may be able to unify the implementations.) + """ + + # Class variables serving as defaults for instance variables. + _state = _PENDING + _result = None + _exception = None + _loop = None + _source_traceback = None + + _blocking = False # proper use of future (yield vs yield from) + + _log_traceback = False # Used for Python 3.4 and later + _tb_logger = None # Used for Python 3.3 only + + def __init__(self, *, loop=None): + """Initialize the future. + + The optional event_loop argument allows explicitly setting the event + loop object used by the future. If it's not provided, the future uses + the default event loop. + """ + if loop is None: + self._loop = events.get_event_loop() + else: + self._loop = loop + self._callbacks = [] + if self._loop.get_debug(): + self._source_traceback = traceback.extract_stack(sys._getframe(1)) + + def cancel(self): """Cancel the future and schedule callbacks. @@ -426,6 +432,21 @@ dest.set_result(result) +try: + import _futures +except ImportError: + pass +else: + class Future(_BaseFuture, _futures.Future): + pass + + _futures._init_module( + traceback.extract_stack, + events.get_event_loop, + InvalidStateError, + CancelledError) + + def _chain_future(source, destination): """Chain two futures so that when one completes, so does the other. diff -r 960a73507d4d Modules/Setup.dist --- a/Modules/Setup.dist Sat Sep 10 19:05:32 2016 -0700 +++ b/Modules/Setup.dist Sun Sep 11 12:34:25 2016 +0900 @@ -181,6 +181,7 @@ #_datetime _datetimemodule.c # datetime accelerator #_bisect _bisectmodule.c # Bisection algorithms #_heapq _heapqmodule.c # Heap queue algorithm +#_futures _futuresmodule.c # Fast asyncio Future #unicodedata unicodedata.c # static Unicode character database diff -r 960a73507d4d Modules/_futuresmodule.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Modules/_futuresmodule.c Sun Sep 11 12:34:25 2016 +0900 @@ -0,0 +1,977 @@ +#include "Python.h" +#include "structmember.h" + + +/* identifiers used from some functions */ +_Py_IDENTIFIER(call_soon); + + +/* State of the _futures module */ +typedef struct { + int ready; + + PyObject *traceback_extract_stack; + PyObject *asyncio_get_event_loop; + PyObject *asyncio_InvalidStateError; + PyObject *asyncio_CancelledError; +} FuturesModState; + + +/* Forward declaration of the _futures module definition. */ +static struct PyModuleDef _futuresmodule; + +/* Get FutureIter from Future */ +static PyObject* future_iter(PyObject *fut); + +/* Given a module object, get its per-module state. */ +static FuturesModState * +_FuturesMod_GetState(PyObject *module) +{ + return (FuturesModState *) PyModule_GetState(module); +} + + +/* Find the module instance imported in the currently running sub-interpreter + and get the module's state. */ +static FuturesModState * +_FuturesMod_GetGlobalState(void) +{ + PyObject *m = PyState_FindModule(&_futuresmodule); + if (m == NULL) + return NULL; + return _FuturesMod_GetState(m); +} + + +/* Get the global module state and make sure it's initialized + and ready to be used. */ +static FuturesModState * +_FuturesMod_EnsureState(void) +{ + FuturesModState *st = _FuturesMod_GetGlobalState(); + + if (st == NULL) + goto error; + + if (!st->ready) + goto error; + + return st; + + error: + PyErr_SetString(PyExc_RuntimeError, + "_futures module wasn't properly initialized"); + return NULL; +} + + +/* Clear the given _futures module state. */ +static void +_FuturesMod_ClearState(FuturesModState *st) +{ + st->ready = 0; + Py_CLEAR(st->traceback_extract_stack); + Py_CLEAR(st->asyncio_get_event_loop); + Py_CLEAR(st->asyncio_InvalidStateError); + Py_CLEAR(st->asyncio_CancelledError); +} + + +/* Initialize the given pickle module state. */ +static int +_FuturesMod_InitState(PyObject *module) +{ + FuturesModState *st; + + st = _FuturesMod_GetState(module); + if (st == NULL) + return -1; + + st->ready = 0; + st->traceback_extract_stack = NULL; + st->asyncio_get_event_loop = NULL; + st->asyncio_InvalidStateError = NULL; + st->asyncio_CancelledError = NULL; + return 0; +} + + +typedef enum { + STATE_PENDING, + STATE_CANCELLED, + STATE_FINISHED +} fut_state; + + +typedef struct { + PyObject_HEAD + FuturesModState *fut_modstate; + PyObject *fut_loop; + PyObject *fut_callbacks; + PyObject *fut_exception; + PyObject *fut_result; + PyObject *fut_source_tb; + fut_state fut_state; + int fut_log_tb; + int fut_blocking; + PyObject *dict; + PyObject *fut_weakreflist; +} FutureObj; + + +static int +_schedule_callbacks(FutureObj *fut) +{ + Py_ssize_t len; + PyObject* iters; + int i; + + if (fut->fut_callbacks == NULL) { + PyErr_SetString(PyExc_RuntimeError, "NULL callbacks"); + return -1; + } + + len = PyList_GET_SIZE(fut->fut_callbacks); + if (len == 0) + return 0; + + iters = PyList_GetSlice(fut->fut_callbacks, 0, len); + if (iters == NULL) + return -1; + if (PyList_SetSlice(fut->fut_callbacks, 0, len, NULL) < 0) { + Py_DECREF(iters); + return -1; + } + + for (i = 0; i < len; i++) { + PyObject *handle = NULL; + PyObject *cb = PyList_GET_ITEM(iters, i); + + handle = _PyObject_CallMethodId( + fut->fut_loop, &PyId_call_soon, "OO", cb, fut, NULL); + + if (handle == NULL) { + Py_DECREF(iters); + return -1; + } else { + Py_DECREF(handle); + } + } + + Py_DECREF(iters); + return 0; +} + +static int +FutureObj_init(FutureObj *fut, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"loop", NULL}; + PyObject *loop = NULL; + PyObject *res = NULL; + FuturesModState *st; + _Py_IDENTIFIER(get_debug); + + st = _FuturesMod_EnsureState(); + if (st == NULL) + return -1; + fut->fut_modstate = st; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|$O", kwlist, &loop)) + return -1; + if (loop == NULL || loop == Py_None) { + loop = PyObject_CallObject(fut->fut_modstate->asyncio_get_event_loop, + NULL); + if (loop == NULL) + return -1; + } else { + Py_INCREF(loop); + } + Py_CLEAR(fut->fut_loop); + fut->fut_loop = loop; + + res = _PyObject_CallMethodId(fut->fut_loop, &PyId_get_debug, "()", NULL); + if (res == NULL) + return -1; + if (PyObject_IsTrue(res)) { + Py_CLEAR(res); + fut->fut_source_tb = PyObject_CallObject( + fut->fut_modstate->traceback_extract_stack, NULL); + if (fut->fut_source_tb == NULL) + return -1; + } else { + Py_CLEAR(res); + } + + fut->fut_callbacks = PyList_New(0); + if (fut->fut_callbacks == NULL) + return -1; + + return 0; +} + +static int +FutureObj_clear(FutureObj *fut) +{ + Py_CLEAR(fut->fut_loop); + Py_CLEAR(fut->fut_callbacks); + Py_CLEAR(fut->fut_result); + Py_CLEAR(fut->fut_exception); + Py_CLEAR(fut->fut_source_tb); + Py_CLEAR(fut->dict); + return 0; +} + +static void +FutureObj_dealloc(FutureObj *fut) +{ + FutureObj_clear(fut); + + if (fut->fut_weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *)fut); + + fut->fut_modstate = NULL; + Py_TYPE(fut)->tp_free(fut); +} + +static int +FutureObj_traverse(FutureObj *fut, visitproc visit, void *arg) +{ + Py_VISIT(fut->fut_loop); + Py_VISIT(fut->fut_callbacks); + Py_VISIT(fut->fut_result); + Py_VISIT(fut->fut_exception); + Py_VISIT(fut->fut_source_tb); + Py_VISIT(fut->dict); + return 0; +} + +PyDoc_STRVAR(pydoc_result, + "Return the result this future represents.\n" + "\n" + "If the future has been cancelled, raises CancelledError. If the\n" + "future's result isn't yet available, raises InvalidStateError. If\n" + "the future is done and has an exception set, this exception is raised." +); + +static PyObject * +FutureObj_result(FutureObj *fut, PyObject *arg) +{ + if (fut->fut_state == STATE_CANCELLED) { + PyErr_SetString(fut->fut_modstate->asyncio_CancelledError, ""); + return NULL; + } + + if (fut->fut_state != STATE_FINISHED) { + PyErr_SetString(fut->fut_modstate->asyncio_InvalidStateError, + "Result is not ready."); + return NULL; + } + + fut->fut_log_tb = 0; + if (fut->fut_exception != NULL) { + PyObject *type = NULL; + type = PyExceptionInstance_Class(fut->fut_exception); + PyErr_SetObject(type, fut->fut_exception); + return NULL; + } + + Py_INCREF(fut->fut_result); + return fut->fut_result; +} + +PyDoc_STRVAR(pydoc_exception, + "Return the exception that was set on this future.\n" + "\n" + "The exception (or None if no exception was set) is returned only if\n" + "the future is done. If the future has been cancelled, raises\n" + "CancelledError. If the future isn't done yet, raises\n" + "InvalidStateError." +); + +static PyObject * +FutureObj_exception(FutureObj *fut, PyObject *arg) +{ + FuturesModState *st; + + st = _FuturesMod_EnsureState(); + if (st == NULL) + return NULL; + + if (fut->fut_state == STATE_CANCELLED) { + PyErr_SetString(fut->fut_modstate->asyncio_CancelledError, ""); + return NULL; + } + + if (fut->fut_state != STATE_FINISHED) { + PyErr_SetString(fut->fut_modstate->asyncio_InvalidStateError, + "Result is not ready."); + return NULL; + } + + if (fut->fut_exception != NULL) { + fut->fut_log_tb = 0; + Py_INCREF(fut->fut_exception); + return fut->fut_exception; + } + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pydoc_set_result, + "Mark the future done and set its result.\n" + "\n" + "If the future is already done when this method is called, raises\n" + "InvalidStateError." +); + +static PyObject * +FutureObj_set_result(FutureObj *fut, PyObject *res) +{ + FuturesModState *st; + + st = _FuturesMod_EnsureState(); + if (st == NULL) + return NULL; + + if (fut->fut_state != STATE_PENDING) { + PyErr_SetString(fut->fut_modstate->asyncio_InvalidStateError, + "invalid state"); + return NULL; + } + + Py_INCREF(res); + fut->fut_result = res; + fut->fut_state = STATE_FINISHED; + + if (_schedule_callbacks(fut) == -1) + return NULL; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pydoc_set_exception, + "Mark the future done and set an exception.\n" + "\n" + "If the future is already done when this method is called, raises\n" + "InvalidStateError." +); + +static PyObject * +FutureObj_set_exception(FutureObj *fut, PyObject *exc) +{ + FuturesModState *st; + PyObject *exc_val = NULL; + + st = _FuturesMod_EnsureState(); + if (st == NULL) + return NULL; + + if (fut->fut_state != STATE_PENDING) { + PyErr_SetString(fut->fut_modstate->asyncio_InvalidStateError, + "invalid state"); + return NULL; + } + + if (PyExceptionClass_Check(exc)) { + exc_val = PyObject_CallObject(exc, NULL); + if (exc_val == NULL) + return NULL; + } + else { + exc_val = exc; + Py_INCREF(exc_val); + } + if (!PyExceptionInstance_Check(exc_val)) { + Py_DECREF(exc_val); + PyErr_SetString(PyExc_TypeError, "invalid exception object"); + return NULL; + } + if ((PyObject*)Py_TYPE(exc_val) == PyExc_StopIteration) { + Py_DECREF(exc_val); + PyErr_SetString(PyExc_TypeError, + "StopIteration interacts badly with generators " + "and cannot be raised into a Future"); + return NULL; + } + + fut->fut_exception = exc_val; + fut->fut_state = STATE_FINISHED; + + if (_schedule_callbacks(fut) == -1) + return NULL; + + fut->fut_log_tb = 1; + + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pydoc_add_done_callback, + "Add a callback to be run when the future becomes done.\n" + "\n" + "The callback is called with a single argument - the future object. If\n" + "the future is already done when this is called, the callback is\n" + "scheduled with call_soon."; +); + +static PyObject * +FutureObj_add_done_callback(FutureObj *fut, PyObject *arg) +{ + if (fut->fut_state != STATE_PENDING) { + PyObject *handle = _PyObject_CallMethodId( + fut->fut_loop, &PyId_call_soon, "OO", arg, fut, NULL); + + if (handle == NULL) + return NULL; + else + Py_DECREF(handle); + } else { + int err = PyList_Append(fut->fut_callbacks, arg); + if (err != 0) { + return NULL; + } + } + Py_RETURN_NONE; +} + +PyDoc_STRVAR(pydoc_remove_done_callback, + "Remove all instances of a callback from the \"call when done\" list.\n" + "\n" + "Returns the number of callbacks removed." +); + +static PyObject * +FutureObj_remove_done_callback(FutureObj *fut, PyObject *arg) +{ + PyObject *newlist; + Py_ssize_t len, i, j=0; + + len = PyList_GET_SIZE(fut->fut_callbacks); + if (len == 0) { + return PyLong_FromSsize_t(0); + } + + newlist = PyList_New(len); + if (newlist == NULL) { + return NULL; + } + + for (i = 0; i < len; i++) { + int ret; + PyObject *item = PyList_GET_ITEM(fut->fut_callbacks, i); + + if ((ret = PyObject_RichCompareBool(arg, item, Py_EQ)) < 0) { + goto fail; + } + if (ret == 0) { + Py_INCREF(item); + PyList_SET_ITEM(newlist, j, item); + j++; + } + } + + if (PyList_SetSlice(newlist, j, len, NULL) < 0) { + goto fail; + } + if (PyList_SetSlice(fut->fut_callbacks, 0, len, newlist) < 0) { + goto fail; + } + Py_DECREF(newlist); + return PyLong_FromSsize_t(len - j); + +fail: + Py_DECREF(newlist); + return NULL; +} + +PyDoc_STRVAR(pydoc_cancel, + "Cancel the future and schedule callbacks.\n" + "\n" + "If the future is already done or cancelled, return False. Otherwise,\n" + "change the future's state to cancelled, schedule the callbacks and\n" + "return True." +); + +static PyObject * +FutureObj_cancel(FutureObj *fut, PyObject *arg) +{ + if (fut->fut_state != STATE_PENDING) { + Py_RETURN_FALSE; + } + fut->fut_state = STATE_CANCELLED; + + if (_schedule_callbacks(fut) == -1) { + return NULL; + } + + Py_RETURN_TRUE; +} + +PyDoc_STRVAR(pydoc_cancelled, "Return True if the future was cancelled."); + +static PyObject * +FutureObj_cancelled(FutureObj *fut, PyObject *arg) +{ + if (fut->fut_state == STATE_CANCELLED) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +PyDoc_STRVAR(pydoc_done, + "Return True if the future is done.\n" + "\n" + "Done means either that a result / exception are available, or that the\n" + "future was cancelled." +); + +static PyObject * +FutureObj_done(FutureObj *fut, PyObject *arg) +{ + if (fut->fut_state == STATE_PENDING) { + Py_RETURN_FALSE; + } else { + Py_RETURN_TRUE; + } +} + +static PyObject * +FutureObj_get_blocking(FutureObj *fut) +{ + if (fut->fut_blocking) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +static int +FutureObj_set_blocking(FutureObj *fut, PyObject *val) +{ + int is_true = PyObject_IsTrue(val); + if (is_true < 0) + return -1; + + fut->fut_blocking = is_true; + return 0; +} + +static PyObject * +FutureObj_get_log_traceback(FutureObj *fut) +{ + if (fut->fut_log_tb) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +static PyObject * +FutureObj_get_loop(FutureObj *fut) +{ + if (fut->fut_loop == NULL) + Py_RETURN_NONE; + + Py_INCREF(fut->fut_loop); + return fut->fut_loop; +} + +static PyObject * +FutureObj_get_callbacks(FutureObj *fut) +{ + if (fut->fut_callbacks == NULL) + Py_RETURN_NONE; + + Py_INCREF(fut->fut_callbacks); + return fut->fut_callbacks; +} + +static PyObject * +FutureObj_get_result(FutureObj *fut) +{ + if (fut->fut_result == NULL) + Py_RETURN_NONE; + + Py_INCREF(fut->fut_result); + return fut->fut_result; +} + +static PyObject * +FutureObj_get_exception(FutureObj *fut) +{ + if (fut->fut_exception == NULL) + Py_RETURN_NONE; + + Py_INCREF(fut->fut_exception); + return fut->fut_exception; +} + +static PyObject * +FutureObj_get_source_traceback(FutureObj *fut) +{ + if (fut->fut_source_tb == NULL) + Py_RETURN_NONE; + + Py_INCREF(fut->fut_source_tb); + return fut->fut_source_tb; +} + +static PyObject * +FutureObj_get_state(FutureObj *fut) +{ + _Py_IDENTIFIER(PENDING); + _Py_IDENTIFIER(CANCELLED); + _Py_IDENTIFIER(FINISHED); + PyObject *ret = NULL; + + switch (fut->fut_state) { + case STATE_PENDING: + ret = _PyUnicode_FromId(&PyId_PENDING); + break; + case STATE_CANCELLED: + ret = _PyUnicode_FromId(&PyId_CANCELLED); + break; + case STATE_FINISHED: + ret = _PyUnicode_FromId(&PyId_FINISHED); + break; + default: + assert (0); + } + Py_INCREF(ret); + return ret; +} + +static PyAsyncMethods FutureType_as_async = { + (unaryfunc)future_iter, /* am_await */ + 0, /* am_aiter */ + 0 /* am_anext */ +}; + +static PyMethodDef FutureType_methods[] = { + {"add_done_callback", + (PyCFunction)FutureObj_add_done_callback, + METH_O, pydoc_add_done_callback}, + {"remove_done_callback", + (PyCFunction)FutureObj_remove_done_callback, + METH_O, pydoc_remove_done_callback}, + {"set_result", + (PyCFunction)FutureObj_set_result, METH_O, pydoc_set_result}, + {"set_exception", + (PyCFunction)FutureObj_set_exception, METH_O, pydoc_set_exception}, + {"cancel", (PyCFunction)FutureObj_cancel, METH_NOARGS, pydoc_cancel}, + {"cancelled", + (PyCFunction)FutureObj_cancelled, METH_NOARGS, pydoc_cancelled}, + {"done", (PyCFunction)FutureObj_done, METH_NOARGS, pydoc_done}, + {"result", (PyCFunction)FutureObj_result, METH_NOARGS, pydoc_result}, + {"exception", + (PyCFunction)FutureObj_exception, METH_NOARGS, pydoc_exception}, + {NULL, NULL} /* Sentinel */ +}; + +static PyGetSetDef FutureType_getsetlist[] = { + {"_state", (getter)FutureObj_get_state, NULL, NULL}, + {"_blocking", (getter)FutureObj_get_blocking, + (setter)FutureObj_set_blocking, NULL}, + {"_loop", (getter)FutureObj_get_loop, NULL, NULL}, + {"_callbacks", (getter)FutureObj_get_callbacks, NULL, NULL}, + {"_result", (getter)FutureObj_get_result, NULL, NULL}, + {"_exception", (getter)FutureObj_get_exception, NULL, NULL}, + {"_log_traceback", (getter)FutureObj_get_log_traceback, NULL, NULL}, + {"_source_traceback", (getter)FutureObj_get_source_traceback, NULL, NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject FutureType = { + PyVarObject_HEAD_INIT(0, 0) + "_futures.Future", + sizeof(FutureObj), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)FutureObj_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + &FutureType_as_async, /* tp_as_async */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + 0, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC + | Py_TPFLAGS_BASETYPE, /* tp_flags */ + "Fast asyncio.Future implementation.", /* tp_doc */ + (traverseproc)FutureObj_traverse, /* tp_traverse */ + (inquiry)FutureObj_clear, /* tp_clear */ + 0, /* tp_richcompare */ + offsetof(FutureObj, fut_weakreflist), /* tp_weaklistoffset */ + (getiterfunc)future_iter, /* tp_iter */ + 0, /* tp_iternext */ + FutureType_methods, /* tp_methods */ + 0, /* tp_members */ + FutureType_getsetlist, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + offsetof(FutureObj, dict), /* tp_dictoffset */ + (initproc)FutureObj_init, /* tp_init */ + 0, /* tp_alloc */ + PyType_GenericNew, /* tp_new */ + 0, /* tp_free */ +}; + +/*********************** Future Iterator **************************/ + +typedef struct { + PyObject_HEAD + FutureObj *future; +} futureiterobject; + +static void +FutureIter_dealloc(futureiterobject *it) +{ + _PyObject_GC_UNTRACK(it); + Py_XDECREF(it->future); + PyObject_GC_Del(it); +} + +static PyObject * +FutureIter_iternext(futureiterobject *it) +{ + PyObject *res; + FutureObj *fut = it->future; + + if (fut == NULL) + return NULL; + + if (fut->fut_state == STATE_PENDING) { + if (!fut->fut_blocking) { + fut->fut_blocking = 1; + Py_INCREF(fut); + return (PyObject *)fut; + } + PyErr_Format(PyExc_AssertionError, + "yield from wasn't used with future"); + return NULL; + } + + res = FutureObj_result(fut, NULL); + if (res != NULL) { + // normal result + PyErr_SetObject(PyExc_StopIteration, res); + Py_DECREF(res); + } + + it->future = NULL; + Py_DECREF(fut); + return NULL; +} + +static PyObject * +FutureIter_send(futureiterobject *self, PyObject *arg) +{ + return FutureIter_iternext(self); +} + +static PyObject * +FutureIter_throw(futureiterobject *self, PyObject *args) +{ + PyObject *type=NULL, *val=NULL, *tb=NULL; + if (!PyArg_ParseTuple(args, "O|OO", &type, &val, &tb)) + return NULL; + + Py_CLEAR(self->future); + + if (tb != NULL) { + PyErr_Restore(type, val, tb); + } + else if (val != NULL) { + PyErr_SetObject(type, val); + } + else { + if (PyExceptionClass_Check(type)) { + val = PyObject_CallObject(type, NULL); + } + else { + val = type; + type = (PyObject*)Py_TYPE(val); + } + PyErr_SetObject(type, val); + } + return NULL; +} + +static PyObject * +FutureIter_close(futureiterobject *self, PyObject *arg) +{ + Py_CLEAR(self->future); + Py_RETURN_NONE; +} + +static int +FutureIter_traverse(futureiterobject *it, visitproc visit, void *arg) +{ + Py_VISIT(it->future); + return 0; +} + +static PyMethodDef FutureIter_methods[] = { + {"send", (PyCFunction)FutureIter_send, METH_O, NULL}, + {"throw", (PyCFunction)FutureIter_throw, METH_VARARGS, NULL}, + {"close", (PyCFunction)FutureIter_close, METH_NOARGS, NULL}, + {NULL, NULL} /* Sentinel */ +}; + +static PyTypeObject FutureIterType = { + PyVarObject_HEAD_INIT(0, 0) + "_futures.FutureIter", + sizeof(futureiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + (destructor)FutureIter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_as_async */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + 0, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + 0, /* tp_doc */ + (traverseproc)FutureIter_traverse, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)FutureIter_iternext, /* tp_iternext */ + FutureIter_methods, /* tp_methods */ + 0, /* tp_members */ +}; + +static PyObject * +future_iter(PyObject *fut) +{ + futureiterobject *it; + + if (!PyObject_TypeCheck(fut, &FutureType)) { + PyErr_BadInternalCall(); + return NULL; + } + it = PyObject_GC_New(futureiterobject, &FutureIterType); + if (it == NULL) + return NULL; + Py_INCREF(fut); + it->future = (FutureObj*)fut; + _PyObject_GC_TRACK(it); + return (PyObject*)it; +} + +/*********************** Module **************************/ + +PyDoc_STRVAR(module_doc, "Fast asyncio.Future implementation.\n"); + +PyObject * +_init_module(PyObject *self, PyObject *args) +{ + FuturesModState *st; + + PyObject *traceback_extract_stack; + PyObject *asyncio_get_event_loop; + PyObject *asyncio_InvalidStateError; + PyObject *asyncio_CancelledError; + + if (!PyArg_UnpackTuple(args, "_init_module", 4, 4, + &traceback_extract_stack, + &asyncio_get_event_loop, + &asyncio_InvalidStateError, + &asyncio_CancelledError)) + return NULL; + + st = _FuturesMod_GetGlobalState(); + assert(st != NULL); + + Py_INCREF(traceback_extract_stack); + st->traceback_extract_stack = traceback_extract_stack; + + Py_INCREF(asyncio_get_event_loop); + st->asyncio_get_event_loop = asyncio_get_event_loop; + + Py_INCREF(asyncio_InvalidStateError); + st->asyncio_InvalidStateError = asyncio_InvalidStateError; + + Py_INCREF(asyncio_CancelledError); + st->asyncio_CancelledError = asyncio_CancelledError; + + st->ready = 1; + + Py_RETURN_NONE; +} + + +static void +futuresmod_free(PyObject *m) +{ + _FuturesMod_ClearState(_FuturesMod_GetState(m)); +} + + +static struct PyMethodDef futuresmod_methods[] = { + {"_init_module", _init_module, METH_VARARGS, NULL}, + {NULL, NULL} +}; + + +static struct PyModuleDef _futuresmodule = { + PyModuleDef_HEAD_INIT, /* m_base */ + "_futures", /* m_name */ + module_doc, /* m_doc */ + sizeof(FuturesModState), /* m_size */ + futuresmod_methods, /* m_methods */ + NULL, /* m_slots */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + (freefunc)futuresmod_free /* m_free */ +}; + + +PyMODINIT_FUNC +PyInit__futures(void) +{ + PyObject *m; + + m = PyState_FindModule(&_futuresmodule); + if (m) { + Py_INCREF(m); + return m; + } + + if (PyType_Ready(&FutureType) < 0) + return NULL; + if (PyType_Ready(&FutureIterType) < 0) + return NULL; + + m = PyModule_Create(&_futuresmodule); + if (m == NULL) + return NULL; + + Py_INCREF(&FutureType); + if (PyModule_AddObject(m, "Future", (PyObject *)&FutureType) < 0) { + Py_DECREF(&FutureType); + return NULL; + } + + if (_FuturesMod_InitState(m) < 0) + return NULL; + + return m; +} diff -r 960a73507d4d PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj Sat Sep 10 19:05:32 2016 -0700 +++ b/PCbuild/pythoncore.vcxproj Sun Sep 11 12:34:25 2016 +0900 @@ -221,6 +221,7 @@ + diff -r 960a73507d4d PCbuild/pythoncore.vcxproj.filters --- a/PCbuild/pythoncore.vcxproj.filters Sat Sep 10 19:05:32 2016 -0700 +++ b/PCbuild/pythoncore.vcxproj.filters Sun Sep 11 12:34:25 2016 +0900 @@ -470,6 +470,9 @@ Modules + + Modules + Modules diff -r 960a73507d4d setup.py --- a/setup.py Sat Sep 10 19:05:32 2016 -0700 +++ b/setup.py Sun Sep 11 12:34:25 2016 +0900 @@ -655,6 +655,8 @@ exts.append( Extension('unicodedata', ['unicodedata.c']) ) # _opcode module exts.append( Extension('_opcode', ['_opcode.c']) ) + # Fast asyncio Future implementation + exts.append( Extension("_futures", ["_futuresmodule.c"]) ) # Modules with some UNIX dependencies -- on by default: # (If you have a really backward UNIX, select and socket may not be