Skip to content

Commit

Permalink
clinic: support '@' syntax for recursive mutexes
Browse files Browse the repository at this point in the history
This adds support for protecting functions wrapped by argument clinic
with recursive mutexes or critical sections.
  • Loading branch information
colesbury committed Apr 23, 2023
1 parent 9bf62ff commit a8251a8
Showing 1 changed file with 26 additions and 4 deletions.
30 changes: 26 additions & 4 deletions Tools/clinic/clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,9 @@ def __init__(self):
# The C statements required to clean up after the impl call.
self.cleanup = []

self.lock_statement = []
self.unlock_statement = []


class FormatCounterFormatter(string.Formatter):
"""
Expand Down Expand Up @@ -822,7 +825,9 @@ def parser_body(prototype, *fields, declarations=''):
# just imagine--your code is here in the middle
fields.append(normalize_snippet("""
{modifications}
{lock_statement}
{return_value} = {c_basename}_impl({impl_arguments});
{unlock_statement}
{return_conversion}
{post_parsing}
Expand Down Expand Up @@ -862,7 +867,7 @@ def parser_body(prototype, *fields, declarations=''):
}}
""" % return_error, indent=4)]

if default_return_converter:
if default_return_converter and not f.lock:
parser_definition = '\n'.join([
parser_prototype,
'{{',
Expand All @@ -882,7 +887,7 @@ def parser_body(prototype, *fields, declarations=''):
{c_basename}({impl_parameters})
""")

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

if f.lock is not None:
if f.lock == 'rlock':
data.lock_statement.append('_PyRecursiveMutex_lock(&{self_name}->{lock_name});')
data.unlock_statement.append('_PyRecursiveMutex_unlock(&{self_name}->{lock_name});')
else:
data.lock_statement.append('Py_BEGIN_CRITICAL_SECTION(&{self_name}->{lock_name});')
data.unlock_statement.append('Py_END_CRITICAL_SECTION;')

last_group = 0
first_optional = len(selfless)
positional = selfless and selfless[-1].is_positional_only()
Expand Down Expand Up @@ -1474,6 +1487,10 @@ def render_function(self, clinic, f):
template_dict['cleanup'] = format_escape("".join(data.cleanup))
template_dict['return_value'] = data.return_value

template_dict['lock_name'] = f.lock
template_dict['lock_statement'] = "\n".join(data.lock_statement)
template_dict['unlock_statement'] = "\n".join(data.unlock_statement)

# used by unpack tuple code generator
ignore_self = -1 if isinstance(converters[0], self_converter) else 0
unpack_min = first_optional
Expand All @@ -1497,6 +1514,8 @@ def render_function(self, clinic, f):
modifications=template_dict['modifications'],
post_parsing=template_dict['post_parsing'],
cleanup=template_dict['cleanup'],
lock_statement=template_dict['lock_statement'],
unlock_statement=template_dict['unlock_statement'],
)

# Only generate the "exit:" label
Expand Down Expand Up @@ -2371,7 +2390,7 @@ def __init__(self, parameters=None, *, name,
module, cls=None, c_basename=None,
full_name=None,
return_converter, return_annotation=inspect.Signature.empty,
docstring=None, kind=CALLABLE, coexist=False,
docstring=None, kind=CALLABLE, coexist=False, lock=None,
docstring_only=False):
self.parameters = parameters or collections.OrderedDict()
self.return_annotation = return_annotation
Expand All @@ -2385,6 +2404,7 @@ def __init__(self, parameters=None, *, name,
self.docstring = docstring or ''
self.kind = kind
self.coexist = coexist
self.lock = lock
self.self_converter = None
# docstring_only means "don't generate a machine-readable
# signature, just a normal docstring". it's True for
Expand Down Expand Up @@ -4362,10 +4382,12 @@ def state_modulename_name(self, line):
return

line, _, returns = line.partition('->')
line, _, lock = line.partition(' @ ')

full_name, _, c_basename = line.partition(' as ')
full_name = full_name.strip()
c_basename = c_basename.strip() or None
lock = lock.strip() or None

if not is_legal_py_identifier(full_name):
fail("Illegal function name: {}".format(full_name))
Expand Down Expand Up @@ -4418,7 +4440,7 @@ def state_modulename_name(self, line):
if not module:
fail("Undefined module used in declaration of " + repr(full_name.strip()) + ".")
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
return_converter=return_converter, kind=self.kind, coexist=self.coexist)
return_converter=return_converter, kind=self.kind, coexist=self.coexist, lock=lock)
self.block.signatures.append(self.function)

# insert a self converter automatically
Expand Down

0 comments on commit a8251a8

Please sign in to comment.