Ticket #2245: cdefer.patch

File cdefer.patch, 34.5 KB (added by Peaker, 9 years ago)

Fixed a reentrancy bug (runCallbacks->callback->addCallbacks->runCallbacks). Added relevant test cases. Fixed use of defer.setTimeout in twisted.mail

  • twisted/test/test_defer.py

     
    4545    def _errback(self, *args, **kw):
    4646        self.errback_results = args, kw
    4747
     48    def testNoResult(self):
     49        deferred = defer.Deferred()
     50        self.assertRaises(AttributeError, lambda : deferred.result)
     51
     52    def testReentrantRunCallbacks(self):
     53        deferred = defer.Deferred()
     54        called = []
     55        def callback2(result):
     56            called.append(2)
     57        def callback1(result):
     58            called.append(1)
     59            deferred.addCallback(callback2)
     60        deferred.addCallback(callback1)
     61        deferred.callback(None)
     62        self.assertEquals(called, [1, 2])
     63
     64    def testAddCallbackErrbackWithoutArgs(self):
     65        deferred = defer.Deferred()
     66        self.assertRaises(TypeError, deferred.addCallback)
     67        self.assertRaises(TypeError, deferred.addErrback)
     68        self.assertRaises(TypeError, deferred.addBoth)
     69        # assertRaises gives a **{} to the function, whereas no arg at
     70        # all gives a NULL, so lets implement it manually here:
     71        for cback in [defer.Deferred.addCallback,
     72                      defer.Deferred.addErrback,
     73                      defer.Deferred.addBoth]:
     74            try:
     75                cback(deferred)
     76            except TypeError, e:
     77                pass
     78            else:
     79                self.assert_(False, "addCallback did not raise TypeError")
     80
    4881    def testCallbackWithoutArgs(self):
    4982        deferred = defer.Deferred()
    5083        deferred.addCallback(self._callback)
     
    562595        def _subErrorLogWithInnerFrameCycle():
    563596            d = defer.Deferred()
    564597            d.addCallback(lambda x, d=d: 1/0)
    565             d._d = d
    566598            d.callback(1)
     599            d.result = d
    567600
    568601        _subErrorLogWithInnerFrameCycle()
    569602        gc.collect()
  • twisted/mail/alias.py

     
    210210    def eomReceived(self):
    211211        if not self.done:
    212212            self.protocol.transport.loseConnection()
    213             self.completion.setTimeout(60)
     213            from twisted.internet import reactor
     214            def timeout_cb():
     215                timeout[0] = None
     216                self.completion.errback(defer.TimeoutError())
     217            timeout = [reactor.callLater(60, timeout_cb)]
     218            def cancel_timeout(result):
     219                if timeout[0] is not None:
     220                    timeout[0].cancel()
     221                    timeout[0] = None
     222                return result
     223            self.completion.addBoth(cancel_timeout)
    214224        return self.completion
    215225   
    216226    def connectionLost(self):
  • twisted/topfiles/setup.py

     
    3434    # Extension modules to build.
    3535    exts = []
    3636
     37    # twisted.internet.cdefer implementation of Deferred
     38    exts.append( Extension('twisted.internet.cdefer',
     39                           ['twisted/internet/cdefer/cdefer.c']) )
     40
     41
    3742    # urllib.unquote accelerator
    3843    exts.append( Extension("twisted.protocols._c_urlarg",
    3944                            ["twisted/protocols/_c_urlarg.c"],
  • twisted/internet/cdefer/cdefer.c

     
     1/*
     2 * A Deferred implementation in C. Cover most of the Deferred API but try
     3 * to be faster.
     4 *
     5 * TODO: Review failure handling - is everything decref'd properly?
     6 * TODO: Try to replace IsInstance calls with a fast ptr comparison?
     7 *
     8 */
     9
     10#include <Python.h>
     11#include "structmember.h"
     12
     13/* Py_VISIT and Py_CLEAR are defined here to be compatible with Python 2.3 */
     14
     15#ifndef Py_VISIT
     16#define Py_VISIT(op) \
     17    do { \
     18        if (op) { \
     19            int vret = visit((PyObject *)(op), arg); \
     20            if (vret) \
     21                return vret; \
     22        } \
     23    } while (0)
     24#endif
     25
     26#ifndef Py_CLEAR
     27#define Py_CLEAR(op) \
     28    do { \
     29        if (op) { \
     30            PyObject *tmp = (PyObject *)(op); \
     31            (op) = NULL; \
     32            Py_DECREF(tmp); \
     33        } \
     34    } while (0)
     35#endif
     36
     37PyObject * failure_class = NULL;
     38PyObject * already_called = NULL;
     39PyObject * debuginfo_class = NULL;
     40
     41typedef struct {
     42    PyObject_HEAD
     43    PyObject *result;
     44    int paused;
     45    PyObject *callbacks;
     46    PyObject *debuginfo;
     47    int called;
     48    /* Current callback index in the callbacks list to run. This
     49     * allows clearing the list once per runCallbacks rather than
     50     * popping every item. It has to be per-deferred, because
     51     * runCallbacks can be called reentrantly, and must not repeat the
     52     * previously-called callbacks. */
     53    Py_ssize_t callback_index;
     54} cdefer_Deferred;
     55
     56/* Prototypes */
     57
     58static PyObject * cdefer_Deferred_new(PyTypeObject *type, PyObject *args,
     59        PyObject *kwargs);
     60
     61static void cdefer_Deferred_dealloc(PyObject *o);
     62
     63static int cdefer_Deferred_traverse(PyObject *o, visitproc visit, void *arg);
     64
     65static int cdefer_Deferred_clear(PyObject *o);
     66
     67static int cdefer_Deferred_clear(PyObject *o);
     68
     69static int cdefer_Deferred___init__(cdefer_Deferred *self, PyObject *args,
     70        PyObject *kwargs);
     71
     72static PyObject *cdefer_Deferred__addCallbacks(cdefer_Deferred *self,
     73        PyObject *callback, PyObject *errback, PyObject *callbackArgs,
     74        PyObject *callbackKeywords, PyObject *errbackArgs,
     75        PyObject *errbackKeywords);
     76
     77static PyObject *cdefer_Deferred_addCallback(cdefer_Deferred *self,
     78        PyObject *args, PyObject *kwargs);
     79
     80static PyObject *cdefer_Deferred_addErrback(cdefer_Deferred *self,
     81        PyObject *args, PyObject *kwargs);
     82
     83static PyObject *cdefer_Deferred_addBoth(cdefer_Deferred *self, PyObject *args,
     84        PyObject *kwargs);
     85
     86static PyObject *cdefer_Deferred_pause(cdefer_Deferred *self, PyObject *args);
     87
     88static PyObject *cdefer_Deferred_unpause(cdefer_Deferred *self,
     89        PyObject *args);
     90
     91static PyObject *cdefer_Deferred_chainDeferred(cdefer_Deferred *self,
     92        PyObject *args, PyObject *kwargs);
     93
     94static PyObject *cdefer_Deferred__runCallbacks(cdefer_Deferred *self);
     95
     96static PyObject *cdefer_Deferred__startRunCallbacks(cdefer_Deferred *self,
     97        PyObject *result);
     98
     99static PyObject *cdefer_Deferred_callback(cdefer_Deferred *self, PyObject *args,
     100        PyObject *kwargs);
     101
     102static PyObject *cdefer_Deferred_errback(cdefer_Deferred *self, PyObject *args,
     103        PyObject *kwargs);
     104
     105static PyObject *cdefer_Deferred__continue(cdefer_Deferred *self,
     106        PyObject *args, PyObject *kwargs);
     107
     108static PyTypeObject cdefer_DeferredType;
     109
     110static PyObject * cdefer_Deferred_new(PyTypeObject *type, PyObject *args,
     111        PyObject *kwargs) {
     112    cdefer_Deferred *self;
     113    self = (cdefer_Deferred *)type->tp_alloc(type, 0);
     114    return (PyObject *)self;
     115}
     116
     117static void cdefer_Deferred_dealloc(PyObject *o) {
     118    cdefer_Deferred *self;
     119    self = (cdefer_Deferred *)o;
     120    PyObject_GC_UnTrack(self);
     121    Py_XDECREF(self->result);
     122    Py_XDECREF(self->debuginfo);
     123    Py_XDECREF(self->callbacks);
     124    (*o->ob_type->tp_free)(o);
     125}
     126
     127static int cdefer_Deferred_traverse(PyObject *o, visitproc visit, void *arg) {
     128    cdefer_Deferred *self;
     129    self = (cdefer_Deferred *)o;
     130    Py_VISIT(self->result);
     131    Py_VISIT(self->debuginfo);
     132    Py_VISIT(self->callbacks);
     133    return 0;
     134}
     135
     136static int cdefer_Deferred_clear(PyObject *o) {
     137    cdefer_Deferred *self;
     138    self = (cdefer_Deferred *)o;
     139    Py_CLEAR(self->result);
     140    Py_CLEAR(self->debuginfo);
     141    Py_CLEAR(self->callbacks);
     142    return 0;
     143}
     144
     145static int cdefer_Deferred___init__(cdefer_Deferred *self, PyObject *args,
     146        PyObject *kwargs) {
     147    static char *argnames[] = {NULL};
     148    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "", argnames)) {
     149        return -1;
     150    }
     151    self->paused = 0;
     152    self->callback_index = 0;
     153    self->callbacks = PyList_New(0);
     154    if (!self->callbacks) {
     155        return -1;
     156    }
     157    return 0;
     158}
     159
     160static PyObject *cdefer_Deferred__addCallbacks(cdefer_Deferred *self,
     161        PyObject *callback, PyObject *errback, PyObject *callbackArgs,
     162        PyObject *callbackKeywords, PyObject *errbackArgs,
     163        PyObject *errbackKeywords) {
     164    PyObject *result;
     165    PyObject *cbs = 0;
     166    int rc;
     167
     168    if (callback != Py_None) {
     169        if (!PyCallable_Check(callback)) {
     170            PyErr_SetNone(PyExc_AssertionError);
     171            return NULL;
     172        }
     173    }
     174    if (errback != Py_None) {
     175        if (!PyCallable_Check(errback)) {
     176            PyErr_SetNone(PyExc_AssertionError);
     177            return NULL;
     178        }
     179    }
     180
     181    cbs = Py_BuildValue("(OOOOOO)", callback, callbackArgs, callbackKeywords,
     182                                    errback, errbackArgs, errbackKeywords);
     183    if (!cbs) {
     184        return NULL;
     185    }
     186
     187    rc = PyList_Append(self->callbacks, cbs);
     188    Py_CLEAR(cbs);
     189    if (rc == -1) {
     190        return NULL;
     191    }
     192
     193    if (self->called) {
     194        if (cdefer_Deferred__runCallbacks(self) == NULL) {
     195            return NULL;
     196        }
     197    }
     198
     199    result = (PyObject *)self;
     200    Py_INCREF(result);
     201    return result;
     202}
     203
     204static char cdefer_Deferred_addCallbacks_doc[] = "Add a pair of callbacks (success and error) to this Deferred.\n\nThese will be executed when the \'master\' callback is run.";
     205
     206static PyObject *cdefer_Deferred_addCallbacks(cdefer_Deferred *self,
     207        PyObject *args, PyObject *kwargs) {
     208    static char *argnames[] = {"callback", "errback", "callbackArgs",
     209        "callbackKeywords", "errbackArgs", "errbackKeywords", NULL};
     210    PyObject *callback;
     211    PyObject *errback = Py_None;
     212    PyObject *callbackArgs = Py_None;
     213    PyObject *callbackKeywords = Py_None;
     214    PyObject *errbackArgs = Py_None;
     215    PyObject *errbackKeywords = Py_None;
     216    PyObject *result;
     217    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOO", argnames,
     218                &callback, &errback, &callbackArgs,
     219                &callbackKeywords, &errbackArgs, &errbackKeywords)) {
     220        return NULL;
     221    }
     222    result = cdefer_Deferred__addCallbacks(self, callback, errback,
     223        callbackArgs, callbackKeywords, errbackArgs, errbackKeywords);
     224    return result;
     225}
     226
     227static char cdefer_Deferred_addCallback_doc[] = "Convenience method for adding just a callback.\n\nSee L{addCallbacks}.";
     228
     229/* Returns a NEW reference to the callback/errback arg, and to the
     230 * cbackArgs, but a BORROWED reference to the keywords. In case of
     231 * error, no references are returned/touched */
     232static PyObject *extract_cback_args_kw(char *argname,
     233                                       PyObject *args, PyObject *kwargs,
     234                                       PyObject **cbackArgs,
     235                                       PyObject **cbackKeywords)
     236{
     237    PyObject *cback;
     238   
     239    if (kwargs) {
     240        (*cbackKeywords) = kwargs;
     241    } else {
     242        (*cbackKeywords) = Py_None;
     243    }
     244    if (PyTuple_Size(args) > 0) {
     245        cback = PyTuple_GET_ITEM(args, 0);
     246        if (!cback) {
     247            return NULL;
     248        }
     249        (*cbackArgs) = PyTuple_GetSlice(args, 1, PyTuple_Size(args));
     250        if (!(*cbackArgs)) {
     251            return NULL;
     252        }
     253        Py_INCREF(cback);
     254    } else {
     255        cback = PyDict_GetItemString((*cbackKeywords), argname);
     256        if (!cback) {
     257            PyErr_Format(PyExc_TypeError, "addCallback requires '%s' argument'", argname);
     258            return NULL;
     259        }
     260        (*cbackArgs) = Py_None;
     261        Py_INCREF(Py_None);
     262
     263        /* "callback" in the keyword dict may be the only reference to
     264         * it, and we delete it from the dict, so we must own a
     265         * reference too */
     266        Py_INCREF(cback);
     267
     268        if (PyDict_DelItemString((*cbackKeywords), argname) == -1) {
     269            Py_DECREF(cback);
     270            Py_DECREF(Py_None);
     271            return NULL;
     272        }
     273    }
     274    return cback;
     275}
     276
     277static PyObject *cdefer_Deferred_addCallback(cdefer_Deferred *self,
     278        PyObject *args, PyObject *kwargs) {
     279    PyObject *callback;
     280    PyObject *callbackArgs;
     281    PyObject *callbackKeywords;
     282    PyObject *result;
     283    callback = extract_cback_args_kw("callback", args, kwargs, &callbackArgs, &callbackKeywords);
     284    if (!callback) {
     285        return NULL;
     286    }
     287    result = cdefer_Deferred__addCallbacks(self, callback, Py_None, callbackArgs,
     288        callbackKeywords, Py_None, Py_None);
     289    Py_DECREF(callback);
     290    Py_DECREF(callbackArgs);
     291    return result;
     292}
     293
     294static char cdefer_Deferred_addErrback_doc[] = "Convenience method for adding just an errback.\n\nSee L{addCallbacks}.";
     295
     296static PyObject *cdefer_Deferred_addErrback(cdefer_Deferred *self,
     297        PyObject *args, PyObject *kwargs) {
     298    PyObject *errback;
     299    PyObject *errbackArgs;
     300    PyObject *errbackKeywords;
     301    PyObject *result;
     302    errback = extract_cback_args_kw("errback", args, kwargs, &errbackArgs, &errbackKeywords);
     303    if (!errback) {
     304        return NULL;
     305    }
     306    result = cdefer_Deferred__addCallbacks(self, Py_None, errback, Py_None,
     307        Py_None, errbackArgs, errbackKeywords);
     308    Py_DECREF(errback);
     309    Py_DECREF(errbackArgs);
     310    return result;
     311}
     312
     313static char cdefer_Deferred_addBoth_doc[] = "Convenience method for adding a single callable as both a callback\nand an errback.\n\nSee L{addCallbacks}.";
     314
     315static PyObject *cdefer_Deferred_addBoth(cdefer_Deferred *self, PyObject *args,
     316        PyObject *kwargs) {
     317    PyObject *callback;
     318    PyObject *callbackArgs;
     319    PyObject *callbackKeywords;
     320    PyObject *result;
     321    callback = extract_cback_args_kw("callback", args, kwargs, &callbackArgs, &callbackKeywords);
     322    if (!callback) {
     323        return NULL;
     324    }
     325    result = cdefer_Deferred__addCallbacks(self, callback, callback,
     326        callbackArgs, callbackKeywords, callbackArgs, callbackKeywords);
     327    Py_DECREF(callback);
     328    Py_DECREF(callbackArgs);
     329    return result;
     330}
     331
     332static char cdefer_Deferred_pause_doc[] = "Stop processing on a Deferred until L{unpause}() is called.";
     333
     334static PyObject *cdefer_Deferred_pause(cdefer_Deferred *self, PyObject *args) {
     335    PyObject *result;
     336    self->paused++;
     337    result = Py_None;
     338    Py_INCREF(Py_None);
     339    return result;
     340}
     341
     342static char cdefer_Deferred_unpause_doc[] = "Process all callbacks made since L{pause}() was called.";
     343
     344static PyObject *cdefer_Deferred_unpause(cdefer_Deferred *self,
     345        PyObject *args) {
     346    self->paused--;
     347    if (!self->paused && self->called) {
     348        return cdefer_Deferred__runCallbacks(self);
     349    }
     350    Py_INCREF(Py_None);
     351    return Py_None;
     352}
     353
     354static char cdefer_Deferred_chainDeferred_doc[] = "Chain another Deferred to this Deferred.\n\nThis method adds callbacks to this Deferred to call d\'s callback or\nerrback, as appropriate.";
     355
     356static PyObject *cdefer_Deferred_chainDeferred(cdefer_Deferred *self,
     357        PyObject *args, PyObject *kwargs) {
     358    PyObject *d;
     359    PyObject *callback;
     360    PyObject *errback;
     361    PyObject *result;
     362    static char *argnames[] = {"d", NULL};
     363    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", argnames, &d)) {
     364        return NULL;
     365    }
     366    callback = PyObject_GetAttrString(d, "callback");
     367    if (!callback) {
     368        return NULL;
     369    }
     370    errback = PyObject_GetAttrString(d, "errback");
     371    if (!errback) {
     372        Py_DECREF(callback);
     373        return NULL;
     374    }
     375    result = cdefer_Deferred__addCallbacks(self, callback, errback, Py_None,
     376        Py_None, Py_None, Py_None);
     377    Py_DECREF(callback);
     378    Py_DECREF(errback);
     379    return result;
     380}
     381
     382static int cdefer_Deferred__set_debuginfo_fail_result(cdefer_Deferred *self) {
     383    if (!self->debuginfo) {
     384        self->debuginfo = PyObject_CallObject(debuginfo_class, NULL);
     385        if (!self->debuginfo) {
     386            return -1;
     387        }
     388    }
     389    if (PyObject_SetAttrString(self->debuginfo, "failResult", self->result) == -1) {
     390        return -1;
     391    }
     392    return 0;
     393}
     394
     395static int cdefer_Deferred__clear_debuginfo(cdefer_Deferred *self) {
     396    if (self->debuginfo) {
     397        if (PyObject_SetAttrString(self->debuginfo, "failResult", Py_None) == -1) {
     398            return -1;
     399        }
     400    }
     401    return 0;
     402}
     403
     404
     405static PyObject *cdefer_Deferred__runCallbacks(cdefer_Deferred *self) {
     406    PyObject *cb;
     407    PyObject *item;
     408    PyObject *callback;
     409    PyObject *args;
     410    PyObject *newArgs;
     411    PyObject *newArgs2;
     412    PyObject *kwargs;
     413    PyObject *_continue;
     414    PyObject *type, *value, *traceback, *failArgs;
     415    PyObject *tmp;
     416    PyObject *result;
     417    int size;
     418    int offset;
     419
     420    if (!self->paused) {
     421        cb = self->callbacks;
     422        for (;;) {
     423            size = PyList_GET_SIZE(cb);
     424            if (size == -1) {
     425                return NULL;
     426            }
     427            if (self->callback_index >= size) {
     428                break;
     429            }
     430           
     431            item = PyList_GET_ITEM(cb, self->callback_index);
     432            if (!item) {
     433                return NULL;
     434            }
     435
     436            offset = 0;
     437            if (PyObject_IsInstance(self->result, failure_class)) {
     438                offset = 3;
     439            }
     440
     441            callback = PyTuple_GET_ITEM(item, offset + 0);
     442            if (!callback) {
     443                return NULL;
     444            }
     445            if (callback == Py_None) {
     446                ++self->callback_index;
     447                continue;
     448            }
     449
     450            args = PyTuple_GET_ITEM(item, offset + 1);
     451            if (!args) {
     452                return NULL;
     453            }
     454
     455            kwargs = PyTuple_GET_ITEM(item, offset + 2);
     456            if (!kwargs) {
     457                return NULL;
     458            }
     459
     460            newArgs = Py_BuildValue("(O)", self->result);
     461            if (!newArgs) {
     462                return NULL;
     463            }
     464
     465            if (args != Py_None) {
     466                newArgs2 = PySequence_InPlaceConcat(newArgs, args);
     467                if (!newArgs2) {
     468                    return NULL;
     469                }
     470                Py_CLEAR(newArgs);
     471            } else {
     472                newArgs2 = newArgs;
     473                newArgs = NULL;
     474            }
     475
     476            ++self->callback_index;
     477            if (kwargs == Py_None) {
     478                tmp = PyObject_Call(callback, newArgs2, NULL);
     479            } else {
     480                tmp = PyObject_Call(callback, newArgs2, kwargs);
     481            }
     482            Py_DECREF(self->result);
     483            self->result = tmp;
     484
     485            Py_CLEAR(newArgs2);
     486
     487            if (!self->result) {
     488                PyErr_Fetch(&type, &value, &traceback);
     489                PyErr_NormalizeException(&type, &value, &traceback);
     490                if (!traceback) {
     491                    traceback = Py_None;
     492                    Py_INCREF(traceback);
     493                }
     494
     495                failArgs = Py_BuildValue("(OOO)", value, type, traceback);
     496                if (!failArgs) {
     497                    PyErr_Restore(type, value, traceback);
     498                    return NULL;
     499                }
     500                self->result = PyObject_CallObject(failure_class, failArgs);
     501                Py_DECREF(failArgs);
     502                if (!self->result) {
     503                    PyErr_Restore(type, value, traceback);
     504                    return NULL;
     505                }
     506                Py_DECREF(type);
     507                Py_DECREF(value);
     508                Py_DECREF(traceback);
     509                continue;
     510            }
     511            Py_INCREF(self->result);
     512            if (PyObject_TypeCheck(self->result, &cdefer_DeferredType)) {
     513                if (PyList_SetSlice(cb, 0, self->callback_index, NULL) == -1) {
     514                    return NULL;
     515                }
     516                self->callback_index = 0;
     517               
     518                result = PyObject_CallMethod((PyObject *)self, "pause", NULL);
     519                if (!result) {
     520                    return NULL;
     521                }
     522                Py_DECREF(result);
     523               
     524                _continue = PyObject_GetAttrString((PyObject *)self,
     525                                                   "_continue");
     526                if (!_continue) {
     527                    return NULL;
     528                }
     529
     530                result = cdefer_Deferred__addCallbacks(
     531                    (cdefer_Deferred *)self->result, _continue,
     532                    _continue, Py_None, Py_None, Py_None, Py_None);
     533                /* The reference was either copied/incref'd or not
     534                 * (when errored) in addCallbacks, either way, we own
     535                 * one too, and don't need it anymore. */
     536                Py_DECREF(_continue);
     537               
     538                if (!result) {
     539                    return NULL;
     540                }
     541                Py_DECREF(result);
     542               
     543                goto endLabel;
     544            }
     545        }
     546        if (PyList_SetSlice(cb, 0, PyList_GET_SIZE(cb), NULL) == -1) {
     547            return NULL;
     548        }
     549        self->callback_index = 0;
     550    }
     551endLabel:;
     552    if (PyObject_IsInstance(self->result, failure_class)) {
     553        result = PyObject_CallMethod((PyObject *)self->result,
     554                                     "cleanFailure", NULL);
     555        if (!result) {
     556            return NULL;
     557        }
     558        Py_DECREF(result);
     559        if (cdefer_Deferred__set_debuginfo_fail_result(self) == -1) {
     560            return NULL;
     561        }
     562    } else {
     563        if (cdefer_Deferred__clear_debuginfo(self) == -1) {
     564            return NULL;
     565        }
     566    }
     567    Py_INCREF(Py_None);
     568    return Py_None;
     569}
     570
     571static PyObject *cdefer_Deferred__startRunCallbacks(cdefer_Deferred *self,
     572        PyObject *result) {
     573    if (self->called) {
     574        PyErr_SetNone(already_called);
     575        return NULL;
     576    }
     577    self->called = 1;
     578    Py_XDECREF(self->result);
     579    self->result = result;
     580    Py_INCREF(self->result);
     581    return cdefer_Deferred__runCallbacks(self);
     582}
     583
     584static char cdefer_Deferred_callback_doc[] = "Run all success callbacks that have been added to this Deferred.\n\nEach callback will have its result passed as the first\nargument to the next; this way, the callbacks act as a\n\'processing chain\'. Also, if the success-callback returns a Failure\nor raises an Exception, processing will continue on the *error*-\ncallback chain.";
     585
     586static PyObject *cdefer_Deferred_callback(cdefer_Deferred *self, PyObject *args,
     587        PyObject *kwargs) {
     588    PyObject *result;
     589    static char *argnames[] = {"result", NULL};
     590    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", argnames, &result)) {
     591        return NULL;
     592    }
     593    return cdefer_Deferred__startRunCallbacks(self, result);
     594}
     595
     596static char cdefer_Deferred_errback_doc[] = "Run all error callbacks that have been added to this Deferred.\n\nEach callback will have its result passed as the first\nargument to the next; this way, the callbacks act as a\n\'processing chain\'. Also, if the error-callback returns a non-Failure\nor doesn\'t raise an Exception, processing will continue on the\n*success*-callback chain.\n\nIf the argument that\'s passed to me is not a Failure instance,\nit will be embedded in one. If no argument is passed, a Failure\ninstance will be created based on the current traceback stack.\n\nPassing a string as `fail\' is deprecated, and will be punished with\na warning message.";
     597
     598static PyObject *cdefer_Deferred_errback(cdefer_Deferred *self, PyObject *args,
     599        PyObject *kwargs) {
     600    PyObject *fail;
     601    PyObject *tpl;
     602    PyObject *tmp;
     603    PyObject *result;
     604    static char *argnames[] = {"fail", NULL};
     605    fail = Py_None;
     606    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O", argnames, &fail)) {
     607        return NULL;
     608    }
     609
     610    if (PyObject_IsInstance(fail, failure_class)) {
     611        /* Make "fail" belong to us even if we don't create a Failure
     612         * wrapper (If we do, the wrapper belongs to us) */
     613        Py_INCREF(fail);
     614    } else {
     615        tpl = Py_BuildValue("(O)", fail);
     616        if (!tpl) {
     617            return NULL;
     618        }
     619        tmp = PyObject_CallObject(failure_class, tpl);
     620        Py_DECREF(tpl);
     621        if (!tmp) {
     622            return NULL;
     623        }
     624        fail = tmp;
     625    }
     626    result = cdefer_Deferred__startRunCallbacks(self, fail);
     627    Py_DECREF(fail);
     628    return result;
     629}
     630
     631static PyObject *cdefer_Deferred__continue(cdefer_Deferred *self,
     632        PyObject *args, PyObject *kwargs) {
     633    PyObject *result;
     634    static char *argnames[] = {"result", NULL};
     635    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", argnames, &result)) {
     636        return NULL;
     637    }
     638    Py_XDECREF(self->result);
     639    self->result = result;
     640    Py_INCREF(self->result);
     641    return PyObject_CallMethod((PyObject *)self, "unpause", NULL);
     642}
     643
     644static struct PyMethodDef cdefer_Deferred_methods[] = {
     645  {"addCallbacks", (PyCFunction)cdefer_Deferred_addCallbacks,
     646                   METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addCallbacks_doc},
     647  {"addCallback", (PyCFunction)cdefer_Deferred_addCallback,
     648                  METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addCallback_doc},
     649  {"addErrback", (PyCFunction)cdefer_Deferred_addErrback,
     650                 METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addErrback_doc},
     651  {"addBoth", (PyCFunction)cdefer_Deferred_addBoth,
     652               METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_addBoth_doc},
     653  {"chainDeferred", (PyCFunction)cdefer_Deferred_chainDeferred,
     654                    METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_chainDeferred_doc},
     655  {"callback", (PyCFunction)cdefer_Deferred_callback,
     656               METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_callback_doc},
     657  {"errback", (PyCFunction)cdefer_Deferred_errback,
     658              METH_VARARGS|METH_KEYWORDS, cdefer_Deferred_errback_doc},
     659  {"pause", (PyCFunction)cdefer_Deferred_pause,
     660            METH_VARARGS, cdefer_Deferred_pause_doc},
     661  {"unpause", (PyCFunction)cdefer_Deferred_unpause,
     662              METH_VARARGS, cdefer_Deferred_unpause_doc},
     663  {"_continue", (PyCFunction)cdefer_Deferred__continue,
     664                METH_VARARGS|METH_KEYWORDS, ""},
     665  {0, 0, 0, 0}
     666};
     667
     668static struct PyMemberDef cdefer_Deferred_members[] = {
     669  {"result", T_OBJECT_EX, offsetof(cdefer_Deferred, result), 0, 0},
     670  {"paused", T_INT, offsetof(cdefer_Deferred, paused), READONLY, 0},
     671  {"called", T_INT, offsetof(cdefer_Deferred, called), READONLY, 0},
     672  {"callbacks", T_OBJECT, offsetof(cdefer_Deferred, callbacks), READONLY, 0},
     673  {0, 0, 0, 0, 0}
     674};
     675
     676static PyTypeObject cdefer_DeferredType = {
     677    PyObject_HEAD_INIT(0)
     678    0,                          /*ob_size*/
     679    "cdefer.Deferred",          /*tp_name*/
     680    sizeof(cdefer_Deferred),    /*tp_basicsize*/
     681    0,                          /*tp_itemsize*/
     682    (destructor)cdefer_Deferred_dealloc,    /*tp_dealloc*/
     683    0,                          /*tp_print*/
     684    0,                          /*tp_getattr*/
     685    0,                          /*tp_setattr*/
     686    0,                          /*tp_compare*/
     687    0,                          /*tp_repr*/
     688    0,                          /*tp_as_number*/
     689    0,                          /*tp_as_sequence*/
     690    0,                          /*tp_as_mapping*/
     691    0,                          /*tp_hash */
     692    0,                          /*tp_call*/
     693    0,                          /*tp_str*/
     694    0,                          /*tp_getattro*/
     695    0,                          /*tp_setattro*/
     696    0,                          /*tp_as_buffer*/
     697    Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
     698    "This is a callback which will be put off until later.\n\nWhy do we want this? Well, in cases where a function in a threaded\nprogram would block until it gets a result, for Twisted it should\nnot block. Instead, it should return a Deferred.\n\nThis can be implemented for protocols that run over the network by\nwriting an asynchronous protocol for twisted.internet. For methods\nthat come from outside packages that are not under our control, we use\nthreads (see for example L{twisted.enterprise.adbapi}).\n\nFor more information about Deferreds, see doc/howto/defer.html or\nU{http://www.twistedmatrix.com/documents/howto/defer}.", /*tp_doc*/
     699    (traverseproc)cdefer_Deferred_traverse,   /*tp_traverse*/
     700    (inquiry)cdefer_Deferred_clear,           /*tp_clear*/
     701    0,                          /*tp_richcompare*/
     702    0,                          /*tp_weaklistoffset*/
     703    0,                          /*tp_iter*/
     704    0,                          /*tp_iternext*/
     705    cdefer_Deferred_methods,    /*tp_methods*/
     706    cdefer_Deferred_members,    /*tp_members*/
     707    0,                          /*tp_getset*/
     708    0,                          /*tp_base*/
     709    0,                          /*tp_dict*/
     710    0,                          /*tp_descr_get*/
     711    0,                          /*tp_descr_set*/
     712    0,                          /*tp_dictoffset*/
     713    (initproc)cdefer_Deferred___init__,   /*tp_init*/
     714    0,                          /*tp_alloc*/
     715    cdefer_Deferred_new,        /*tp_new*/
     716    PyObject_GC_Del,            /*tp_free*/
     717    0,                          /*tp_is_gc*/
     718    0,                          /*tp_bases*/
     719    0,                          /*tp_mro*/
     720    0,                          /*tp_cache*/
     721    0,                          /*tp_subclasses*/
     722    0,                          /*tp_weaklist*/
     723};
     724
     725static PyMethodDef cdefer_methods[] = {
     726    {NULL}  /* Sentinel */
     727};
     728
     729#ifndef PyMODINIT_FUNC  /* declarations for DLL import/export */
     730#define PyMODINIT_FUNC void
     731#endif
     732PyMODINIT_FUNC initcdefer(void) {
     733    PyObject * m = NULL;
     734    PyObject * f = NULL;
     735    PyObject * d = NULL;
     736
     737    if (PyType_Ready(&cdefer_DeferredType) < 0) {
     738        return;
     739    }
     740
     741    m = Py_InitModule3("cdefer", cdefer_methods,
     742                       "cdefer");
     743
     744    if (!m) {
     745        return;
     746    }
     747
     748    Py_INCREF(&cdefer_DeferredType);
     749    PyModule_AddObject(m, "Deferred", (PyObject *)&cdefer_DeferredType);
     750
     751    f = PyImport_ImportModule("twisted.python.failure");
     752    if (!f) {
     753        goto Error;
     754    }
     755
     756    failure_class = PyObject_GetAttrString(f, "Failure");
     757    if (!failure_class) {
     758        goto Error;
     759    }
     760
     761    d = PyImport_ImportModule("twisted.internet.defer");
     762    if (!d) {
     763        goto Error;
     764    }
     765    already_called = PyObject_GetAttrString(d, "AlreadyCalledError");
     766    if (!already_called) {
     767        goto Error;
     768    }
     769
     770    debuginfo_class = PyObject_GetAttrString(d, "DebugInfo");
     771    if(!debuginfo_class) {
     772        goto Error;
     773    }
     774   
     775    return;
     776Error:
     777    Py_XDECREF(f);
     778    Py_XDECREF(failure_class);
     779    Py_XDECREF(d);
     780    Py_XDECREF(already_called);
     781    Py_XDECREF(debuginfo_class);
     782}
     783
  • twisted/internet/defer.py

     
    438438                    self.index == other.index)
    439439        return False
    440440
     441try:
     442    from twisted.internet.cdefer import Deferred
     443except ImportError:
     444    pass
     445else:
     446    # Stubs for setDebugging/getDebugging for backwards compatibility
     447    # when using cdefer.
     448    is_debug = False
     449    def setDebugging(new_value):
     450        global is_debug
     451        is_debug = new_value
     452    def getDebugging():
     453        global is_debug
     454        return is_debug
     455
    441456class DeferredList(Deferred):
    442457    """I combine a group of deferreds into one callback.
    443458
  • doc/core/benchmarks/timer.py

     
    1111    calls func iter times with args and kwargs, returns time elapsed
    1212    """
    1313
    14     import time
     14    from time import time as current_time
    1515    r = range(iter)
    16     t = time.time()
     16    t = current_time()
    1717    for i in r:
    1818        func(*args, **kwargs)
    19     return time.time() - t
     19    return current_time() - t
  • doc/core/benchmarks/defer.py

     
     1import functools
     2from twisted.internet import defer
     3from timer import timeit
     4
     5benchmark_funcs = []
     6
     7def benchmark_func(iter, args=()):
     8    def decorator(func):
     9        benchmark_funcs.append((func, args, iter))
     10        return func
     11    return decorator
     12
     13def benchmark_n_func(iter, ns):
     14    def decorator(func):
     15        for n in ns:
     16            benchmark_funcs.append((func, (n,), iter))
     17        return func
     18    return decorator
     19
     20def instantiate():
     21    d = defer.Deferred()
     22instantiate = benchmark_func(100000)(instantiate)
     23
     24def instantiate_shoot_callback():
     25    d = defer.Deferred()
     26    d.callback(1)
     27instantiate_shoot_callback = benchmark_func(100000)(instantiate_shoot_callback)
     28
     29def instantiate_shoot_errback():
     30    d = defer.Deferred()
     31    try:
     32        1/0
     33    except:
     34        d.errback()
     35    d.addErrback(lambda x: None)
     36instantiate_shoot_errback = benchmark_func(200)(instantiate_shoot_errback)
     37
     38ns = [10, 1000, 10000]
     39
     40def instantiate_add_callbacks_no_result(n):
     41    d = defer.Deferred()
     42    def f(result):
     43        return result
     44    for i in xrange(n):
     45        d.addCallback(f)
     46        d.addErrback(f)
     47        d.addBoth(f)
     48        d.addCallbacks(f)
     49instantiate_add_callbacks_no_result = benchmark_n_func(20, ns)(instantiate_add_callbacks_no_result)
     50
     51def instantiate_add_callbacks_before_result(n):
     52    d = defer.Deferred()
     53    def f(result):
     54        return result
     55    for i in xrange(n):
     56        d.addCallback(f)
     57        d.addErrback(f)
     58        d.addBoth(f)
     59        d.addCallbacks(f)
     60    d.callback(1)
     61instantiate_add_callbacks_before_result = benchmark_n_func(20, ns)(instantiate_add_callbacks_before_result)
     62
     63def instantiate_add_callbacks_after_result(n):
     64    d = defer.Deferred()
     65    def f(result):
     66        return result
     67    d.callback(1)
     68    for i in xrange(n):
     69        d.addCallback(f)
     70        d.addErrback(f)
     71        d.addBoth(f)
     72        d.addCallbacks(f)
     73instantiate_add_callbacks_after_result = benchmark_n_func(20, ns)(instantiate_add_callbacks_after_result)
     74
     75def pause_unpause(n):
     76    d = defer.Deferred()
     77    def f(result):
     78        return result
     79    d.callback(1)
     80    d.pause()
     81    for i in xrange(n):
     82        d.addCallback(f)
     83        d.addErrback(f)
     84        d.addBoth(f)
     85        d.addCallbacks(f)
     86    d.unpause()
     87pause_unpause = benchmark_n_func(20, ns)(pause_unpause)
     88
     89print defer.Deferred.__module__
     90for func, args, iter in benchmark_funcs:
     91    print func, args, timeit(func, iter, *args)