Skip to content

Commit 23e65b2

Browse files
william-grvstinner
authored andcommitted
bpo-33625: Release GIL for grp.getgr{nam,gid} and pwd.getpw{nam,uid} (GH-7081)
Release GIL on grp.getgrnam(), grp.getgrgid(), pwd.getpwnam() and pwd.getpwuid() if reentrant variants of these functions are available. Patch by William Grzybowski.
1 parent 25fa141 commit 23e65b2

File tree

6 files changed

+228
-15
lines changed

6 files changed

+228
-15
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Release GIL on `grp.getgrnam`, `grp.getgrgid`, `pwd.getpwnam` and
2+
`pwd.getpwuid` if reentrant variants of these functions are available.
3+
Patch by William Grzybowski.

Modules/grpmodule.c

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ static PyStructSequence_Desc struct_group_type_desc = {
3737
static int initialized;
3838
static PyTypeObject StructGrpType;
3939

40+
#define DEFAULT_BUFFER_SIZE 1024
41+
4042
static PyObject *
4143
mkgrent(struct group *p)
4244
{
@@ -96,7 +98,9 @@ static PyObject *
9698
grp_getgrgid_impl(PyObject *module, PyObject *id)
9799
/*[clinic end generated code: output=30797c289504a1ba input=15fa0e2ccf5cda25]*/
98100
{
99-
PyObject *py_int_id;
101+
PyObject *py_int_id, *retval = NULL;
102+
int nomem = 0;
103+
char *buf = NULL, *buf2 = NULL;
100104
gid_t gid;
101105
struct group *p;
102106

@@ -119,16 +123,60 @@ grp_getgrgid_impl(PyObject *module, PyObject *id)
119123
}
120124
Py_DECREF(py_int_id);
121125
}
126+
#ifdef HAVE_GETGRGID_R
127+
Py_BEGIN_ALLOW_THREADS
128+
int status;
129+
Py_ssize_t bufsize;
130+
struct group grp;
131+
132+
bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
133+
if (bufsize == -1) {
134+
bufsize = DEFAULT_BUFFER_SIZE;
135+
}
136+
137+
while (1) {
138+
buf2 = PyMem_RawRealloc(buf, bufsize);
139+
if (buf2 == NULL) {
140+
p = NULL;
141+
nomem = 1;
142+
break;
143+
}
144+
buf = buf2;
145+
status = getgrgid_r(gid, &grp, buf, bufsize, &p);
146+
if (status != 0) {
147+
p = NULL;
148+
}
149+
if (p != NULL || status != ERANGE) {
150+
break;
151+
}
152+
if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
153+
nomem = 1;
154+
break;
155+
}
156+
bufsize <<= 1;
157+
}
122158

123-
if ((p = getgrgid(gid)) == NULL) {
159+
Py_END_ALLOW_THREADS
160+
#else
161+
p = getgrgid(gid);
162+
#endif
163+
if (p == NULL) {
164+
PyMem_RawFree(buf);
165+
if (nomem == 1) {
166+
return PyErr_NoMemory();
167+
}
124168
PyObject *gid_obj = _PyLong_FromGid(gid);
125169
if (gid_obj == NULL)
126170
return NULL;
127171
PyErr_Format(PyExc_KeyError, "getgrgid(): gid not found: %S", gid_obj);
128172
Py_DECREF(gid_obj);
129173
return NULL;
130174
}
131-
return mkgrent(p);
175+
retval = mkgrent(p);
176+
#ifdef HAVE_GETGRGID_R
177+
PyMem_RawFree(buf);
178+
#endif
179+
return retval;
132180
}
133181

134182
/*[clinic input]
@@ -145,7 +193,8 @@ static PyObject *
145193
grp_getgrnam_impl(PyObject *module, PyObject *name)
146194
/*[clinic end generated code: output=67905086f403c21c input=08ded29affa3c863]*/
147195
{
148-
char *name_chars;
196+
char *buf = NULL, *buf2 = NULL, *name_chars;
197+
int nomem = 0;
149198
struct group *p;
150199
PyObject *bytes, *retval = NULL;
151200

@@ -154,13 +203,55 @@ grp_getgrnam_impl(PyObject *module, PyObject *name)
154203
/* check for embedded null bytes */
155204
if (PyBytes_AsStringAndSize(bytes, &name_chars, NULL) == -1)
156205
goto out;
206+
#ifdef HAVE_GETGRNAM_R
207+
Py_BEGIN_ALLOW_THREADS
208+
int status;
209+
Py_ssize_t bufsize;
210+
struct group grp;
211+
212+
bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
213+
if (bufsize == -1) {
214+
bufsize = DEFAULT_BUFFER_SIZE;
215+
}
216+
217+
while(1) {
218+
buf2 = PyMem_RawRealloc(buf, bufsize);
219+
if (buf2 == NULL) {
220+
p = NULL;
221+
nomem = 1;
222+
break;
223+
}
224+
buf = buf2;
225+
status = getgrnam_r(name_chars, &grp, buf, bufsize, &p);
226+
if (status != 0) {
227+
p = NULL;
228+
}
229+
if (p != NULL || status != ERANGE) {
230+
break;
231+
}
232+
if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
233+
nomem = 1;
234+
break;
235+
}
236+
bufsize <<= 1;
237+
}
157238

158-
if ((p = getgrnam(name_chars)) == NULL) {
159-
PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %s", name_chars);
239+
Py_END_ALLOW_THREADS
240+
#else
241+
p = getgrnam(name_chars);
242+
#endif
243+
if (p == NULL) {
244+
if (nomem == 1) {
245+
PyErr_NoMemory();
246+
}
247+
else {
248+
PyErr_Format(PyExc_KeyError, "getgrnam(): name not found: %s", name_chars);
249+
}
160250
goto out;
161251
}
162252
retval = mkgrent(p);
163253
out:
254+
PyMem_RawFree(buf);
164255
Py_DECREF(bytes);
165256
return retval;
166257
}

Modules/pwdmodule.c

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ exception is raised if the entry asked for cannot be found.");
5050
static int initialized;
5151
static PyTypeObject StructPwdType;
5252

53+
#define DEFAULT_BUFFER_SIZE 1024
54+
5355
static void
5456
sets(PyObject *v, int i, const char* val)
5557
{
@@ -116,16 +118,59 @@ static PyObject *
116118
pwd_getpwuid(PyObject *module, PyObject *uidobj)
117119
/*[clinic end generated code: output=c4ee1d4d429b86c4 input=ae64d507a1c6d3e8]*/
118120
{
121+
PyObject *retval = NULL;
119122
uid_t uid;
123+
int nomem = 0;
120124
struct passwd *p;
125+
char *buf = NULL, *buf2 = NULL;
121126

122127
if (!_Py_Uid_Converter(uidobj, &uid)) {
123128
if (PyErr_ExceptionMatches(PyExc_OverflowError))
124129
PyErr_Format(PyExc_KeyError,
125130
"getpwuid(): uid not found");
126131
return NULL;
127132
}
128-
if ((p = getpwuid(uid)) == NULL) {
133+
#ifdef HAVE_GETPWUID_R
134+
Py_BEGIN_ALLOW_THREADS
135+
int status;
136+
Py_ssize_t bufsize;
137+
struct passwd pwd;
138+
139+
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
140+
if (bufsize == -1) {
141+
bufsize = DEFAULT_BUFFER_SIZE;
142+
}
143+
144+
while(1) {
145+
buf2 = PyMem_RawRealloc(buf, bufsize);
146+
if (buf2 == NULL) {
147+
nomem = 1;
148+
break;
149+
}
150+
buf = buf2;
151+
status = getpwuid_r(uid, &pwd, buf, bufsize, &p);
152+
if (status != 0) {
153+
p = NULL;
154+
}
155+
if (p != NULL || status != ERANGE) {
156+
break;
157+
}
158+
if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
159+
nomem = 1;
160+
break;
161+
}
162+
bufsize <<= 1;
163+
}
164+
165+
Py_END_ALLOW_THREADS
166+
#else
167+
p = getpwuid(uid);
168+
#endif
169+
if (p == NULL) {
170+
PyMem_RawFree(buf);
171+
if (nomem == 1) {
172+
return PyErr_NoMemory();
173+
}
129174
PyObject *uid_obj = _PyLong_FromUid(uid);
130175
if (uid_obj == NULL)
131176
return NULL;
@@ -134,7 +179,11 @@ pwd_getpwuid(PyObject *module, PyObject *uidobj)
134179
Py_DECREF(uid_obj);
135180
return NULL;
136181
}
137-
return mkpwent(p);
182+
retval = mkpwent(p);
183+
#ifdef HAVE_GETPWUID_R
184+
PyMem_RawFree(buf);
185+
#endif
186+
return retval;
138187
}
139188

140189
/*[clinic input]
@@ -152,7 +201,8 @@ static PyObject *
152201
pwd_getpwnam_impl(PyObject *module, PyObject *arg)
153202
/*[clinic end generated code: output=6abeee92430e43d2 input=d5f7e700919b02d3]*/
154203
{
155-
char *name;
204+
char *buf = NULL, *buf2 = NULL, *name;
205+
int nomem = 0;
156206
struct passwd *p;
157207
PyObject *bytes, *retval = NULL;
158208

@@ -161,13 +211,55 @@ pwd_getpwnam_impl(PyObject *module, PyObject *arg)
161211
/* check for embedded null bytes */
162212
if (PyBytes_AsStringAndSize(bytes, &name, NULL) == -1)
163213
goto out;
164-
if ((p = getpwnam(name)) == NULL) {
165-
PyErr_Format(PyExc_KeyError,
166-
"getpwnam(): name not found: %s", name);
214+
#ifdef HAVE_GETPWNAM_R
215+
Py_BEGIN_ALLOW_THREADS
216+
int status;
217+
Py_ssize_t bufsize;
218+
struct passwd pwd;
219+
220+
bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
221+
if (bufsize == -1) {
222+
bufsize = DEFAULT_BUFFER_SIZE;
223+
}
224+
225+
while(1) {
226+
buf2 = PyMem_RawRealloc(buf, bufsize);
227+
if (buf2 == NULL) {
228+
nomem = 1;
229+
break;
230+
}
231+
buf = buf2;
232+
status = getpwnam_r(name, &pwd, buf, bufsize, &p);
233+
if (status != 0) {
234+
p = NULL;
235+
}
236+
if (p != NULL || status != ERANGE) {
237+
break;
238+
}
239+
if (bufsize > (PY_SSIZE_T_MAX >> 1)) {
240+
nomem = 1;
241+
break;
242+
}
243+
bufsize <<= 1;
244+
}
245+
246+
Py_END_ALLOW_THREADS
247+
#else
248+
p = getpwnam(name);
249+
#endif
250+
if (p == NULL) {
251+
if (nomem == 1) {
252+
PyErr_NoMemory();
253+
}
254+
else {
255+
PyErr_Format(PyExc_KeyError,
256+
"getpwnam(): name not found: %s", name);
257+
}
167258
goto out;
168259
}
169260
retval = mkpwent(p);
170261
out:
262+
PyMem_RawFree(buf);
171263
Py_DECREF(bytes);
172264
return retval;
173265
}

configure

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,7 @@ infodir
781781
docdir
782782
oldincludedir
783783
includedir
784+
runstatedir
784785
localstatedir
785786
sharedstatedir
786787
sysconfdir
@@ -890,6 +891,7 @@ datadir='${datarootdir}'
890891
sysconfdir='${prefix}/etc'
891892
sharedstatedir='${prefix}/com'
892893
localstatedir='${prefix}/var'
894+
runstatedir='${localstatedir}/run'
893895
includedir='${prefix}/include'
894896
oldincludedir='/usr/include'
895897
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1142,6 +1144,15 @@ do
11421144
| -silent | --silent | --silen | --sile | --sil)
11431145
silent=yes ;;
11441146

1147+
-runstatedir | --runstatedir | --runstatedi | --runstated \
1148+
| --runstate | --runstat | --runsta | --runst | --runs \
1149+
| --run | --ru | --r)
1150+
ac_prev=runstatedir ;;
1151+
-runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
1152+
| --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
1153+
| --run=* | --ru=* | --r=*)
1154+
runstatedir=$ac_optarg ;;
1155+
11451156
-sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
11461157
ac_prev=sbindir ;;
11471158
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1279,7 +1290,7 @@ fi
12791290
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
12801291
datadir sysconfdir sharedstatedir localstatedir includedir \
12811292
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
1282-
libdir localedir mandir
1293+
libdir localedir mandir runstatedir
12831294
do
12841295
eval ac_val=\$$ac_var
12851296
# Remove trailing slashes.
@@ -1432,6 +1443,7 @@ Fine tuning of the installation directories:
14321443
--sysconfdir=DIR read-only single-machine data [PREFIX/etc]
14331444
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
14341445
--localstatedir=DIR modifiable single-machine data [PREFIX/var]
1446+
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
14351447
--libdir=DIR object code libraries [EPREFIX/lib]
14361448
--includedir=DIR C header files [PREFIX/include]
14371449
--oldincludedir=DIR C header files for non-gcc [/usr/include]
@@ -11242,8 +11254,9 @@ for ac_func in alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
1124211254
clock confstr ctermid dup3 execv faccessat fchmod fchmodat fchown fchownat \
1124311255
fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \
1124411256
futimens futimes gai_strerror getentropy \
11257+
getgrgid_r getgrnam_r \
1124511258
getgrouplist getgroups getlogin getloadavg getpeername getpgid getpid \
11246-
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
11259+
getpriority getresuid getresgid getpwent getpwnam_r getpwuid_r getspnam getspent getsid getwd \
1124711260
if_nameindex \
1124811261
initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mmap \
1124911262
memrchr mbrtowc mkdirat mkfifo \
@@ -13675,6 +13688,7 @@ fi
1367513688

1367613689

1367713690

13691+
1367813692
# checks for system services
1367913693
# (none yet)
1368013694

configure.ac

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3434,8 +3434,9 @@ AC_CHECK_FUNCS(alarm accept4 setitimer getitimer bind_textdomain_codeset chown \
34343434
clock confstr ctermid dup3 execv faccessat fchmod fchmodat fchown fchownat \
34353435
fexecve fdopendir fork fpathconf fstatat ftime ftruncate futimesat \
34363436
futimens futimes gai_strerror getentropy \
3437+
getgrgid_r getgrnam_r \
34373438
getgrouplist getgroups getlogin getloadavg getpeername getpgid getpid \
3438-
getpriority getresuid getresgid getpwent getspnam getspent getsid getwd \
3439+
getpriority getresuid getresgid getpwent getpwnam_r getpwuid_r getspnam getspent getsid getwd \
34393440
if_nameindex \
34403441
initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mmap \
34413442
memrchr mbrtowc mkdirat mkfifo \

0 commit comments

Comments
 (0)