Qucs-S S-parameter Viewer & RF Synthesis Tools
Loading...
Searching...
No Matches
TThreadState.hpp
1#ifndef GREENLET_THREAD_STATE_HPP
2#define GREENLET_THREAD_STATE_HPP
3
4#include <ctime>
5#include <stdexcept>
6#include <atomic>
7
8#include "greenlet_internal.hpp"
9#include "greenlet_refs.hpp"
10#include "greenlet_thread_support.hpp"
11
25
26namespace greenlet {
88private:
89 // As of commit 08ad1dd7012b101db953f492e0021fb08634afad
90 // this class needed 56 bytes in o Py_DEBUG build
91 // on 64-bit macOS 11.
92 // Adding the vector takes us up to 80 bytes ()
93
94 /* Strong reference to the main greenlet */
95 OwnedMainGreenlet main_greenlet;
96
97 /* Strong reference to the current greenlet. */
98 OwnedGreenlet current_greenlet;
99
100 /* Strong reference to the trace function, if any. */
101 OwnedObject tracefunc;
102
103 typedef std::vector<PyGreenlet*, PythonAllocator<PyGreenlet*> > deleteme_t;
104 /* A vector of raw PyGreenlet pointers representing things that need
105 deleted when this thread is running. The vector owns the
106 references, but you need to manually INCREF/DECREF as you use
107 them. We don't use a vector<refs::OwnedGreenlet> because we
108 make copy of this vector, and that would become O(n) as all the
109 refcounts are incremented in the copy.
110 */
111 deleteme_t deleteme;
112
113#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
114 void* exception_state;
115#endif
116
117#ifdef Py_GIL_DISABLED
118 static std::atomic<std::clock_t> _clocks_used_doing_gc;
119#else
120 static std::clock_t _clocks_used_doing_gc;
121#endif
122 static ImmortalString get_referrers_name;
123 static PythonAllocator<ThreadState> allocator;
124
125 G_NO_COPIES_OF_CLS(ThreadState);
126
127
128 // Allocates a main greenlet for the thread state. If this fails,
129 // exits the process. Called only during constructing a ThreadState.
130 MainGreenlet* alloc_main()
131 {
132 PyGreenlet* gmain;
133
134 /* create the main greenlet for this thread */
135 gmain = reinterpret_cast<PyGreenlet*>(PyType_GenericAlloc(&PyGreenlet_Type, 0));
136 if (gmain == NULL) {
137 throw PyFatalError("alloc_main failed to alloc"); //exits the process
138 }
139
140 MainGreenlet* const main = new MainGreenlet(gmain, this);
141
142 assert(Py_REFCNT(gmain) == 1);
143 assert(gmain->pimpl == main);
144 return main;
145 }
146
147
148public:
149 static void* operator new(size_t UNUSED(count))
150 {
151 return ThreadState::allocator.allocate(1);
152 }
153
154 static void operator delete(void* ptr)
155 {
156 return ThreadState::allocator.deallocate(static_cast<ThreadState*>(ptr),
157 1);
158 }
159
160 static void init()
161 {
162 ThreadState::get_referrers_name = "get_referrers";
163 ThreadState::set_clocks_used_doing_gc(0);
164 }
165
167 {
168
169#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
170 this->exception_state = slp_get_exception_state();
171#endif
172
173 // XXX: Potentially dangerous, exposing a not fully
174 // constructed object.
175 MainGreenlet* const main = this->alloc_main();
176 this->main_greenlet = OwnedMainGreenlet::consuming(
177 main->self()
178 );
179 assert(this->main_greenlet);
180 this->current_greenlet = main->self();
181 // The main greenlet starts with 1 refs: The returned one. We
182 // then copied it to the current greenlet.
183 assert(this->main_greenlet.REFCNT() == 2);
184 }
185
186 inline void restore_exception_state()
187 {
188#ifdef GREENLET_NEEDS_EXCEPTION_STATE_SAVED
189 // It's probably important this be inlined and only call C
190 // functions to avoid adding an SEH frame.
191 slp_set_exception_state(this->exception_state);
192#endif
193 }
194
195 inline bool has_main_greenlet() const noexcept
196 {
197 return bool(this->main_greenlet);
198 }
199
200 // Called from the ThreadStateCreator when we're in non-standard
201 // threading mode. In that case, there is an object in the Python
202 // thread state dictionary that points to us. The main greenlet
203 // also traverses into us, in which case it's crucial not to
204 // traverse back into the main greenlet.
205 int tp_traverse(visitproc visit, void* arg, bool traverse_main=true)
206 {
207 if (traverse_main) {
208 Py_VISIT(main_greenlet.borrow_o());
209 }
210 if (traverse_main || current_greenlet != main_greenlet) {
211 Py_VISIT(current_greenlet.borrow_o());
212 }
213 Py_VISIT(tracefunc.borrow());
214 return 0;
215 }
216
217 inline BorrowedMainGreenlet borrow_main_greenlet() const noexcept
218 {
219 assert(this->main_greenlet);
220 assert(this->main_greenlet.REFCNT() >= 2);
221 return this->main_greenlet;
222 };
223
224 inline OwnedMainGreenlet get_main_greenlet() const noexcept
225 {
226 return this->main_greenlet;
227 }
228
233 inline OwnedGreenlet get_current()
234 {
235 /* green_dealloc() cannot delete greenlets from other threads, so
236 it stores them in the thread dict; delete them now. */
237 this->clear_deleteme_list();
238 //assert(this->current_greenlet->main_greenlet == this->main_greenlet);
239 //assert(this->main_greenlet->main_greenlet == this->main_greenlet);
240 return this->current_greenlet;
241 }
242
246 inline BorrowedGreenlet borrow_current()
247 {
248 this->clear_deleteme_list();
249 return this->current_greenlet;
250 }
251
255 inline OwnedGreenlet get_current() const
256 {
257 return this->current_greenlet;
258 }
259
260 template<typename T, refs::TypeChecker TC>
261 inline bool is_current(const refs::PyObjectPointer<T, TC>& obj) const
262 {
263 return this->current_greenlet.borrow_o() == obj.borrow_o();
264 }
265
266 inline void set_current(const OwnedGreenlet& target)
267 {
268 this->current_greenlet = target;
269 }
270
271private:
283 inline void clear_deleteme_list(const bool murder=false)
284 {
285 if (!this->deleteme.empty()) {
286 // It's possible we could add items to this list while
287 // running Python code if there's a thread switch, so we
288 // need to defensively copy it before that can happen.
289 deleteme_t copy = this->deleteme;
290 this->deleteme.clear(); // in case things come back on the list
291 for(deleteme_t::iterator it = copy.begin(), end = copy.end();
292 it != end;
293 ++it ) {
294 PyGreenlet* to_del = *it;
295 if (murder) {
296 // Force each greenlet to appear dead; we can't raise an
297 // exception into it anymore anyway.
298 to_del->pimpl->murder_in_place();
299 }
300
301 // The only reference to these greenlets should be in
302 // this list, decreffing them should let them be
303 // deleted again, triggering calls to green_dealloc()
304 // in the correct thread (if we're not murdering).
305 // This may run arbitrary Python code and switch
306 // threads or greenlets!
307 Py_DECREF(to_del);
308 if (PyErr_Occurred()) {
309 PyErr_WriteUnraisable(nullptr);
310 PyErr_Clear();
311 }
312 }
313 }
314 }
315
316public:
317
321 inline OwnedObject get_tracefunc() const
322 {
323 return tracefunc;
324 };
325
326
327 inline void set_tracefunc(BorrowedObject tracefunc)
328 {
329 assert(tracefunc);
330 if (tracefunc == BorrowedObject(Py_None)) {
331 this->tracefunc.CLEAR();
332 }
333 else {
334 this->tracefunc = tracefunc;
335 }
336 }
337
344 {
345 Py_INCREF(to_del);
346 this->deleteme.push_back(to_del);
347 }
348
352 inline static std::clock_t clocks_used_doing_gc()
353 {
354#ifdef Py_GIL_DISABLED
355 return ThreadState::_clocks_used_doing_gc.load(std::memory_order_relaxed);
356#else
357 return ThreadState::_clocks_used_doing_gc;
358#endif
359 }
360
361 inline static void set_clocks_used_doing_gc(std::clock_t value)
362 {
363#ifdef Py_GIL_DISABLED
364 ThreadState::_clocks_used_doing_gc.store(value, std::memory_order_relaxed);
365#else
366 ThreadState::_clocks_used_doing_gc = value;
367#endif
368 }
369
370 inline static void add_clocks_used_doing_gc(std::clock_t value)
371 {
372#ifdef Py_GIL_DISABLED
373 ThreadState::_clocks_used_doing_gc.fetch_add(value, std::memory_order_relaxed);
374#else
375 ThreadState::_clocks_used_doing_gc += value;
376#endif
377 }
378
379 ~ThreadState()
380 {
381 if (!PyInterpreterState_Head()) {
382 // We shouldn't get here (our callers protect us)
383 // but if we do, all we can do is bail early.
384 return;
385 }
386
387 // We should not have an "origin" greenlet; that only exists
388 // for the temporary time during a switch, which should not
389 // be in progress as the thread dies.
390 //assert(!this->switching_state.origin);
391
392 this->tracefunc.CLEAR();
393
394 // Forcibly GC as much as we can.
395 this->clear_deleteme_list(true);
396
397 // The pending call did this.
398 assert(this->main_greenlet->thread_state() == nullptr);
399
400 // If the main greenlet is the current greenlet,
401 // then we "fell off the end" and the thread died.
402 // It's possible that there is some other greenlet that
403 // switched to us, leaving a reference to the main greenlet
404 // on the stack, somewhere uncollectible. Try to detect that.
405 if (this->current_greenlet == this->main_greenlet && this->current_greenlet) {
406 assert(this->current_greenlet->is_currently_running_in_some_thread());
407 // Drop one reference we hold.
408 this->current_greenlet.CLEAR();
409 assert(!this->current_greenlet);
410 // Only our reference to the main greenlet should be left,
411 // But hold onto the pointer in case we need to do extra cleanup.
412 PyGreenlet* old_main_greenlet = this->main_greenlet.borrow();
413 Py_ssize_t cnt = this->main_greenlet.REFCNT();
414 this->main_greenlet.CLEAR();
415 if (ThreadState::clocks_used_doing_gc() != std::clock_t(-1)
416 && cnt == 2 && Py_REFCNT(old_main_greenlet) == 1) {
417 // Highly likely that the reference is somewhere on
418 // the stack, not reachable by GC. Verify.
419 // XXX: This is O(n) in the total number of objects.
420 // TODO: Add a way to disable this at runtime, and
421 // another way to report on it.
422 std::clock_t begin = std::clock();
423 NewReference gc(PyImport_ImportModule("gc"));
424 if (gc) {
425 OwnedObject get_referrers = gc.PyRequireAttr(ThreadState::get_referrers_name);
426 OwnedList refs(get_referrers.PyCall(old_main_greenlet));
427 if (refs && refs.empty()) {
428 assert(refs.REFCNT() == 1);
429 // We found nothing! So we left a dangling
430 // reference: Probably the last thing some
431 // other greenlet did was call
432 // 'getcurrent().parent.switch()' to switch
433 // back to us. Clean it up. This will be the
434 // case on CPython 3.7 and newer, as they use
435 // an internal calling conversion that avoids
436 // creating method objects and storing them on
437 // the stack.
438 Py_DECREF(old_main_greenlet);
439 }
440 else if (refs
441 && refs.size() == 1
442 && PyCFunction_Check(refs.at(0))
443 && Py_REFCNT(refs.at(0)) == 2) {
444 assert(refs.REFCNT() == 1);
445 // Ok, we found a C method that refers to the
446 // main greenlet, and its only referenced
447 // twice, once in the list we just created,
448 // once from...somewhere else. If we can't
449 // find where else, then this is a leak.
450 // This happens in older versions of CPython
451 // that create a bound method object somewhere
452 // on the stack that we'll never get back to.
453 if (PyCFunction_GetFunction(refs.at(0).borrow()) == (PyCFunction)green_switch) {
454 BorrowedObject function_w = refs.at(0);
455 refs.clear(); // destroy the reference
456 // from the list.
457 // back to one reference. Can *it* be
458 // found?
459 assert(function_w.REFCNT() == 1);
460 refs = get_referrers.PyCall(function_w);
461 if (refs && refs.empty()) {
462 // Nope, it can't be found so it won't
463 // ever be GC'd. Drop it.
464 Py_CLEAR(function_w);
465 }
466 }
467 }
468 std::clock_t end = std::clock();
469 ThreadState::add_clocks_used_doing_gc(end - begin);
470 }
471 }
472 }
473
474 // We need to make sure this greenlet appears to be dead,
475 // because otherwise deallocing it would fail to raise an
476 // exception in it (the thread is dead) and put it back in our
477 // deleteme list.
478 if (this->current_greenlet) {
479 this->current_greenlet->murder_in_place();
480 this->current_greenlet.CLEAR();
481 }
482
483 if (this->main_greenlet) {
484 // Couldn't have been the main greenlet that was running
485 // when the thread exited (because we already cleared this
486 // pointer if it was). This shouldn't be possible?
487
488 // If the main greenlet was current when the thread died (it
489 // should be, right?) then we cleared its self pointer above
490 // when we cleared the current greenlet's main greenlet pointer.
491 // assert(this->main_greenlet->main_greenlet == this->main_greenlet
492 // || !this->main_greenlet->main_greenlet);
493 // // self reference, probably gone
494 // this->main_greenlet->main_greenlet.CLEAR();
495
496 // This will actually go away when the ivar is destructed.
497 this->main_greenlet.CLEAR();
498 }
499
500 if (PyErr_Occurred()) {
501 PyErr_WriteUnraisable(NULL);
502 PyErr_Clear();
503 }
504
505 }
506
507};
508
509ImmortalString ThreadState::get_referrers_name(nullptr);
510PythonAllocator<ThreadState> ThreadState::allocator;
511#ifdef Py_GIL_DISABLED
512std::atomic<std::clock_t> ThreadState::_clocks_used_doing_gc(0);
513#else
514std::clock_t ThreadState::_clocks_used_doing_gc(0);
515#endif
516
517
518
519
520
521}; // namespace greenlet
522
523#endif
Definition TGreenlet.hpp:752
Definition greenlet_exceptions.hpp:139
Definition TThreadState.hpp:87
OwnedObject get_tracefunc() const
Definition TThreadState.hpp:321
OwnedGreenlet get_current()
Definition TThreadState.hpp:233
OwnedGreenlet get_current() const
Definition TThreadState.hpp:255
static std::clock_t clocks_used_doing_gc()
Definition TThreadState.hpp:352
void delete_when_thread_running(PyGreenlet *to_del)
Definition TThreadState.hpp:343
BorrowedGreenlet borrow_current()
Definition TThreadState.hpp:246
Definition greenlet_refs.hpp:600
Definition greenlet_refs.hpp:534
Definition greenlet_refs.hpp:872
Definition greenlet_refs.hpp:655
Definition greenlet_refs.hpp:435
Definition greenlet_refs.hpp:813
Definition greenlet_refs.hpp:301
Definition greenlet_refs.hpp:1102
Definition greenlet_refs.hpp:925
Definition greenlet_refs.hpp:995
Definition greenlet_refs.hpp:171
Definition greenlet_refs.hpp:560
Definition greenlet_refs.hpp:471
Definition __init__.py:1
Definition greenlet.h:22
Definition greenlet_allocator.hpp:17