From a8251a8d255f9c62aee26a1a1ff2a69af11435ad Mon Sep 17 00:00:00 2001 From: Sam Gross Date: Tue, 2 Nov 2021 09:23:40 -0700 Subject: [PATCH] clinic: support '@' syntax for recursive mutexes This adds support for protecting functions wrapped by argument clinic with recursive mutexes or critical sections. --- Tools/clinic/clinic.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 3dc8478e73..f8830404cb 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -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): """ @@ -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} @@ -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, '{{', @@ -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. @@ -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() @@ -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 @@ -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 @@ -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 @@ -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 @@ -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)) @@ -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