##// END OF EJS Templates
worker: adapt _blockingreader to work around a python3.8.[0-1] bug (issue6444)...
worker: adapt _blockingreader to work around a python3.8.[0-1] bug (issue6444) Python 3.8.0 is the latest I can load on Ubuntu 18.04, and I regularly hit the TypeError because this function is missing. While it can be avoided by disabling worker usage via config option, that's a bit obscure. I'm limiting the function definition to the narrow range of affected pythons because there were other bugs in this area that were worked around, that I don't fully understand. See the bug report for discussions on why the narrow range, and related commits working around other bugs. Differential Revision: https://phab.mercurial-scm.org/D12627

File last commit:

r49674:bf6cdda9 default
r50103:2fe4efaa stable
Show More
dirs.c
330 lines | 7.2 KiB | text/x-c | CLexer
Yuya Nishihara
parsers: switch to policy importer...
r32372 /*
dirs.c - dynamic directory diddling for dirstates
Copyright 2013 Facebook
This software may be used and distributed according to the terms of
the GNU General Public License, incorporated herein by reference.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 #include <string.h>
Gregory Szorc
cext: reorder #include...
r34439
Yuya Nishihara
parsers: switch to policy importer...
r32372 #include "util.h"
#ifdef IS_PY3K
Martin von Zweigbergk
dirs: fix out-of-bounds access in Py3...
r44290 #define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[0]
Yuya Nishihara
parsers: switch to policy importer...
r32372 #else
#define PYLONG_VALUE(o) PyInt_AS_LONG(o)
#endif
/*
* This is a multiset of directory names, built from the files that
* appear in a dirstate or manifest.
*
* A few implementation notes:
*
* We modify Python integers for refcounting, but those integers are
* never visible to Python code.
*/
Augie Fackler
dirs: tag a struct as not being formattable...
r43506 /* clang-format off */
Yuya Nishihara
parsers: switch to policy importer...
r32372 typedef struct {
PyObject_HEAD
PyObject *dict;
} dirsObject;
Augie Fackler
dirs: tag a struct as not being formattable...
r43506 /* clang-format on */
Yuya Nishihara
parsers: switch to policy importer...
r32372
static inline Py_ssize_t _finddir(const char *path, Py_ssize_t pos)
{
while (pos != -1) {
if (path[pos] == '/')
break;
pos -= 1;
}
Martin von Zweigbergk
util: make util.dirs() and util.finddirs() include root directory (API)...
r42530 if (pos == -1) {
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 return 0;
Martin von Zweigbergk
util: make util.dirs() and util.finddirs() include root directory (API)...
r42530 }
Yuya Nishihara
parsers: switch to policy importer...
r32372
return pos;
}
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 /* Mercurial will fail to run on directory hierarchies deeper than
* this constant, so we should try and keep this constant as big as
* possible.
*/
#define MAX_DIRS_DEPTH 2048
Yuya Nishihara
parsers: switch to policy importer...
r32372 static int _addpath(PyObject *dirs, PyObject *path)
{
const char *cpath = PyBytes_AS_STRING(path);
Py_ssize_t pos = PyBytes_GET_SIZE(path);
PyObject *key = NULL;
int ret = -1;
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 size_t num_slashes = 0;
Yuya Nishihara
parsers: switch to policy importer...
r32372
/* This loop is super critical for performance. That's why we inline
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 * access to Python structs instead of going through a supported API.
* The implementation, therefore, is heavily dependent on CPython
* implementation details. We also commit violations of the Python
* "protocol" such as mutating immutable objects. But since we only
* mutate objects created in this function or in other well-defined
* locations, the references are known so these violations should go
* unnoticed. */
Yuya Nishihara
parsers: switch to policy importer...
r32372 while ((pos = _finddir(cpath, pos - 1)) != -1) {
PyObject *val;
Augie Fackler
dirs: resolve fuzzer OOM situation by disallowing deep directory hierarchies...
r44057 ++num_slashes;
if (num_slashes > MAX_DIRS_DEPTH) {
PyErr_SetString(PyExc_ValueError,
"Directory hierarchy too deep.");
goto bail;
}
Yuya Nishihara
parsers: switch to policy importer...
r32372
Augie Fackler
dirs: reject consecutive slashes in paths...
r43799 /* Sniff for trailing slashes, a marker of an invalid input. */
if (pos > 0 && cpath[pos - 1] == '/') {
PyErr_SetString(
PyExc_ValueError,
"found invalid consecutive slashes in path");
goto bail;
}
Yuya Nishihara
dirs: remove mutable string optimization at all...
r43503 key = PyBytes_FromStringAndSize(cpath, pos);
if (key == NULL)
goto bail;
Yuya Nishihara
parsers: switch to policy importer...
r32372
val = PyDict_GetItem(dirs, key);
if (val != NULL) {
PYLONG_VALUE(val) += 1;
Yuya Nishihara
dirs: remove mutable string optimization at all...
r43503 Py_CLEAR(key);
Yuya Nishihara
parsers: switch to policy importer...
r32372 break;
}
/* Force Python to not reuse a small shared int. */
#ifdef IS_PY3K
val = PyLong_FromLong(0x1eadbeef);
#else
val = PyInt_FromLong(0x1eadbeef);
#endif
if (val == NULL)
goto bail;
PYLONG_VALUE(val) = 1;
ret = PyDict_SetItem(dirs, key, val);
Py_DECREF(val);
if (ret == -1)
goto bail;
Py_CLEAR(key);
}
ret = 0;
bail:
Py_XDECREF(key);
return ret;
}
static int _delpath(PyObject *dirs, PyObject *path)
{
char *cpath = PyBytes_AS_STRING(path);
Py_ssize_t pos = PyBytes_GET_SIZE(path);
PyObject *key = NULL;
int ret = -1;
while ((pos = _finddir(cpath, pos - 1)) != -1) {
PyObject *val;
key = PyBytes_FromStringAndSize(cpath, pos);
if (key == NULL)
goto bail;
val = PyDict_GetItem(dirs, key);
if (val == NULL) {
PyErr_SetString(PyExc_ValueError,
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 "expected a value, found none");
Yuya Nishihara
parsers: switch to policy importer...
r32372 goto bail;
}
if (--PYLONG_VALUE(val) <= 0) {
if (PyDict_DelItem(dirs, key) == -1)
goto bail;
} else
break;
Py_CLEAR(key);
}
ret = 0;
bail:
Py_XDECREF(key);
return ret;
}
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 static int dirs_fromdict(PyObject *dirs, PyObject *source, bool only_tracked)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
PyObject *key, *value;
Py_ssize_t pos = 0;
while (PyDict_Next(source, &pos, &key, &value)) {
if (!PyBytes_Check(key)) {
PyErr_SetString(PyExc_TypeError, "expected string key");
return -1;
}
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 if (only_tracked) {
Yuya Nishihara
parsers: switch to policy importer...
r32372 if (!dirstate_tuple_check(value)) {
PyErr_SetString(PyExc_TypeError,
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 "expected a dirstate tuple");
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
}
dirstate-item: move the C implementation to the same logic...
r48760 if (!(((dirstateItemObject *)value)->flags &
dirstate_flag_wc_tracked))
Yuya Nishihara
parsers: switch to policy importer...
r32372 continue;
}
if (_addpath(dirs, key) == -1)
return -1;
}
return 0;
}
static int dirs_fromiter(PyObject *dirs, PyObject *source)
{
PyObject *iter, *item = NULL;
int ret;
iter = PyObject_GetIter(source);
if (iter == NULL)
return -1;
while ((item = PyIter_Next(iter)) != NULL) {
if (!PyBytes_Check(item)) {
PyErr_SetString(PyExc_TypeError, "expected string");
break;
}
if (_addpath(dirs, item) == -1)
break;
Py_CLEAR(item);
}
ret = PyErr_Occurred() ? -1 : 0;
Py_DECREF(iter);
Py_XDECREF(item);
return ret;
}
/*
* Calculate a refcounted set of directory names for the files in a
* dirstate.
*/
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 static int dirs_init(dirsObject *self, PyObject *args, PyObject *kwargs)
Yuya Nishihara
parsers: switch to policy importer...
r32372 {
PyObject *dirs = NULL, *source = NULL;
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 int only_tracked = 0;
Yuya Nishihara
parsers: switch to policy importer...
r32372 int ret = -1;
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 static char *keywords_name[] = {"map", "only_tracked", NULL};
Yuya Nishihara
parsers: switch to policy importer...
r32372
self->dict = NULL;
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|Oi:__init__",
keywords_name, &source, &only_tracked))
Yuya Nishihara
parsers: switch to policy importer...
r32372 return -1;
dirs = PyDict_New();
if (dirs == NULL)
return -1;
if (source == NULL)
ret = 0;
else if (PyDict_Check(source))
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 ret = dirs_fromdict(dirs, source, (bool)only_tracked);
else if (only_tracked)
Yuya Nishihara
parsers: switch to policy importer...
r32372 PyErr_SetString(PyExc_ValueError,
pathutil: replace the `skip` argument of `dirs` with a boolean...
r48756 "`only_tracked` is only supported "
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 "with a dict source");
Yuya Nishihara
parsers: switch to policy importer...
r32372 else
ret = dirs_fromiter(dirs, source);
if (ret == -1)
Py_XDECREF(dirs);
else
self->dict = dirs;
return ret;
}
PyObject *dirs_addpath(dirsObject *self, PyObject *args)
{
PyObject *path;
if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path))
return NULL;
if (_addpath(self->dict, path) == -1)
return NULL;
Py_RETURN_NONE;
}
static PyObject *dirs_delpath(dirsObject *self, PyObject *args)
{
PyObject *path;
if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path))
return NULL;
if (_delpath(self->dict, path) == -1)
return NULL;
Py_RETURN_NONE;
}
static int dirs_contains(dirsObject *self, PyObject *value)
{
return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0;
}
static void dirs_dealloc(dirsObject *self)
{
Py_XDECREF(self->dict);
PyObject_Del(self);
}
static PyObject *dirs_iter(dirsObject *self)
{
return PyObject_GetIter(self->dict);
}
static PySequenceMethods dirs_sequence_methods;
static PyMethodDef dirs_methods[] = {
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 {"addpath", (PyCFunction)dirs_addpath, METH_VARARGS, "add a path"},
{"delpath", (PyCFunction)dirs_delpath, METH_VARARGS, "remove a path"},
{NULL} /* Sentinel */
Yuya Nishihara
parsers: switch to policy importer...
r32372 };
Augie Fackler
dirs: give formatting oversight to clang-format...
r43507 static PyTypeObject dirsType = {PyVarObject_HEAD_INIT(NULL, 0)};
Yuya Nishihara
parsers: switch to policy importer...
r32372
void dirs_module_init(PyObject *mod)
{
dirs_sequence_methods.sq_contains = (objobjproc)dirs_contains;
dirsType.tp_name = "parsers.dirs";
dirsType.tp_new = PyType_GenericNew;
dirsType.tp_basicsize = sizeof(dirsObject);
dirsType.tp_dealloc = (destructor)dirs_dealloc;
dirsType.tp_as_sequence = &dirs_sequence_methods;
dirsType.tp_flags = Py_TPFLAGS_DEFAULT;
dirsType.tp_doc = "dirs";
dirsType.tp_iter = (getiterfunc)dirs_iter;
dirsType.tp_methods = dirs_methods;
dirsType.tp_init = (initproc)dirs_init;
if (PyType_Ready(&dirsType) < 0)
return;
Py_INCREF(&dirsType);
PyModule_AddObject(mod, "dirs", (PyObject *)&dirsType);
}