summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jamadden@gmail.com>2021-10-05 17:50:09 -0500
committerJason Madden <jamadden@gmail.com>2021-10-12 08:53:58 -0500
commit4a35c59c8a1f090b87cb194d410b0e59920d7f71 (patch)
treedd3311b7e10c3fea25bde02809f9151fbed7612d
parent6c350ef1d4584be67e6dab74725717f275da9b54 (diff)
downloadgreenlet-4a35c59c8a1f090b87cb194d410b0e59920d7f71.tar.gz
Introduce PyMainGreenlet as a subtype of greenlet.
This way we don't have to grow the greenlet class by a pointer that is almost always empty. Also brings more type safety to the code.
-rw-r--r--src/greenlet/greenlet.cpp36
-rw-r--r--src/greenlet/greenlet.h5
-rw-r--r--src/greenlet/greenlet_compiler_compat.hpp4
-rw-r--r--src/greenlet/greenlet_internal.hpp25
-rw-r--r--src/greenlet/greenlet_thread_state.hpp28
5 files changed, 64 insertions, 34 deletions
diff --git a/src/greenlet/greenlet.cpp b/src/greenlet/greenlet.cpp
index 480a6fe..c3552c6 100644
--- a/src/greenlet/greenlet.cpp
+++ b/src/greenlet/greenlet.cpp
@@ -178,7 +178,7 @@ _ThreadStateCreator_Destroy(void* arg)
// Passed a borrowed reference to the main greenlet.
// main greenlet -> state -> main greenlet
// main greenlet -> main greenlet
- PyGreenlet* main = (PyGreenlet*)arg;
+ PyMainGreenlet* main = (PyMainGreenlet*)arg;
ThreadState* s = main->thread_state;
_GDPrint("PendingCall: Destroying main greenlet %p (refcount %ld)\n", main, Py_REFCNT(main));
// When we need to do cross-thread operations, we check this.
@@ -240,7 +240,7 @@ ThreadStateCreator_Destroy(ThreadState* state)
return;
}
- g_cleanup_queue.push_back(state->borrow_main_greenlet());
+ g_cleanup_queue.push_back((PyGreenlet*)state->borrow_main_greenlet());
if (g_cleanup_queue.size() == 1) {
// We added the first item to the queue. We need to schedule
// the cleanup.
@@ -322,7 +322,7 @@ public:
{
return this->state().borrow_origin();
};
- inline PyGreenlet* borrow_main_greenlet()
+ inline PyMainGreenlet* borrow_main_greenlet()
{
return this->state().borrow_main_greenlet();
};
@@ -372,20 +372,20 @@ green_clear_exc(PyGreenlet* g)
#endif
}
-static PyGreenlet*
+static PyMainGreenlet*
green_create_main(void)
{
- PyGreenlet* gmain;
+ PyMainGreenlet* gmain;
/* create the main greenlet for this thread */
- gmain = (PyGreenlet*)PyType_GenericAlloc(&PyGreenlet_Type, 0);
+ gmain = (PyMainGreenlet*)PyType_GenericAlloc(&PyMainGreenlet_Type, 0);
if (gmain == NULL) {
return NULL;
}
- gmain->stack_start = (char*)1;
- gmain->stack_stop = (char*)-1;
+ gmain->super.stack_start = (char*)1;
+ gmain->super.stack_stop = (char*)-1;
// circular reference; the pending call will clean this up.
- gmain->main_greenlet_s = gmain;
+ gmain->super.main_greenlet_s = gmain;
Py_INCREF(gmain);
assert(Py_REFCNT(gmain) == 2);
return gmain;
@@ -488,7 +488,7 @@ green_create_main(void)
// return g->run_info;
// }
-static PyGreenlet*
+static PyMainGreenlet*
find_and_borrow_main_greenlet_in_lineage(PyGreenlet* g)
{
while (!PyGreenlet_STARTED(g)) {
@@ -1566,11 +1566,17 @@ green_dealloc(PyGreenlet* self)
#endif
Py_CLEAR(self->dict);
assert(!PyErr_Occurred());
+ // XXX: We should never get here with a main greenlet that still
+ // has a thread state attached. If we do, that's a problem, right?
+ // (If it's not a problem, we can customize the dealloc function
+ // for the main greenlet class).
+#if 0
if (self->thread_state) {
// HMM. We shouldn't get here.
_GDPrint("DEALLOC MAIN GREENLET (%p) WITH THREAD STATE (%p)\n", self, self->thread_state);
assert(!self->thread_state);
}
+#endif
Py_TYPE(self)->tp_free((PyObject*)self);
assert(!PyErr_Occurred());
@@ -1838,14 +1844,14 @@ green_setparent(PyGreenlet* self, PyObject* nparent, void* c)
_GDPrint("Examining parent ");
_GDPoPrint((PyObject*)p);
_GDPrint("\n\tActive? %d Run info: %p\n", PyGreenlet_ACTIVE(p), p->main_greenlet_s);
- run_info = PyGreenlet_ACTIVE(p) ? p->main_greenlet_s : NULL;
+ run_info = PyGreenlet_ACTIVE(p) ? (PyGreenlet*)p->main_greenlet_s : NULL;
}
if (run_info == NULL) {
PyErr_SetString(PyExc_ValueError,
"parent must not be garbage collected");
return -1;
}
- if (PyGreenlet_STARTED(self) && self->main_greenlet_s != run_info) {
+ if (PyGreenlet_STARTED(self) && self->main_greenlet_s != (void*)run_info) {
PyErr_SetString(PyExc_ValueError,
"parent cannot be on a different thread");
return -1;
@@ -2244,6 +2250,7 @@ PyTypeObject PyGreenlet_Type = {
};
+
PyDoc_STRVAR(mod_getcurrent_doc,
"getcurrent() -> greenlet\n"
"\n"
@@ -2403,6 +2410,11 @@ init_greenlet(void)
if (PyType_Ready(&PyGreenlet_Type) < 0) {
INITERROR;
}
+ PyMainGreenlet_Type.tp_base = &PyGreenlet_Type;
+ if (PyType_Ready(&PyMainGreenlet_Type) < 0) {
+ INITERROR;
+ }
+
PyExc_GreenletError = PyErr_NewException("greenlet.error", NULL, NULL);
if (PyExc_GreenletError == NULL) {
INITERROR;
diff --git a/src/greenlet/greenlet.h b/src/greenlet/greenlet.h
index 8344809..1375bd9 100644
--- a/src/greenlet/greenlet.h
+++ b/src/greenlet/greenlet.h
@@ -15,7 +15,7 @@ extern "C" {
#define GREENLET_VERSION "1.0.0"
#ifndef GREENLET_MODULE
-#define state_ptr_t void*
+#define main_greenlet_ptr_t void*
#endif
typedef struct _greenlet {
@@ -28,7 +28,7 @@ typedef struct _greenlet {
struct _greenlet* parent;
/* strong reference, set when the greenlet begins to run. */
/* Used to be called run_info */
- struct _greenlet* main_greenlet_s;
+ main_greenlet_ptr_t main_greenlet_s;
struct _frame* top_frame; /* weak reference (suspended) or NULL (running) */
int recursion_depth;
PyObject* weakreflist;
@@ -52,7 +52,6 @@ typedef struct _greenlet {
// If we can get it down to just one pointer, we could
// re-purpose an existing field.
PyObject* run_callable;
- state_ptr_t thread_state;
} PyGreenlet;
#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type)
diff --git a/src/greenlet/greenlet_compiler_compat.hpp b/src/greenlet/greenlet_compiler_compat.hpp
index b22092c..b57b1d8 100644
--- a/src/greenlet/greenlet_compiler_compat.hpp
+++ b/src/greenlet/greenlet_compiler_compat.hpp
@@ -15,10 +15,10 @@ typedef unsigned long long uint64_t;
typedef signed long long int64_t;
typedef unsigned int uint32_t;
#define nullptr NULL
-#define G_DELETED_METHOD 0
+#define G_HAS_METHOD_DELETE 0
#else
#include <cstdint>
-#define G_DELETED_METHOD delete
+#define G_HAS_METHOD_DELETE 1
#endif
// CAUTION: MSVC is stupidly picky:
diff --git a/src/greenlet/greenlet_internal.hpp b/src/greenlet/greenlet_internal.hpp
index 746db75..4318f6a 100644
--- a/src/greenlet/greenlet_internal.hpp
+++ b/src/greenlet/greenlet_internal.hpp
@@ -13,16 +13,35 @@
namespace greenlet {
class ThreadState;
};
-#define state_ptr_t greenlet::ThreadState*
+struct _PyMainGreenlet;
+#define main_greenlet_ptr_t struct _PyMainGreenlet*
+
#include "greenlet.h"
#include "greenlet_compiler_compat.hpp"
#include "greenlet_cpython_compat.hpp"
-
#include <vector>
#include <memory>
+// Define a special type for the main greenlets. This way it can have
+// a thread state pointer without having to carry the expense of a
+// NULL field around on every other greenlet.
+// At the Python level, the main greenlet class is
+// *almost* indistinguisable from plain greenlets.
+typedef struct _PyMainGreenlet
+{
+ PyGreenlet super;
+ greenlet::ThreadState* thread_state;
+} PyMainGreenlet;
+
+static PyTypeObject PyMainGreenlet_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ .tp_name = "greenlet.greenlet",
+ .tp_basicsize = sizeof(PyMainGreenlet)
+};
+
+
#define UNUSED(expr) do { (void)(expr); } while (0)
namespace greenlet {
@@ -90,7 +109,7 @@ typedef std::vector<PyGreenlet*, PythonAllocator<PyGreenlet*> > g_deleteme_t;
/**
* Forward declarations needed in multiple files.
*/
-static PyGreenlet* green_create_main();
+static PyMainGreenlet* green_create_main();
static PyObject* green_switch(PyGreenlet* self, PyObject* args, PyObject* kwargs);
diff --git a/src/greenlet/greenlet_thread_state.hpp b/src/greenlet/greenlet_thread_state.hpp
index af36f8c..9b7952d 100644
--- a/src/greenlet/greenlet_thread_state.hpp
+++ b/src/greenlet/greenlet_thread_state.hpp
@@ -53,7 +53,7 @@ private:
// Adding the vector takes us up to 80 bytes ()
/* Strong reference to the main greenlet */
- PyGreenlet* main_greenlet_s;
+ PyMainGreenlet* main_greenlet_s;
/* Strong reference to the current greenlet. */
PyGreenlet* current_greenlet_s;
@@ -111,7 +111,7 @@ public:
return this->main_greenlet_s != nullptr;
}
- inline PyGreenlet* borrow_main_greenlet()
+ inline PyMainGreenlet* borrow_main_greenlet()
{
if (!this->main_greenlet_s) {
assert(!this->current_greenlet_s);
@@ -119,16 +119,16 @@ public:
this->main_greenlet_s = green_create_main();
if (this->main_greenlet_s) {
this->main_greenlet_s->thread_state = this;
- this->set_current(this->main_greenlet_s);
+ this->set_current((PyGreenlet*)this->main_greenlet_s);
assert(Py_REFCNT(this->main_greenlet_s) == 3);
}
}
return this->main_greenlet_s;
};
- inline PyGreenlet* get_main_greenlet()
+ inline PyMainGreenlet* get_main_greenlet()
{
- PyGreenlet* g = this->borrow_main_greenlet();
+ PyMainGreenlet* g = this->borrow_main_greenlet();
Py_XINCREF(g);
return g;
}
@@ -277,7 +277,7 @@ public:
*/
assert(this->current_greenlet_s->main_greenlet_s == this->main_greenlet_s);
- assert(this->main_greenlet_s->main_greenlet_s == this->main_greenlet_s);
+ assert(this->main_greenlet_s->super.main_greenlet_s == this->main_greenlet_s);
this->clear_deleteme_list();
return true;
};
@@ -401,18 +401,18 @@ public:
// It's possible that there is some other greenlet that
// switched to us, leaving a reference to the main greenlet
// on the stack, somewhere uncollectable. Try to detect that.
- if (this->current_greenlet_s == this->main_greenlet_s && this->current_greenlet_s) {
+ if (this->current_greenlet_s == (void*)this->main_greenlet_s && this->current_greenlet_s) {
assert(PyGreenlet_MAIN(this->main_greenlet_s));
assert(!this->current_greenlet_s->top_frame);
- assert(this->main_greenlet_s->main_greenlet_s == this->main_greenlet_s);
+ assert(this->main_greenlet_s->super.main_greenlet_s == this->main_greenlet_s);
// Break a cycle we know about, the self reference
// the main greenlet keeps.
- Py_CLEAR(this->main_greenlet_s->main_greenlet_s);
+ Py_CLEAR(this->main_greenlet_s->super.main_greenlet_s);
// Drop one reference we hold.
Py_CLEAR(this->current_greenlet_s);
// Only our reference to the main greenlet should be left,
// But hold onto the pointer in case we need to do extra cleanup.
- PyGreenlet* old_main_greenlet = this->main_greenlet_s;
+ PyMainGreenlet* old_main_greenlet = this->main_greenlet_s;
Py_ssize_t cnt = Py_REFCNT(this->main_greenlet_s);
Py_CLEAR(this->main_greenlet_s);
_GDPrint("Destructor: Init refcount: %ld Current: %ld\n",
@@ -519,7 +519,7 @@ public:
// when the thread exited (because we already cleared this
// pointer if it was). This shouldn't be possible?
- if (this->main_greenlet_s->top_frame) {
+ if (this->main_greenlet_s->super.top_frame) {
_GDPrint("The main greenlet had a frame: %p (refcount %ld)",
this->main_greenlet_s,
Py_REFCNT(this->main_greenlet_s->top_frame)
@@ -534,10 +534,10 @@ public:
// If the main greenlet was current when the thread died (it
// should be, right?) then we cleared its self pointer above
// when we cleared the current greenlet's main greenlet pointer.
- assert(this->main_greenlet_s->main_greenlet_s == this->main_greenlet_s
- || !this->main_greenlet_s->main_greenlet_s);
+ assert(this->main_greenlet_s->super.main_greenlet_s == this->main_greenlet_s
+ || !this->main_greenlet_s->super.main_greenlet_s);
// self reference, probably gone
- Py_CLEAR(this->main_greenlet_s->main_greenlet_s);
+ Py_CLEAR(this->main_greenlet_s->super.main_greenlet_s);
_GDPrint("\tFinishing double decref of main greenlet %p Refcount: %ld\n",
this->main_greenlet_s,