/* * Get the current Fiber reference on the current thread. The first time this * function is called on a given (real) thread, the main Fiber is created. */ static Fiber * get_current(void) { Fiber *current; PyObject *tstate_dict; /* get current Fiber from the active thread-state */ tstate_dict = PyThreadState_GetDict(); if (tstate_dict == NULL) { if (!PyErr_Occurred()) { PyErr_NoMemory(); } return NULL; } current = (Fiber *)PyDict_GetItem(tstate_dict, current_fiber_key); if (current == NULL) { current = fiber_create_main(); if (current == NULL) { return NULL; } /* Keep a reference to the main fiber in the thread dict. The main * fiber is special because we don't require the user to keep a * reference to it. It should be deleted when the thread exits. */ if (PyDict_SetItem(tstate_dict, main_fiber_key, (PyObject *) current) < 0) { Py_DECREF(current); return NULL; } /* current starts out as main */ if (PyDict_SetItem(tstate_dict, current_fiber_key, (PyObject *) current) < 0) { Py_DECREF(current); return NULL; } /* return a borrowed ref. refcount should be 2 after this */ Py_DECREF(current); } ASSERT(current != NULL); return current; }
/* * Update the current Fiber reference on the current thread. The first time this * function is called on a given (real) thread, the main Fiber is created. */ static Fiber * update_current(void) { Fiber *current, *previous; PyObject *exc, *val, *tb, *tstate_dict; restart: /* save current exception */ PyErr_Fetch(&exc, &val, &tb); /* get current Fiber from the active thread-state */ tstate_dict = PyThreadState_GetDict(); if (tstate_dict == NULL) { if (!PyErr_Occurred()) { PyErr_NoMemory(); } return NULL; } current = (Fiber *)PyDict_GetItem(tstate_dict, current_fiber_key); if (current) { /* found - remove it, to avoid keeping a ref */ Py_INCREF(current); PyDict_DelItem(tstate_dict, current_fiber_key); } else { /* first time we see this thread-state, create main Fiber */ current = fiber_create_main(); if (current == NULL) { Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return NULL; } if (_global_state.current == NULL) { /* First time a main Fiber is allocated in any thread */ _global_state.current = current; } } ASSERT(current->ts_dict == tstate_dict); retry: Py_INCREF(current); previous = _global_state.current; _global_state.current = current; if (PyDict_GetItem(previous->ts_dict, current_fiber_key) != (PyObject *)previous) { /* save previous as the current Fiber of its own (real) thread */ if (PyDict_SetItem(previous->ts_dict, current_fiber_key, (PyObject*) previous) < 0) { Py_DECREF(previous); Py_DECREF(current); Py_XDECREF(exc); Py_XDECREF(val); Py_XDECREF(tb); return NULL; } Py_DECREF(previous); } ASSERT(Py_REFCNT(previous) >= 1); if (_global_state.current != current) { /* some Python code executed above and there was a thread switch, * so the global current points to some other Fiber again. We need to * delete current_fiber_key and retry. */ PyDict_DelItem(tstate_dict, current_fiber_key); goto retry; } /* release the extra reference */ Py_DECREF(current); /* restore current exception */ PyErr_Restore(exc, val, tb); /* thread switch could happen during PyErr_Restore, in that case there's nothing to do except restart from scratch. */ if (_global_state.current->ts_dict != tstate_dict) { goto restart; } return current; }