@@ -470,6 +470,10 @@ def __init__(self) -> None:
470
470
# The C statements required to clean up after the impl call.
471
471
self .cleanup : list [str ] = []
472
472
473
+ # The C statements to generate critical sections (per-object locking).
474
+ self .lock : list [str ] = []
475
+ self .unlock : list [str ] = []
476
+
473
477
474
478
class FormatCounterFormatter (string .Formatter ):
475
479
"""
@@ -1109,7 +1113,8 @@ def output_templates(
1109
1113
condition = include .condition )
1110
1114
1111
1115
has_option_groups = parameters and (parameters [0 ].group or parameters [- 1 ].group )
1112
- default_return_converter = f .return_converter .type == 'PyObject *'
1116
+ simple_return = (f .return_converter .type == 'PyObject *'
1117
+ and not f .critical_section )
1113
1118
new_or_init = f .kind .new_or_init
1114
1119
1115
1120
vararg : int | str = NO_VARARG
@@ -1183,7 +1188,9 @@ def parser_body(
1183
1188
""" ) + "\n "
1184
1189
finale = normalize_snippet ("""
1185
1190
{modifications}
1191
+ {lock}
1186
1192
{return_value} = {c_basename}_impl({impl_arguments});
1193
+ {unlock}
1187
1194
{return_conversion}
1188
1195
{post_parsing}
1189
1196
@@ -1219,7 +1226,7 @@ def parser_body(
1219
1226
1220
1227
flags = "METH_METHOD|METH_FASTCALL|METH_KEYWORDS"
1221
1228
parser_prototype = self .PARSER_PROTOTYPE_DEF_CLASS
1222
- return_error = ('return NULL;' if default_return_converter
1229
+ return_error = ('return NULL;' if simple_return
1223
1230
else 'goto exit;' )
1224
1231
parser_code = [normalize_snippet ("""
1225
1232
if (nargs) {{
@@ -1228,7 +1235,7 @@ def parser_body(
1228
1235
}}
1229
1236
""" % return_error , indent = 4 )]
1230
1237
1231
- if default_return_converter :
1238
+ if simple_return :
1232
1239
parser_definition = '\n ' .join ([
1233
1240
parser_prototype ,
1234
1241
'{{' ,
@@ -1245,7 +1252,7 @@ def parser_body(
1245
1252
converters [0 ].format_unit == 'O' ):
1246
1253
meth_o_prototype = self .METH_O_PROTOTYPE
1247
1254
1248
- if default_return_converter :
1255
+ if simple_return :
1249
1256
# maps perfectly to METH_O, doesn't need a return converter.
1250
1257
# so we skip making a parse function
1251
1258
# and call directly into the impl function.
@@ -1858,6 +1865,10 @@ def render_function(
1858
1865
selfless = parameters [1 :]
1859
1866
assert isinstance (f_self .converter , self_converter ), "No self parameter in " + repr (f .full_name ) + "!"
1860
1867
1868
+ if f .critical_section :
1869
+ data .lock .append ('Py_BEGIN_CRITICAL_SECTION({self_name});' )
1870
+ data .unlock .append ('Py_END_CRITICAL_SECTION();' )
1871
+
1861
1872
last_group = 0
1862
1873
first_optional = len (selfless )
1863
1874
positional = selfless and selfless [- 1 ].is_positional_only ()
@@ -1937,6 +1948,8 @@ def render_function(
1937
1948
template_dict ['post_parsing' ] = format_escape ("" .join (data .post_parsing ).rstrip ())
1938
1949
template_dict ['cleanup' ] = format_escape ("" .join (data .cleanup ))
1939
1950
template_dict ['return_value' ] = data .return_value
1951
+ template_dict ['lock' ] = "\n " .join (data .lock )
1952
+ template_dict ['unlock' ] = "\n " .join (data .unlock )
1940
1953
1941
1954
# used by unpack tuple code generator
1942
1955
unpack_min = first_optional
@@ -1961,6 +1974,8 @@ def render_function(
1961
1974
modifications = template_dict ['modifications' ],
1962
1975
post_parsing = template_dict ['post_parsing' ],
1963
1976
cleanup = template_dict ['cleanup' ],
1977
+ lock = template_dict ['lock' ],
1978
+ unlock = template_dict ['unlock' ],
1964
1979
)
1965
1980
1966
1981
# Only generate the "exit:" label
@@ -2954,6 +2969,7 @@ class Function:
2954
2969
# functions with optional groups because we can't represent
2955
2970
# those accurately with inspect.Signature in 3.4.
2956
2971
docstring_only : bool = False
2972
+ critical_section : bool = False
2957
2973
2958
2974
def __post_init__ (self ) -> None :
2959
2975
self .parent = self .cls or self .module
@@ -5108,6 +5124,7 @@ class DSLParser:
5108
5124
coexist : bool
5109
5125
parameter_continuation : str
5110
5126
preserve_output : bool
5127
+ critical_section : bool
5111
5128
from_version_re = re .compile (r'([*/]) +\[from +(.+)\]' )
5112
5129
5113
5130
def __init__ (self , clinic : Clinic ) -> None :
@@ -5142,6 +5159,7 @@ def reset(self) -> None:
5142
5159
self .forced_text_signature : str | None = None
5143
5160
self .parameter_continuation = ''
5144
5161
self .preserve_output = False
5162
+ self .critical_section = False
5145
5163
5146
5164
def directive_version (self , required : str ) -> None :
5147
5165
global version
@@ -5270,6 +5288,9 @@ def at_classmethod(self) -> None:
5270
5288
fail ("Can't set @classmethod, function is not a normal callable" )
5271
5289
self .kind = CLASS_METHOD
5272
5290
5291
+ def at_critical_section (self ) -> None :
5292
+ self .critical_section = True
5293
+
5273
5294
def at_staticmethod (self ) -> None :
5274
5295
if self .kind is not CALLABLE :
5275
5296
fail ("Can't set @staticmethod, function is not a normal callable" )
@@ -5492,7 +5513,8 @@ def state_modulename_name(self, line: str) -> None:
5492
5513
return_converter = CReturnConverter ()
5493
5514
5494
5515
self .function = Function (name = function_name , full_name = full_name , module = module , cls = cls , c_basename = c_basename ,
5495
- return_converter = return_converter , kind = self .kind , coexist = self .coexist )
5516
+ return_converter = return_converter , kind = self .kind , coexist = self .coexist ,
5517
+ critical_section = self .critical_section )
5496
5518
self .block .signatures .append (self .function )
5497
5519
5498
5520
# insert a self converter automatically
0 commit comments