Skip to content

Commit 1daac18

Browse files
committed
update
1 parent 8d0ef46 commit 1daac18

14 files changed

Lines changed: 4588 additions & 398 deletions

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
# Python 3.9 源代码阅读笔记
22

3-
之前看了 "Python 源码剖析", 这本书是基于 Python 2.5 的, 现在的 Python 已经发生了很大的改变. 因此, 在这里记录下阅读 Python 3.9 的源代码的笔记, 当然阅读内容主要是 Python3 某些新加的特性.
3+
之前看了 "Python 源码剖析", 这本书是基于 Python 2.5 的, 现在的 Python 已经发生了很大的改变, 因此, 我决定重新阅读 Python 的源码.
4+
5+
在这里记录下阅读 [Python 3.9](https://github.com/ausaki/cpython/tree/v3.9.notes) 的源代码的笔记.
46

57
- [Python 虚拟机](ceval.md)
68

79
- [GIL](gil.md)
810

911

1012

11-
[源码注释分支](https://github.com/ausaki/python)
13+
[源码注释分支](https://github.com/ausaki/cpython/tree/v3.9.notes)
1214

trashcan.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# Trashcan
2+
3+
注释已经描述得非常清楚, 无需多言.
4+
5+
```c
6+
//Include/cpython/object.h
7+
8+
/* Trashcan mechanism, thanks to Christian Tismer.
9+
10+
When deallocating a container object, it's possible to trigger an unbounded
11+
chain of deallocations, as each Py_DECREF in turn drops the refcount on "the
12+
next" object in the chain to 0. This can easily lead to stack overflows,
13+
especially in threads (which typically have less stack space to work with).
14+
15+
A container object can avoid this by bracketing the body of its tp_dealloc
16+
function with a pair of macros:
17+
18+
static void
19+
mytype_dealloc(mytype *p)
20+
{
21+
... declarations go here ...
22+
23+
PyObject_GC_UnTrack(p); // must untrack first
24+
Py_TRASHCAN_BEGIN(p, mytype_dealloc)
25+
... The body of the deallocator goes here, including all calls ...
26+
... to Py_DECREF on contained objects. ...
27+
Py_TRASHCAN_END // there should be no code after this
28+
}
29+
30+
CAUTION: Never return from the middle of the body! If the body needs to
31+
"get out early", put a label immediately before the Py_TRASHCAN_END
32+
call, and goto it. Else the call-depth counter (see below) will stay
33+
above 0 forever, and the trashcan will never get emptied.
34+
35+
How it works: The BEGIN macro increments a call-depth counter. So long
36+
as this counter is small, the body of the deallocator is run directly without
37+
further ado. But if the counter gets large, it instead adds p to a list of
38+
objects to be deallocated later, skips the body of the deallocator, and
39+
resumes execution after the END macro. The tp_dealloc routine then returns
40+
without deallocating anything (and so unbounded call-stack depth is avoided).
41+
42+
When the call stack finishes unwinding again, code generated by the END macro
43+
notices this, and calls another routine to deallocate all the objects that
44+
may have been added to the list of deferred deallocations. In effect, a
45+
chain of N deallocations is broken into (N-1)/(PyTrash_UNWIND_LEVEL-1) pieces,
46+
with the call stack never exceeding a depth of PyTrash_UNWIND_LEVEL.
47+
48+
Since the tp_dealloc of a subclass typically calls the tp_dealloc of the base
49+
class, we need to ensure that the trashcan is only triggered on the tp_dealloc
50+
of the actual class being deallocated. Otherwise we might end up with a
51+
partially-deallocated object. To check this, the tp_dealloc function must be
52+
passed as second argument to Py_TRASHCAN_BEGIN().
53+
*/
54+
55+
/* This is the old private API, invoked by the macros before 3.2.4.
56+
Kept for binary compatibility of extensions using the stable ABI. */
57+
PyAPI_FUNC(void) _PyTrash_deposit_object(PyObject*);
58+
PyAPI_FUNC(void) _PyTrash_destroy_chain(void);
59+
60+
/* This is the old private API, invoked by the macros before 3.9.
61+
Kept for binary compatibility of extensions using the stable ABI. */
62+
PyAPI_FUNC(void) _PyTrash_thread_deposit_object(PyObject*);
63+
PyAPI_FUNC(void) _PyTrash_thread_destroy_chain(void);
64+
65+
/* Forward declarations for PyThreadState */
66+
struct _ts;
67+
68+
/* Python 3.9 private API, invoked by the macros below. */
69+
PyAPI_FUNC(int) _PyTrash_begin(struct _ts *tstate, PyObject *op);
70+
PyAPI_FUNC(void) _PyTrash_end(struct _ts *tstate);
71+
72+
#define PyTrash_UNWIND_LEVEL 50
73+
74+
// 注意仔细看 Py_TRASHCAN_BEGIN_CONDITION 这个宏的定义, 它不是一个完整的语句块, do 语句的 body 没有结束.
75+
#define Py_TRASHCAN_BEGIN_CONDITION(op, cond) \
76+
do { \
77+
PyThreadState *_tstate = NULL; \
78+
/* If "cond" is false, then _tstate remains NULL and the deallocator \
79+
* is run normally without involving the trashcan */ \
80+
if (cond) { \
81+
_tstate = PyThreadState_GET(); \
82+
if (_PyTrash_begin(_tstate, _PyObject_CAST(op))) { \
83+
break; \
84+
} \
85+
}
86+
/* The body of the deallocator is here. */
87+
#define Py_TRASHCAN_END \
88+
if (_tstate) { \
89+
_PyTrash_end(_tstate); \
90+
} \
91+
} while (0);
92+
93+
#define Py_TRASHCAN_BEGIN(op, dealloc) \
94+
Py_TRASHCAN_BEGIN_CONDITION(op, \
95+
Py_TYPE(op)->tp_dealloc == (destructor)(dealloc))
96+
97+
/* For backwards compatibility, these macros enable the trashcan
98+
* unconditionally */
99+
#define Py_TRASHCAN_SAFE_BEGIN(op) Py_TRASHCAN_BEGIN_CONDITION(op, 1)
100+
#define Py_TRASHCAN_SAFE_END(op) Py_TRASHCAN_END
101+
```
102+
103+
我最喜欢这样的注释, 对新加的某个特性进行详细的描述. 遗憾的是 CPython 的代码中有许多没有详细注释的代码, 对于刚开始阅读 CPython 代码的人来说非常难以理解.
104+
105+
trashcan 的用法可以查看 tuple 的 tpdealloc 函数.

0 commit comments

Comments
 (0)