Beispiel #1
0
/*
 * Create main Fiber. There is always a main Fiber for a given (real) thread,
 * and it's parent is always NULL.
 */
static Fiber *
fiber_create_main(void)
{
    Fiber *t_main;
    PyObject *dict = PyThreadState_GetDict();
    PyTypeObject *cls = (PyTypeObject *)&FiberType;

    if (dict == NULL) {
        if (!PyErr_Occurred()) {
            PyErr_NoMemory();
        }
        return NULL;
    }

    /* create the main Fiber for this thread */
    t_main = (Fiber *)cls->tp_new(cls, NULL, NULL);
    if (!t_main) {
        return NULL;
    }
    Py_INCREF(dict);
    t_main->ts_dict = dict;
    t_main->parent = NULL;
    t_main->thread_h = stacklet_newthread();
    t_main->stacklet_h = NULL;
    t_main->initialized = True;
    t_main->is_main = True;
    return t_main;
}
Beispiel #2
0
extern "C" int Py_ReprEnter(PyObject* obj) noexcept {
    PyObject* dict;
    PyObject* list;
    Py_ssize_t i;

    dict = PyThreadState_GetDict();
    if (dict == NULL)
        return 0;
    list = PyDict_GetItemString(dict, KEY);
    if (list == NULL) {
        list = PyList_New(0);
        if (list == NULL)
            return -1;
        if (PyDict_SetItemString(dict, KEY, list) < 0)
            return -1;
        Py_DECREF(list);
    }
    i = PyList_GET_SIZE(list);
    while (--i >= 0) {
        if (PyList_GET_ITEM(list, i) == obj)
            return 1;
    }
    PyList_Append(list, obj);
    return 0;
}
// dtor
static void
AtTime_dealloc( PyObject* self ) {
	AtTime * at = (AtTime*)self;
	
	PyObject * tsd = PyThreadState_GetDict();
	PyObject * atd = PyDict_GetItemString(tsd, "_AtTime" );
	if( atd ) {
		PyObject * cur_stack = PyDict_GetItemString( atd, "current_stack" );
		PyObject * time_stack = PyDict_GetItemString( atd, "time_stack" );
		
		Py_ssize_t time_top = PyList_Size(time_stack) - 1;
		PyObject * hashKey = PyLong_FromLong( PyObject_Hash((PyObject*)self) );
		Py_ssize_t pos = PySequence_Index(cur_stack, hashKey);
		Py_DECREF(hashKey);
		
		// Restore the correct time value if we are current
		if( PyList_Size(cur_stack) == pos + 1 ) {
			thread_local(current_time) = PyLong_AsLong( PyList_GetItem( time_stack, time_top ) );
			//PySys_WriteStdout( "Current AtTime object destroyed, restoring thread_local(current_time) to %i\n", thread_local(current_time) );
			PySequence_DelItem(time_stack,time_top);
		} else
			// If we aren't current, then we delete the time at the position one above where we are in the current_stack
			PySequence_DelItem(time_stack,pos+1);
		
		PySequence_DelItem(cur_stack,pos);
		
		// If we are the last AtTime object local to this thread, then remove the _AtTime thread-local dict
		if( PyList_Size(cur_stack) == 0 ) {
			thread_local(use_time_context) = (PyDict_GetItemString( atd, "restore_use_time_context" ) == Py_True) ? TRUE : FALSE;
			//PySys_WriteStdout( "Last AtTime object destroyed, setting thread_local(use_time_context) to FALSE\n" );
			PyDict_DelItemString(tsd, "_AtTime");
		}
	}
	self->ob_type->tp_free(self);
}
Beispiel #4
0
static PyObject*
get_inprogress_dict(void)
{
	static PyObject *key;
	PyObject *tstate_dict, *inprogress;

	if (key == NULL) {
		key = PyString_InternFromString("cmp_state");
		if (key == NULL)
			return NULL;
	}

	tstate_dict = PyThreadState_GetDict();
	if (tstate_dict == NULL) {
		PyErr_BadInternalCall();
		return NULL;
	} 

	inprogress = PyDict_GetItem(tstate_dict, key); 
	if (inprogress == NULL) {
		inprogress = PyDict_New();
		if (inprogress == NULL)
			return NULL;
		if (PyDict_SetItem(tstate_dict, key, inprogress) == -1) {
		    Py_DECREF(inprogress);
		    return NULL;
		}
		Py_DECREF(inprogress);
	}

	return inprogress;
}
static PyObject *
_ldict(localobject *self)
{
	PyObject *tdict, *ldict;

	tdict = PyThreadState_GetDict();
	if (tdict == NULL) {
		PyErr_SetString(PyExc_SystemError,
				"Couldn't get thread-state dictionary");
		return NULL;
	}

	ldict = PyDict_GetItem(tdict, self->key);
	if (ldict == NULL) {
		ldict = PyDict_New(); /* we own ldict */

		if (ldict == NULL)
			return NULL;
		else {
			int i = PyDict_SetItem(tdict, self->key, ldict);
			Py_DECREF(ldict); /* now ldict is borrowed */
			if (i < 0) 
				return NULL;
		}

		Py_CLEAR(self->dict);
		Py_INCREF(ldict);
		self->dict = ldict; /* still borrowed */

		if (Py_TYPE(self)->tp_init != PyBaseObject_Type.tp_init &&
		    Py_TYPE(self)->tp_init((PyObject*)self, 
					   self->args, self->kw) < 0) {
			/* we need to get rid of ldict from thread so
			   we create a new one the next time we do an attr
			   acces */
			PyDict_DelItem(tdict, self->key);
			return NULL;
		}
		
	}

	/* The call to tp_init above may have caused another thread to run.
	   Install our ldict again. */
	if (self->dict != ldict) {
		Py_CLEAR(self->dict);
		Py_INCREF(ldict);
		self->dict = ldict;
	}

	return ldict;
}
static PyObject *
local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
	localobject *self;
	PyObject *tdict;

	// 好囧的判断.
	if (type->tp_init == PyBaseObject_Type.tp_init
	    &&
		((args && PyObject_IsTrue(args)) || (kw && PyObject_IsTrue(kw)))) {
		PyErr_SetString(PyExc_TypeError,
			  "Initialization arguments are not supported");
		return NULL;
	}

	self = (localobject *)type->tp_alloc(type, 0);
	if (self == NULL)
		return NULL;

	Py_XINCREF(args);
	self->args = args;
	Py_XINCREF(kw);
	self->kw = kw;
	self->dict = NULL;	/* making sure */
	self->key = PyString_FromFormat("thread.local.%p", self);
	if (self->key == NULL) 
		goto err;

	self->dict = PyDict_New();
	if (self->dict == NULL)
		goto err;

	tdict = PyThreadState_GetDict();
	if (tdict == NULL) {
		PyErr_SetString(PyExc_SystemError,
				"Couldn't get thread-state dictionary");
		goto err;
	}

	// dict里存的是localobject里的(key, value)的引用, 参考local_dealloc(), localobject ref -> 0时, 会进行清理.
	// 也就是不能认为thread._local()提供了一个thread global的存储, thread global要通过 returns of thread._local()的生命周期保证.
	if (PyDict_SetItem(tdict, self->key, self->dict) < 0)
		goto err;

	return (PyObject *)self;

  err:
	Py_DECREF(self);
	return NULL;
}
static PyObject * AtTime_call( AtTime* self, PyObject* args, PyObject* kwds )
{
	if( !PyTuple_Check(args) || (PyTuple_Size(args) != 1) || (!PyNumber_Check(PyTuple_GetItem(args,0))) ) {
		PyErr_SetString( PyExc_AttributeError, "Calling AtTime instances require a single time(int) argument" );
		return 0;
	}
	
	int timeValue = PyInt_AsLong(PyTuple_GetItem(args,0));
	if( PyErr_Occurred() )
		return 0;
	
	PyObject * tsd = PyThreadState_GetDict();
	PyObject * atd = PyDict_GetItemString(tsd, "_AtTime" );
	
	// New reference is either transfered to a list, or decremented below
	PyObject * hashKey = PyLong_FromLong( PyObject_Hash((PyObject*)self) );
	
	if( !atd ) {
		
		// New ref
		atd = PyDict_New();
		
		PyDict_SetItemString( tsd, "_AtTime", atd );
		// tsd now has a ref to atd
		Py_DECREF(atd);
		
		// New refs
		PyObject * time_stack = PyList_New(1), * cur_stack = PyList_New(1);
		
		// PyList_SetItem steals a reference
		PyList_SetItem( time_stack, 0, PyLong_FromLong(thread_local(current_time)) );
		// Give our hashKey reference to cur_stack
		PyList_SetItem( cur_stack, 0, hashKey );
		
		// Dicts take their own reference, so we release ours after this
		PyDict_SetItemString( atd, "time_stack", time_stack );
		PyDict_SetItemString( atd, "current_stack", cur_stack );
		Py_DECREF(time_stack);
		Py_DECREF(cur_stack);
		
		PyObject * utl = PyBool_FromLong( thread_local(use_time_context) );
		// takes it's own ref, so we release ours
		PyDict_SetItemString( atd, "restore_use_time_context", utl );
		Py_DECREF(utl);
		
		thread_local(use_time_context) = TRUE;
		//PySys_WriteStdout( "First AtTime struct activated, setting thread_local(use_time_context) to TRUE\n" );
	} else {
static PyObject *
local_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
	localobject *self;
	PyObject *tdict;

	if (type->tp_init == PyBaseObject_Type.tp_init
	    && ((args && PyObject_IsTrue(args))
		|| (kw && PyObject_IsTrue(kw)))) {
		PyErr_SetString(PyExc_TypeError,
			  "Initialization arguments are not supported");
		return NULL;
	}

	self = (localobject *)type->tp_alloc(type, 0);
	if (self == NULL)
		return NULL;

	Py_XINCREF(args);
	self->args = args;
	Py_XINCREF(kw);
	self->kw = kw;
	self->dict = NULL;	/* making sure */
	self->key = PyUnicode_FromFormat("thread.local.%p", self);
	if (self->key == NULL) 
		goto err;

	self->dict = PyDict_New();
	if (self->dict == NULL)
		goto err;

	tdict = PyThreadState_GetDict();
	if (tdict == NULL) {
		PyErr_SetString(PyExc_SystemError,
				"Couldn't get thread-state dictionary");
		goto err;
	}

	if (PyDict_SetItem(tdict, self->key, self->dict) < 0)
		goto err;

	return (PyObject *)self;

  err:
	Py_DECREF(self);
	return NULL;
}
Beispiel #9
0
static uintptr_t
_current_context_id(PyThreadState *ts)
{
    uintptr_t rc;
    PyObject *callback_rc;
    if (context_id_callback) {
        callback_rc = PyObject_CallFunctionObjArgs(context_id_callback, NULL);
        if (!callback_rc) {
            PyErr_Print();
            goto error;
        }
        rc = (uintptr_t)PyLong_AsLong(callback_rc);
        Py_DECREF(callback_rc);
        if (PyErr_Occurred()) {
            yerr("context id callback returned non-integer");
            goto error;
        }
        return rc;
    } else {
        // Use thread_id instead of ts pointer, because when we create/delete many threads, some
        // of them do not show up in the thread_stats, because ts pointers are recycled in the VM.
        // Also, OS tids are recycled, too. The only valid way is to give ctx's custom tids which
        // are hold in a per-thread structure. Again: we use an integer instead of directly mapping the ctx
        // pointer to some per-thread structure because other threading libraries do not necessarily
        // have direct ThreadState->Thread mapping. Greenlets, for example, will only have a single
        // thread. Therefore, we need to identify the "context" concept independent from ThreadState 
        // objects.

        // TODO: Any more optimization? This has increased the runtime factor from 7x to 11x.
        // and also we may have a memory leak below. We maybe can optimize the common case.
        PyObject *d = PyThreadState_GetDict();
        PyObject *ytid = PyDict_GetItemString(d, "_yappi_tid");
        if (!ytid) {
            ytid = PyLong_FromLong(ycurthreadindex++);
            PyDict_SetItemString(d, "_yappi_tid", ytid);
        }
        rc = PyLong_AsLong(ytid);
        return rc;
    }

error:
    PyErr_Clear();
    Py_CLEAR(context_id_callback); // don't use callback again
    return 0;
}
Beispiel #10
0
NPY_NO_EXPORT PyObject *
get_global_ext_obj(void)
{
    PyObject *thedict;
    PyObject *ref = NULL;

#if USE_USE_DEFAULTS==1
    if (PyUFunc_NUM_NODEFAULTS != 0) {
#endif
        thedict = PyThreadState_GetDict();
        if (thedict == NULL) {
            thedict = PyEval_GetBuiltins();
        }
        ref = PyDict_GetItem(thedict, npy_um_str_pyvals_name);
#if USE_USE_DEFAULTS==1
    }
#endif

    return ref;
}
Beispiel #11
0
static PyGreenlet* green_create_main(void)
{
	PyGreenlet* gmain;
	PyObject* dict = PyThreadState_GetDict();
	if (dict == NULL) {
		if (!PyErr_Occurred())
			PyErr_NoMemory();
		return NULL;
	}

	/* create the main greenlet for this thread */
	gmain = (PyGreenlet*) PyType_GenericAlloc(&PyGreenlet_Type, 0);
	if (gmain == NULL)
		return NULL;
	gmain->stack_start = (char*) 1;
	gmain->stack_stop = (char*) -1;
	gmain->run_info = dict;
	Py_INCREF(dict);
	return gmain;
}
Beispiel #12
0
extern "C" void Py_ReprLeave(PyObject* obj) noexcept {
    PyObject* dict;
    PyObject* list;
    Py_ssize_t i;

    dict = PyThreadState_GetDict();
    if (dict == NULL)
        return;
    list = PyDict_GetItemString(dict, KEY);
    if (list == NULL || !PyList_Check(list))
        return;
    i = PyList_GET_SIZE(list);
    /* Count backwards because we always expect obj to be list[-1] */
    while (--i >= 0) {
        if (PyList_GET_ITEM(list, i) == obj) {
            PyList_SetSlice(list, i, i + 1, NULL);
            break;
        }
    }
}
Beispiel #13
0
PyObject* GetClassForThread(const char* szModule, const char* szClass)
{
    // Returns the given class, specific to the current thread's interpreter.  For performance
    // these are cached for each thread.
    //
    // This is for internal use only, so we'll cache using only the class name.  Make sure they
    // are unique.  (That is, don't try to import classes with the same name from two different
    // modules.)

    PyObject* dict = PyThreadState_GetDict();
    I(dict);
    if (dict == 0)
    {
        // I don't know why there wouldn't be thread state so I'm going to raise an exception
        // unless I find more info.
        return PyErr_Format(PyExc_Exception, "pyodbc: PyThreadState_GetDict returned NULL");
    }

    // Check the cache.  GetItemString returns a borrowed reference.
    PyObject* cls = PyDict_GetItemString(dict, szClass);
    if (cls)
    {
        Py_INCREF(cls);
        return cls;
    }

    // Import the class and cache it.  GetAttrString returns a new reference.
    PyObject* mod = PyImport_ImportModule(szModule);
    if (!mod)
        return 0;

    cls = PyObject_GetAttrString(mod, szClass);
    Py_DECREF(mod);
    if (!cls)
        return 0;

    // SetItemString increments the refcount (not documented)
    PyDict_SetItemString(dict, szClass, cls);

    return cls;
}
Beispiel #14
0
/*
 * 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;
}
Beispiel #15
0
DEFINEFN
PyObject* psyco_thread_dict()
{
  PyObject* dict = PyThreadState_GetDict();
  PyObject* result;
  bool err;

  if (dict == NULL)
    return NULL;
  result = PyDict_GetItem(dict, thread_dict_key);
  if (result == NULL)
    {
      result = PyDict_New();
      if (result == NULL)
        return NULL;
      err = PyDict_SetItem(dict, thread_dict_key, result);
      Py_DECREF(result);  /* one reference left in 'dict' */
      if (err)
        result = NULL;
    }
  return result;
}
Beispiel #16
0
/*
  This function creates and returns a thread-local Python object that has
  space to store two integer error numbers; once created the Python object is
  kept alive in the thread state dictionary as long as the thread itself.
*/
PyObject *
_ctypes_get_errobj(int **pspace)
{
	PyObject *dict = PyThreadState_GetDict();
	PyObject *errobj;
	static PyObject *error_object_name;
	if (dict == 0) {
		PyErr_SetString(PyExc_RuntimeError,
				"cannot get thread state");
		return NULL;
	}
	if (error_object_name == NULL) {
		error_object_name = PyUnicode_InternFromString("ctypes.error_object");
		if (error_object_name == NULL)
			return NULL;
	}
	errobj = PyDict_GetItem(dict, error_object_name);
	if (errobj)
		Py_INCREF(errobj);
	else {
		void *space = PyMem_Malloc(sizeof(int) * 2);
		if (space == NULL)
			return NULL;
		memset(space, 0, sizeof(int) * 2);
		errobj = PyCapsule_New(space, CTYPES_CAPSULE_NAME_PYMEM, pymem_destructor);
		if (errobj == NULL)
			return NULL;
		if (-1 == PyDict_SetItem(dict, error_object_name,
					 errobj)) {
			Py_DECREF(errobj);
			return NULL;
		}
	}
	*pspace = (int *)PyCapsule_GetPointer(errobj, CTYPES_CAPSULE_NAME_PYMEM);
	return errobj;
}
Beispiel #17
0
/*
  This function creates and returns a thread-local Python object that has
  space to store two integer error numbers; once created the Python object is
  kept alive in the thread state dictionary as long as the thread itself.
*/
PyObject *
get_error_object(int **pspace)
{
	PyObject *dict = PyThreadState_GetDict();
	PyObject *errobj;
	static PyObject *error_object_name;
	if (dict == 0) {
		PyErr_SetString(PyExc_RuntimeError,
				"cannot get thread state");
		return NULL;
	}
	if (error_object_name == NULL) {
		error_object_name = PyString_InternFromString("ctypes.error_object");
		if (error_object_name == NULL)
			return NULL;
	}
	errobj = PyDict_GetItem(dict, error_object_name);
	if (errobj)
		Py_INCREF(errobj);
	else {
		void *space = PyMem_Malloc(sizeof(int) * 2);
		if (space == NULL)
			return NULL;
		memset(space, 0, sizeof(int) * 2);
		errobj = PyCObject_FromVoidPtr(space, PyMem_Free);
		if (errobj == NULL)
			return NULL;
		if (-1 == PyDict_SetItem(dict, error_object_name,
					 errobj)) {
			Py_DECREF(errobj);
			return NULL;
		}
	}
	*pspace = (int *)PyCObject_AsVoidPtr(errobj);
	return errobj;
}
bool py_execute(PyCodeObject* py_func, DSMSession* sc_sess, 
		DSMCondition::EventType event, map<string,string>* event_params,
		bool expect_int_result) {
  // acquire the GIL
  PYLOCK;

  bool py_res = false;
  DBG("add main \n");
  PyObject* m = PyImport_AddModule("__main__");
  if (m == NULL) {
    ERROR("getting main module\n");
    return false;
  }
  DBG("get globals \n");
  PyObject* globals = PyModule_GetDict(m);
  PyObject* locals = getPyLocals(sc_sess);

  PyObject* params = PyDict_New();
  if (NULL != event_params) {
    for (map<string,string>::iterator it=event_params->begin(); 
	 it != event_params->end(); it++) {
      PyObject* v = PyString_FromString(it->second.c_str());
      PyDict_SetItemString(params, it->first.c_str(), v);
      Py_DECREF(v);
    }
  }
  PyDict_SetItemString(locals, "params", params);

  PyObject *t = PyInt_FromLong(event);
  PyDict_SetItemString(locals, "type", t);

  PyObject* py_sc_sess = PyCObject_FromVoidPtr(sc_sess,NULL);
  PyObject* ts_dict = PyThreadState_GetDict();
  PyDict_SetItemString(ts_dict, "_dsm_sess_", py_sc_sess);
  Py_DECREF(py_sc_sess);

  // call the function
  PyObject* res = PyEval_EvalCode((PyCodeObject*)py_func, globals, locals);

  if(PyErr_Occurred())
    PyErr_Print();

  PyDict_DelItemString(locals, "params");
  PyDict_Clear(params);
  Py_DECREF(params);

  PyDict_DelItemString(locals, "type");
  Py_DECREF(t);
  
  //   ts_dict = PyThreadState_GetDict(); // should be the same as before
  PyDict_DelItemString(ts_dict, "_dsm_sess_");
  
  if (NULL == res) {
    ERROR("evaluating python code\n");
  } else if (PyBool_Check(res)) {
    py_res = PyInt_AsLong(res);
    Py_DECREF(res);
  } else {
    if (expect_int_result) {
      ERROR("unknown result from python code\n");
    }
    Py_DECREF(res);
  }

  return py_res;
}
Beispiel #19
0
static int
Fiber_tp_init(Fiber *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = {"target", "args", "kwargs", "parent", NULL};

    PyObject *target, *t_args, *t_kwargs;
    Fiber *parent;
    target = t_args = t_kwargs = NULL;
    parent = NULL;

    if (self->initialized) {
        PyErr_SetString(PyExc_RuntimeError, "object was already initialized");
        return -1;
    }

    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOO!:__init__", kwlist, &target, &t_args, &t_kwargs, &FiberType, &parent)) {
        return -1;
    }

    if (!CHECK_STATE) {
        return -1;
    }

    if (parent) {
        /* check if parent is on the same (real) thread */
        if (parent->ts_dict != PyThreadState_GetDict()) {
            PyErr_SetString(PyExc_FiberError, "parent cannot be on a different thread");
            return -1;
        }
        if (parent->stacklet_h == EMPTY_STACKLET_HANDLE) {
            PyErr_SetString(PyExc_ValueError, "parent must not have ended");
            return -1;
        }
    } else {
        parent = _global_state.current;
    }

    if (target) {
        if (!PyCallable_Check(target)) {
            PyErr_SetString(PyExc_TypeError, "if specified, target must be a callable");
            return -1;
        }
        if (t_args) {
            if (!PyTuple_Check(t_args)) {
                PyErr_SetString(PyExc_TypeError, "args must be a tuple");
                return -1;
            }
        } else {
            t_args = PyTuple_New(0);
        }
        if (t_kwargs) {
            if (!PyDict_Check(t_kwargs)) {
                PyErr_SetString(PyExc_TypeError, "kwargs must be a dict");
                return -1;
            }
        }
    }

    Py_XINCREF(target);
    Py_XINCREF(t_args);
    Py_XINCREF(t_kwargs);
    self->target = target;
    self->args = t_args;
    self->kwargs = t_kwargs;

    Py_INCREF(parent);
    self->parent = parent;
    self->thread_h = parent->thread_h;
    self->ts_dict = parent->ts_dict;
    Py_INCREF(self->ts_dict);

    self->initialized = True;
    return 0;
}
Beispiel #20
0
/*
 * 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;
}