Skip to content

Commit a8251a8

Browse files
committed
clinic: support '@' syntax for recursive mutexes
This adds support for protecting functions wrapped by argument clinic with recursive mutexes or critical sections.
1 parent 9bf62ff commit a8251a8

File tree

1 file changed

+26
-4
lines changed

1 file changed

+26
-4
lines changed

‎Tools/clinic/clinic.py‎

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,9 @@ def __init__(self):
355355
# The C statements required to clean up after the impl call.
356356
self.cleanup = []
357357

358+
self.lock_statement = []
359+
self.unlock_statement = []
360+
358361

359362
class FormatCounterFormatter(string.Formatter):
360363
"""
@@ -822,7 +825,9 @@ def parser_body(prototype, *fields, declarations=''):
822825
# just imagine--your code is here in the middle
823826
fields.append(normalize_snippet("""
824827
{modifications}
828+
{lock_statement}
825829
{return_value} = {c_basename}_impl({impl_arguments});
830+
{unlock_statement}
826831
{return_conversion}
827832
{post_parsing}
828833
@@ -862,7 +867,7 @@ def parser_body(prototype, *fields, declarations=''):
862867
}}
863868
""" % return_error, indent=4)]
864869

865-
if default_return_converter:
870+
if default_return_converter and not f.lock:
866871
parser_definition = '\n'.join([
867872
parser_prototype,
868873
'{{',
@@ -882,7 +887,7 @@ def parser_body(prototype, *fields, declarations=''):
882887
{c_basename}({impl_parameters})
883888
""")
884889

885-
if default_return_converter:
890+
if default_return_converter and not f.lock:
886891
# maps perfectly to METH_O, doesn't need a return converter.
887892
# so we skip making a parse function
888893
# and call directly into the impl function.
@@ -1373,6 +1378,14 @@ def render_function(self, clinic, f):
13731378
selfless = parameters[1:]
13741379
assert isinstance(f_self.converter, self_converter), "No self parameter in " + repr(f.full_name) + "!"
13751380

1381+
if f.lock is not None:
1382+
if f.lock == 'rlock':
1383+
data.lock_statement.append('_PyRecursiveMutex_lock(&{self_name}->{lock_name});')
1384+
data.unlock_statement.append('_PyRecursiveMutex_unlock(&{self_name}->{lock_name});')
1385+
else:
1386+
data.lock_statement.append('Py_BEGIN_CRITICAL_SECTION(&{self_name}->{lock_name});')
1387+
data.unlock_statement.append('Py_END_CRITICAL_SECTION;')
1388+
13761389
last_group = 0
13771390
first_optional = len(selfless)
13781391
positional = selfless and selfless[-1].is_positional_only()
@@ -1474,6 +1487,10 @@ def render_function(self, clinic, f):
14741487
template_dict['cleanup'] = format_escape("".join(data.cleanup))
14751488
template_dict['return_value'] = data.return_value
14761489

1490+
template_dict['lock_name'] = f.lock
1491+
template_dict['lock_statement'] = "\n".join(data.lock_statement)
1492+
template_dict['unlock_statement'] = "\n".join(data.unlock_statement)
1493+
14771494
# used by unpack tuple code generator
14781495
ignore_self = -1 if isinstance(converters[0], self_converter) else 0
14791496
unpack_min = first_optional
@@ -1497,6 +1514,8 @@ def render_function(self, clinic, f):
14971514
modifications=template_dict['modifications'],
14981515
post_parsing=template_dict['post_parsing'],
14991516
cleanup=template_dict['cleanup'],
1517+
lock_statement=template_dict['lock_statement'],
1518+
unlock_statement=template_dict['unlock_statement'],
15001519
)
15011520

15021521
# Only generate the "exit:" label
@@ -2371,7 +2390,7 @@ def __init__(self, parameters=None, *, name,
23712390
module, cls=None, c_basename=None,
23722391
full_name=None,
23732392
return_converter, return_annotation=inspect.Signature.empty,
2374-
docstring=None, kind=CALLABLE, coexist=False,
2393+
docstring=None, kind=CALLABLE, coexist=False, lock=None,
23752394
docstring_only=False):
23762395
self.parameters = parameters or collections.OrderedDict()
23772396
self.return_annotation = return_annotation
@@ -2385,6 +2404,7 @@ def __init__(self, parameters=None, *, name,
23852404
self.docstring = docstring or ''
23862405
self.kind = kind
23872406
self.coexist = coexist
2407+
self.lock = lock
23882408
self.self_converter = None
23892409
# docstring_only means "don't generate a machine-readable
23902410
# signature, just a normal docstring". it's True for
@@ -4362,10 +4382,12 @@ def state_modulename_name(self, line):
43624382
return
43634383

43644384
line, _, returns = line.partition('->')
4385+
line, _, lock = line.partition(' @ ')
43654386

43664387
full_name, _, c_basename = line.partition(' as ')
43674388
full_name = full_name.strip()
43684389
c_basename = c_basename.strip() or None
4390+
lock = lock.strip() or None
43694391

43704392
if not is_legal_py_identifier(full_name):
43714393
fail("Illegal function name: {}".format(full_name))
@@ -4418,7 +4440,7 @@ def state_modulename_name(self, line):
44184440
if not module:
44194441
fail("Undefined module used in declaration of " + repr(full_name.strip()) + ".")
44204442
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
4421-
return_converter=return_converter, kind=self.kind, coexist=self.coexist)
4443+
return_converter=return_converter, kind=self.kind, coexist=self.coexist, lock=lock)
44224444
self.block.signatures.append(self.function)
44234445

44244446
# insert a self converter automatically

0 commit comments

Comments
 (0)