Ticket #2245: cdefer.2.patch

File cdefer.2.patch, 32.6 KB (added by Peaker, 7 years ago)

After my review -- fixing a lot of error-handling/refcounting bugs

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