Skip to content

Commit e52cc80

Browse files
gh-112213: Add @critical_section target directive to Argument Clinic (gh-112232)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
1 parent 607b5e3 commit e52cc80

File tree

2 files changed

+191
-4
lines changed

2 files changed

+191
-4
lines changed

Diff for: Lib/test/clinic.test.c

+170
Original file line numberDiff line numberDiff line change
@@ -5542,3 +5542,173 @@ test_critical_section_meth_o(PyObject *module, PyObject *arg)
55425542
static PyObject *
55435543
test_critical_section_meth_o_impl(PyObject *module, PyObject *a)
55445544
/*[clinic end generated code: output=7a9d7420802d1202 input=376533f51eceb6c3]*/
5545+
5546+
/*[clinic input]
5547+
@critical_section a
5548+
test_critical_section_object
5549+
a: object(subclass_of="&PyUnicode_Type")
5550+
/
5551+
test_critical_section_object
5552+
[clinic start generated code]*/
5553+
5554+
PyDoc_STRVAR(test_critical_section_object__doc__,
5555+
"test_critical_section_object($module, a, /)\n"
5556+
"--\n"
5557+
"\n"
5558+
"test_critical_section_object");
5559+
5560+
#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \
5561+
{"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__},
5562+
5563+
static PyObject *
5564+
test_critical_section_object_impl(PyObject *module, PyObject *a);
5565+
5566+
static PyObject *
5567+
test_critical_section_object(PyObject *module, PyObject *arg)
5568+
{
5569+
PyObject *return_value = NULL;
5570+
PyObject *a;
5571+
5572+
if (!PyUnicode_Check(arg)) {
5573+
_PyArg_BadArgument("test_critical_section_object", "argument", "str", arg);
5574+
goto exit;
5575+
}
5576+
a = arg;
5577+
Py_BEGIN_CRITICAL_SECTION(a);
5578+
return_value = test_critical_section_object_impl(module, a);
5579+
Py_END_CRITICAL_SECTION();
5580+
5581+
exit:
5582+
return return_value;
5583+
}
5584+
5585+
static PyObject *
5586+
test_critical_section_object_impl(PyObject *module, PyObject *a)
5587+
/*[clinic end generated code: output=ec06df92232b0fb5 input=6f67f91b523c875f]*/
5588+
5589+
PyDoc_STRVAR(test_critical_section_object__doc__,
5590+
"test_critical_section_object($module, a, /)\n"
5591+
"--\n"
5592+
"\n"
5593+
"test_critical_section_object");
5594+
5595+
#define TEST_CRITICAL_SECTION_OBJECT_METHODDEF \
5596+
{"test_critical_section_object", (PyCFunction)test_critical_section_object, METH_O, test_critical_section_object__doc__},
5597+
5598+
static PyObject *
5599+
test_critical_section_object_impl(PyObject *module, PyObject *a);
5600+
5601+
static PyObject *
5602+
test_critical_section_object(PyObject *module, PyObject *arg)
5603+
{
5604+
PyObject *return_value = NULL;
5605+
PyObject *a;
5606+
5607+
if (!PyUnicode_Check(arg)) {
5608+
_PyArg_BadArgument("test_critical_section_object", "argument", "str", arg);
5609+
goto exit;
5610+
}
5611+
a = arg;
5612+
Py_BEGIN_CRITICAL_SECTION(a);
5613+
return_value = test_critical_section_object_impl(module, a);
5614+
Py_END_CRITICAL_SECTION();
5615+
5616+
exit:
5617+
return return_value;
5618+
}
5619+
5620+
/*[clinic input]
5621+
@critical_section a b
5622+
test_critical_section_object2
5623+
a: object(subclass_of="&PyUnicode_Type")
5624+
b: object(subclass_of="&PyUnicode_Type")
5625+
/
5626+
test_critical_section_object2
5627+
[clinic start generated code]*/
5628+
5629+
PyDoc_STRVAR(test_critical_section_object2__doc__,
5630+
"test_critical_section_object2($module, a, b, /)\n"
5631+
"--\n"
5632+
"\n"
5633+
"test_critical_section_object2");
5634+
5635+
#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \
5636+
{"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__},
5637+
5638+
static PyObject *
5639+
test_critical_section_object2_impl(PyObject *module, PyObject *a,
5640+
PyObject *b);
5641+
5642+
static PyObject *
5643+
test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
5644+
{
5645+
PyObject *return_value = NULL;
5646+
PyObject *a;
5647+
PyObject *b;
5648+
5649+
if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) {
5650+
goto exit;
5651+
}
5652+
if (!PyUnicode_Check(args[0])) {
5653+
_PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]);
5654+
goto exit;
5655+
}
5656+
a = args[0];
5657+
if (!PyUnicode_Check(args[1])) {
5658+
_PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]);
5659+
goto exit;
5660+
}
5661+
b = args[1];
5662+
Py_BEGIN_CRITICAL_SECTION2(a, b);
5663+
return_value = test_critical_section_object2_impl(module, a, b);
5664+
Py_END_CRITICAL_SECTION2();
5665+
5666+
exit:
5667+
return return_value;
5668+
}
5669+
5670+
static PyObject *
5671+
test_critical_section_object2_impl(PyObject *module, PyObject *a,
5672+
PyObject *b)
5673+
/*[clinic end generated code: output=d73a1657c18df17a input=638824e41419a466]*/
5674+
5675+
PyDoc_STRVAR(test_critical_section_object2__doc__,
5676+
"test_critical_section_object2($module, a, b, /)\n"
5677+
"--\n"
5678+
"\n"
5679+
"test_critical_section_object2");
5680+
5681+
#define TEST_CRITICAL_SECTION_OBJECT2_METHODDEF \
5682+
{"test_critical_section_object2", _PyCFunction_CAST(test_critical_section_object2), METH_FASTCALL, test_critical_section_object2__doc__},
5683+
5684+
static PyObject *
5685+
test_critical_section_object2_impl(PyObject *module, PyObject *a,
5686+
PyObject *b);
5687+
5688+
static PyObject *
5689+
test_critical_section_object2(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
5690+
{
5691+
PyObject *return_value = NULL;
5692+
PyObject *a;
5693+
PyObject *b;
5694+
5695+
if (!_PyArg_CheckPositional("test_critical_section_object2", nargs, 2, 2)) {
5696+
goto exit;
5697+
}
5698+
if (!PyUnicode_Check(args[0])) {
5699+
_PyArg_BadArgument("test_critical_section_object2", "argument 1", "str", args[0]);
5700+
goto exit;
5701+
}
5702+
a = args[0];
5703+
if (!PyUnicode_Check(args[1])) {
5704+
_PyArg_BadArgument("test_critical_section_object2", "argument 2", "str", args[1]);
5705+
goto exit;
5706+
}
5707+
b = args[1];
5708+
Py_BEGIN_CRITICAL_SECTION2(a, b);
5709+
return_value = test_critical_section_object2_impl(module, a, b);
5710+
Py_END_CRITICAL_SECTION2();
5711+
5712+
exit:
5713+
return return_value;
5714+
}

Diff for: Tools/clinic/clinic.py

+21-4
Original file line numberDiff line numberDiff line change
@@ -1866,8 +1866,18 @@ def render_function(
18661866
assert isinstance(f_self.converter, self_converter), "No self parameter in " + repr(f.full_name) + "!"
18671867

18681868
if f.critical_section:
1869-
data.lock.append('Py_BEGIN_CRITICAL_SECTION({self_name});')
1870-
data.unlock.append('Py_END_CRITICAL_SECTION();')
1869+
match len(f.target_critical_section):
1870+
case 0:
1871+
lock = 'Py_BEGIN_CRITICAL_SECTION({self_name});'
1872+
unlock = 'Py_END_CRITICAL_SECTION();'
1873+
case 1:
1874+
lock = 'Py_BEGIN_CRITICAL_SECTION({target_critical_section});'
1875+
unlock = 'Py_END_CRITICAL_SECTION();'
1876+
case _:
1877+
lock = 'Py_BEGIN_CRITICAL_SECTION2({target_critical_section});'
1878+
unlock = 'Py_END_CRITICAL_SECTION2();'
1879+
data.lock.append(lock)
1880+
data.unlock.append(unlock)
18711881

18721882
last_group = 0
18731883
first_optional = len(selfless)
@@ -1922,6 +1932,7 @@ def render_function(
19221932
template_dict['docstring'] = self.docstring_for_c_string(f)
19231933

19241934
template_dict['self_name'] = template_dict['self_type'] = template_dict['self_type_check'] = ''
1935+
template_dict['target_critical_section'] = ', '.join(f.target_critical_section)
19251936
for converter in converters:
19261937
converter.set_template_dict(template_dict)
19271938

@@ -2970,6 +2981,7 @@ class Function:
29702981
# those accurately with inspect.Signature in 3.4.
29712982
docstring_only: bool = False
29722983
critical_section: bool = False
2984+
target_critical_section: list[str] = dc.field(default_factory=list)
29732985

29742986
def __post_init__(self) -> None:
29752987
self.parent = self.cls or self.module
@@ -5160,6 +5172,7 @@ def reset(self) -> None:
51605172
self.parameter_continuation = ''
51615173
self.preserve_output = False
51625174
self.critical_section = False
5175+
self.target_critical_section: list[str] = []
51635176

51645177
def directive_version(self, required: str) -> None:
51655178
global version
@@ -5288,7 +5301,10 @@ def at_classmethod(self) -> None:
52885301
fail("Can't set @classmethod, function is not a normal callable")
52895302
self.kind = CLASS_METHOD
52905303

5291-
def at_critical_section(self) -> None:
5304+
def at_critical_section(self, *args: str) -> None:
5305+
if len(args) > 2:
5306+
fail("Up to 2 critical section variables are supported")
5307+
self.target_critical_section.extend(args)
52925308
self.critical_section = True
52935309

52945310
def at_staticmethod(self) -> None:
@@ -5514,7 +5530,8 @@ def state_modulename_name(self, line: str) -> None:
55145530

55155531
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,
55165532
return_converter=return_converter, kind=self.kind, coexist=self.coexist,
5517-
critical_section=self.critical_section)
5533+
critical_section=self.critical_section,
5534+
target_critical_section=self.target_critical_section)
55185535
self.block.signatures.append(self.function)
55195536

55205537
# insert a self converter automatically

0 commit comments

Comments
 (0)