diff -r 8b2546165960 Modules/itertoolsmodule.c
--- a/Modules/itertoolsmodule.c	Sat Oct 06 15:09:06 2012 +0200
+++ b/Modules/itertoolsmodule.c	Sun Oct 07 11:39:44 2012 +0400
@@ -1803,38 +1803,115 @@
     return 0;
 }
 
-static PyObject *
-chain_next(chainobject *lz)
+static int
+chain_advance(chainobject *lz)
 {
-    PyObject *item;
-
     if (lz->source == NULL)
-        return NULL;                                    /* already stopped */
+    {
+        PyErr_SetNone(PyExc_StopIteration);
+        return 0;                                       /* already stopped */
+    }
 
     if (lz->active == NULL) {
         PyObject *iterable = PyIter_Next(lz->source);
         if (iterable == NULL) {
             Py_CLEAR(lz->source);
-            return NULL;                                /* no more input sources */
+            if (!PyErr_Occurred())
+                PyErr_SetNone(PyExc_StopIteration);
+            return 0;                                   /* no more input sources */
         }
         lz->active = PyObject_GetIter(iterable);
         Py_DECREF(iterable);
         if (lz->active == NULL) {
             Py_CLEAR(lz->source);
-            return NULL;                                /* input not iterable */
+            return 0;                                   /* input not iterable */
         }
     }
-    item = PyIter_Next(lz->active);
+
+    return 1;
+}
+
+static PyObject *
+chain_send(chainobject *lz, PyObject *arg)
+{
+    PyObject *item;
+
+    if (!chain_advance(lz))
+        return NULL;
+
+    if (PyGen_CheckExact(lz->active)) {
+        item = _PyGen_Send((PyGenObject *)lz->active, arg);
+    } else {
+        _Py_IDENTIFIER(send);
+        if (arg == Py_None)
+            item = PyIter_Next(lz->active);
+        else
+            item = _PyObject_CallMethodObjIdArgs(lz->active, &PyId_send, arg, NULL);
+    }
+
     if (item != NULL)
         return item;
+
     if (PyErr_Occurred()) {
         if (PyErr_ExceptionMatches(PyExc_StopIteration))
             PyErr_Clear();
         else
-            return NULL;                                /* input raised an exception */
+            return NULL;                               /* input raised an exception */
     }
+
     Py_CLEAR(lz->active);
-    return chain_next(lz);                      /* recurse and use next active */
+    /* Skip to the next iterator.
+     * Note that we send `None` to it -- `arg` was already sent to the last one.
+     */
+    return chain_send(lz, Py_None);
+
+}
+
+static PyObject *
+chain_next(chainobject *lz)
+{
+    /* next(a) <=> a.send(None) */
+    return chain_send(lz, Py_None);
+}
+
+static PyObject *
+chain_throw(chainobject *lz, PyObject *args)
+{
+    PyObject *typ;
+    PyObject *tb = NULL;
+    PyObject *val = NULL;
+    _Py_IDENTIFIER(throw);
+
+    if (!PyArg_UnpackTuple(args, "throw", 1, 3, &typ, &val, &tb))
+        return NULL;
+
+    if (!chain_advance(lz))
+    {
+        if (!PyErr_ExceptionMatches(PyExc_StopIteration))
+            return NULL;
+
+        PyErr_Restore(typ, tb, val);
+        return NULL;
+    }
+
+    return _PyObject_CallMethodObjIdArgs(lz->active, &PyId_throw, typ, tb, val, NULL);
+}
+
+static PyObject *
+chain_close(chainobject *lz)
+{
+    _Py_IDENTIFIER(close);
+
+    if (!chain_advance(lz)) {
+        if (!PyErr_ExceptionMatches(PyExc_StopIteration))
+            return NULL;
+
+        /* Closing an empty chain shouldn't raise an error. */
+        PyErr_Clear();
+        return Py_None;
+    }
+
+    return _PyObject_CallMethodId(lz->active, &PyId_close, NULL);
 }
 
 static PyObject *
@@ -1885,13 +1962,30 @@
 Alternate chain() contructor taking a single iterable argument\n\
 that evaluates lazily.");
 
+PyDoc_STRVAR(chain_send_doc,  /* same as send_doc in Objects/genobject.c */
+"chain.send(arg) -> send 'arg' into generator,\n\
+return next yielded value or raise StopIteration.");
+
+PyDoc_STRVAR(chain_throw_doc,  /* same as throw_doc in Objects/genobject.c */
+"chain.throw(typ[,val[,tb]]) -> raise exception in generator,\n\
+return next yielded value or raise StopIteration.");
+
+PyDoc_STRVAR(chain_close_doc,
+"close() -> raise GeneratorExit inside generator.");
+
 static PyMethodDef chain_methods[] = {
-    {"from_iterable", (PyCFunction) chain_new_from_iterable,            METH_O | METH_CLASS,
+    {"from_iterable",   (PyCFunction)chain_new_from_iterable, METH_O | METH_CLASS,
         chain_from_iterable_doc},
+    {"send",            (PyCFunction)chain_send,        METH_O,
+        chain_send_doc},
+    {"throw",           (PyCFunction)chain_throw,       METH_VARARGS,
+        chain_throw_doc},
+    {"close",           (PyCFunction)chain_close,       METH_NOARGS,
+        chain_close_doc},
     {"__reduce__",      (PyCFunction)chain_reduce,      METH_NOARGS,
-     reduce_doc},
+        reduce_doc},
     {"__setstate__",    (PyCFunction)chain_setstate,    METH_O,
-     setstate_doc},
+        setstate_doc},
     {NULL,              NULL}   /* sentinel */
 };
 
