Ticket #2245: cdefer.patch

File cdefer.patch, 34.5 KB (added by Peaker, 7 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)