Skip to content

Commit 40730d3

Browse files
committed
Refactor so mutex is held for minimal time and not over any code that could be
re-entrant.
1 parent f757a3b commit 40730d3

File tree

1 file changed

+71
-51
lines changed

1 file changed

+71
-51
lines changed

Modules/posixmodule.c

Lines changed: 71 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -16193,7 +16193,7 @@ typedef struct {
1619316193
#ifdef HAVE_FDOPENDIR
1619416194
int fd;
1619516195
#endif
16196-
_PyRecursiveMutex mutex;
16196+
PyMutex mutex;
1619716197
} ScandirIterator;
1619816198

1619916199
#define ScandirIterator_CAST(op) ((ScandirIterator *)(op))
@@ -16209,10 +16209,10 @@ ScandirIterator_is_closed(ScandirIterator *iterator)
1620916209
static void
1621016210
ScandirIterator_closedir(ScandirIterator *iterator)
1621116211
{
16212-
_PyRecursiveMutex_Lock(&iterator->mutex);
16212+
PyMutex_Lock(&iterator->mutex);
1621316213
HANDLE handle = iterator->handle;
1621416214
iterator->handle = INVALID_HANDLE_VALUE;
16215-
_PyRecursiveMutex_Unlock(&iterator->mutex);
16215+
PyMutex_Unlock(&iterator->mutex);
1621616216

1621716217
if (handle == INVALID_HANDLE_VALUE) {
1621816218
return;
@@ -16223,46 +16223,56 @@ ScandirIterator_closedir(ScandirIterator *iterator)
1622316223
Py_END_ALLOW_THREADS
1622416224
}
1622516225

16226+
static BOOL
16227+
ScandirIterator_nextdirentry(ScandirIterator *iterator, WIN32_FIND_DATAW *file_data)
16228+
{
16229+
BOOL has_error = FALSE;
16230+
BOOL has_result = FALSE;
16231+
PyMutex_Lock(&iterator->mutex);
16232+
if (iterator->handle != INVALID_HANDLE_VALUE) {
16233+
if (iterator->first_time) {
16234+
has_result = TRUE;
16235+
memcpy(file_data, &iterator->file_data, sizeof(WIN32_FIND_DATAW));
16236+
iterator->first_time = 0;
16237+
} else {
16238+
Py_BEGIN_ALLOW_THREADS
16239+
has_result = FindNextFileW(iterator->handle, file_data);
16240+
Py_END_ALLOW_THREADS
16241+
has_error = !has_result && GetLastError() != ERROR_NO_MORE_FILES;
16242+
}
16243+
}
16244+
PyMutex_Unlock(&iterator->mutex);
16245+
16246+
/* Error or no more files */
16247+
if (has_error)
16248+
path_error(&iterator->path);
16249+
16250+
return has_result;
16251+
}
16252+
1622616253
static PyObject *
1622716254
ScandirIterator_iternext(PyObject *op)
1622816255
{
1622916256
ScandirIterator *iterator = ScandirIterator_CAST(op);
16230-
WIN32_FIND_DATAW *file_data = &iterator->file_data;
16231-
BOOL success;
16257+
WIN32_FIND_DATAW file_data;
1623216258
PyObject *entry;
1623316259

16234-
_PyRecursiveMutex_Lock(&iterator->mutex);
16235-
while (iterator->handle != INVALID_HANDLE_VALUE) {
16236-
if (!iterator->first_time) {
16237-
Py_BEGIN_ALLOW_THREADS
16238-
success = FindNextFileW(iterator->handle, file_data);
16239-
Py_END_ALLOW_THREADS
16240-
if (!success) {
16241-
/* Error or no more files */
16242-
if (GetLastError() != ERROR_NO_MORE_FILES)
16243-
path_error(&iterator->path);
16244-
break;
16245-
}
16246-
}
16247-
iterator->first_time = 0;
16248-
16260+
while (ScandirIterator_nextdirentry(iterator, &file_data)) {
1624916261
/* Skip over . and .. */
16250-
if (wcscmp(file_data->cFileName, L".") != 0 &&
16251-
wcscmp(file_data->cFileName, L"..") != 0)
16262+
if (wcscmp(file_data.cFileName, L".") != 0 &&
16263+
wcscmp(file_data.cFileName, L"..") != 0)
1625216264
{
1625316265
PyObject *module = PyType_GetModule(Py_TYPE(iterator));
16254-
entry = DirEntry_from_find_data(module, &iterator->path, file_data);
16266+
entry = DirEntry_from_find_data(module, &iterator->path, &file_data);
1625516267
if (!entry)
1625616268
break;
16257-
_PyRecursiveMutex_Unlock(&iterator->mutex);
1625816269
return entry;
1625916270
}
1626016271

1626116272
/* Loop till we get a non-dot directory or finish iterating */
1626216273
}
1626316274

1626416275
/* Already closed, error, or no more files */
16265-
_PyRecursiveMutex_Unlock(&iterator->mutex);
1626616276
ScandirIterator_closedir(iterator);
1626716277
return NULL;
1626816278
}
@@ -16278,10 +16288,10 @@ ScandirIterator_is_closed(ScandirIterator *iterator)
1627816288
static void
1627916289
ScandirIterator_closedir(ScandirIterator *iterator)
1628016290
{
16281-
_PyRecursiveMutex_Lock(&iterator->mutex);
16291+
PyMutex_Lock(&iterator->mutex);
1628216292
DIR *dirp = iterator->dirp;
1628316293
iterator->dirp = NULL;
16284-
_PyRecursiveMutex_Unlock(&iterator->mutex);
16294+
PyMutex_Unlock(&iterator->mutex);
1628516295

1628616296
if (!dirp) {
1628716297
return;
@@ -16297,53 +16307,63 @@ ScandirIterator_closedir(ScandirIterator *iterator)
1629716307
return;
1629816308
}
1629916309

16310+
static int
16311+
ScandirIterator_nextdirentry(ScandirIterator *iterator, struct dirent *direntp)
16312+
{
16313+
int has_error = 0;
16314+
struct dirent *result = NULL;
16315+
PyMutex_Lock(&iterator->mutex);
16316+
if (iterator->dirp) {
16317+
errno = 0;
16318+
Py_BEGIN_ALLOW_THREADS
16319+
result = readdir(iterator->dirp);
16320+
Py_END_ALLOW_THREADS
16321+
has_error = !result && errno != 0;
16322+
}
16323+
/* We need to make a copy of the result before releasing the lock */
16324+
if (result)
16325+
memcpy(direntp, result, sizeof(struct dirent));
16326+
PyMutex_Unlock(&iterator->mutex);
16327+
16328+
/* Error or no more files */
16329+
if (has_error)
16330+
path_error(&iterator->path);
16331+
16332+
return result != NULL;
16333+
}
16334+
1630016335
static PyObject *
1630116336
ScandirIterator_iternext(PyObject *op)
1630216337
{
1630316338
ScandirIterator *iterator = ScandirIterator_CAST(op);
16304-
struct dirent *direntp;
16339+
struct dirent direntp;
1630516340
Py_ssize_t name_len;
1630616341
int is_dot;
1630716342
PyObject *entry;
1630816343

16309-
_PyRecursiveMutex_Lock(&iterator->mutex);
16310-
while (iterator->dirp) {
16311-
errno = 0;
16312-
Py_BEGIN_ALLOW_THREADS
16313-
direntp = readdir(iterator->dirp);
16314-
Py_END_ALLOW_THREADS
16315-
16316-
if (!direntp) {
16317-
/* Error or no more files */
16318-
if (errno != 0)
16319-
path_error(&iterator->path);
16320-
break;
16321-
}
16322-
16344+
while ((ScandirIterator_nextdirentry(iterator, &direntp))) {
1632316345
/* Skip over . and .. */
16324-
name_len = NAMLEN(direntp);
16325-
is_dot = direntp->d_name[0] == '.' &&
16326-
(name_len == 1 || (direntp->d_name[1] == '.' && name_len == 2));
16346+
name_len = NAMLEN(&direntp);
16347+
is_dot = direntp.d_name[0] == '.' &&
16348+
(name_len == 1 || (direntp.d_name[1] == '.' && name_len == 2));
1632716349
if (!is_dot) {
1632816350
PyObject *module = PyType_GetModule(Py_TYPE(iterator));
1632916351
entry = DirEntry_from_posix_info(module,
16330-
&iterator->path, direntp->d_name,
16331-
name_len, direntp->d_ino
16352+
&iterator->path, direntp.d_name,
16353+
name_len, direntp.d_ino
1633216354
#ifdef HAVE_DIRENT_D_TYPE
16333-
, direntp->d_type
16355+
, direntp.d_type
1633416356
#endif
1633516357
);
1633616358
if (!entry)
1633716359
break;
16338-
_PyRecursiveMutex_Unlock(&iterator->mutex);
1633916360
return entry;
1634016361
}
1634116362

1634216363
/* Loop till we get a non-dot directory or finish iterating */
1634316364
}
1634416365

1634516366
/* Already closed, error, or no more files */
16346-
_PyRecursiveMutex_Unlock(&iterator->mutex);
1634716367
ScandirIterator_closedir(iterator);
1634816368
return NULL;
1634916369
}
@@ -16476,7 +16496,7 @@ os_scandir_impl(PyObject *module, path_t *path)
1647616496
if (!iterator)
1647716497
return NULL;
1647816498

16479-
iterator->mutex = (_PyRecursiveMutex){0};
16499+
iterator->mutex = (PyMutex){0};
1648016500
#ifdef MS_WINDOWS
1648116501
iterator->handle = INVALID_HANDLE_VALUE;
1648216502
#else

0 commit comments

Comments
 (0)