summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_socket.py7
-rw-r--r--Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst1
-rw-r--r--Modules/socketmodule.c394
-rw-r--r--Modules/socketmodule.h1
-rw-r--r--Tools/c-analyzer/cpython/globals-to-fix.tsv6
5 files changed, 242 insertions, 167 deletions
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
index 32252f7..bb7bf43 100644
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -8,6 +8,7 @@ import _thread as thread
import array
import contextlib
import errno
+import gc
import io
import itertools
import math
@@ -836,6 +837,12 @@ def requireSocket(*args):
class GeneralModuleTests(unittest.TestCase):
+ @unittest.skipUnless(_socket is not None, 'need _socket module')
+ def test_socket_type(self):
+ self.assertTrue(gc.is_tracked(_socket.socket))
+ with self.assertRaisesRegex(TypeError, "immutable"):
+ _socket.socket.foo = 1
+
def test_SocketType_is_socketobject(self):
import _socket
self.assertTrue(socket.SocketType is _socket.socket)
diff --git a/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst b/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst
new file mode 100644
index 0000000..e7586a2
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-04-03-23-43-12.gh-issue-103092.3xqk4y.rst
@@ -0,0 +1 @@
+Isolate :mod:`!_socket` (apply :pep:`687`). Patch by Erlend E. Aasland.
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 49342b3..656cd54 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -108,6 +108,7 @@ Local naming conventions:
#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_fileutils.h" // _Py_set_inheritable()
+#include "pycore_moduleobject.h" // _PyModule_GetState
#include "structmember.h" // PyMemberDef
#ifdef _Py_MEMORY_SANITIZER
@@ -337,9 +338,9 @@ static FlagRuntimeInfo win_runtime_flags[] = {
/*[clinic input]
module _socket
-class _socket.socket "PySocketSockObject *" "&sock_type"
+class _socket.socket "PySocketSockObject *" "clinic_state()->sock_type"
[clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7a8313d9b7f51988]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=2db2489bd2219fd8]*/
static int
remove_unusable_flags(PyObject *m)
@@ -541,22 +542,59 @@ remove_unusable_flags(PyObject *m)
#define INADDR_NONE (-1)
#endif
+typedef struct _socket_state {
+ /* The sock_type variable contains pointers to various functions,
+ some of which call new_sockobject(), which uses sock_type, so
+ there has to be a circular reference. */
+ PyTypeObject *sock_type;
+
+ /* Global variable holding the exception type for errors detected
+ by this module (but not argument type or memory errors, etc.). */
+ PyObject *socket_herror;
+ PyObject *socket_gaierror;
+
+ /* Default timeout for new sockets */
+ _PyTime_t defaulttimeout;
+
+#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+ /* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
+ int accept4_works;
+#endif
+#endif
+
+#ifdef SOCK_CLOEXEC
+ /* socket() and socketpair() fail with EINVAL on Linux kernel older
+ * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
+ int sock_cloexec_works;
+#endif
+} socket_state;
+
+static inline socket_state *
+get_module_state(PyObject *mod)
+{
+ void *state = _PyModule_GetState(mod);
+ assert(state != NULL);
+ return (socket_state *)state;
+}
+
+static struct PyModuleDef socketmodule;
+
+static inline socket_state *
+find_module_state_by_def(PyTypeObject *type)
+{
+ PyObject *mod = PyType_GetModuleByDef(type, &socketmodule);
+ assert(mod != NULL);
+ return get_module_state(mod);
+}
+
+#define clinic_state() (find_module_state_by_def(type))
#include "clinic/socketmodule.c.h"
+#undef clinic_state
/* XXX There's a problem here: *static* functions are not supposed to have
a Py prefix (or use CapitalizedWords). Later... */
-/* Global variable holding the exception type for errors detected
- by this module (but not argument type or memory errors, etc.). */
-static PyObject *socket_herror;
-static PyObject *socket_gaierror;
-
-/* A forward reference to the socket type object.
- The sock_type variable contains pointers to various functions,
- some of which call new_sockobject(), which uses sock_type, so
- there has to be a circular reference. */
-static PyTypeObject sock_type;
-
#if defined(HAVE_POLL_H)
#include <poll.h>
#elif defined(HAVE_SYS_POLL_H)
@@ -641,7 +679,7 @@ set_error(void)
#if defined(HAVE_GETHOSTBYNAME_R) || defined (HAVE_GETHOSTBYNAME) || defined (HAVE_GETHOSTBYADDR)
static PyObject *
-set_herror(int h_error)
+set_herror(socket_state *state, int h_error)
{
PyObject *v;
@@ -651,7 +689,7 @@ set_herror(int h_error)
v = Py_BuildValue("(is)", h_error, "host not found");
#endif
if (v != NULL) {
- PyErr_SetObject(socket_herror, v);
+ PyErr_SetObject(state->socket_herror, v);
Py_DECREF(v);
}
@@ -662,7 +700,7 @@ set_herror(int h_error)
#ifdef HAVE_GETADDRINFO
static PyObject *
-set_gaierror(int error)
+set_gaierror(socket_state *state, int error)
{
PyObject *v;
@@ -678,7 +716,7 @@ set_gaierror(int error)
v = Py_BuildValue("(is)", error, "getaddrinfo failed");
#endif
if (v != NULL) {
- PyErr_SetObject(socket_gaierror, v);
+ PyErr_SetObject(state->socket_gaierror, v);
Py_DECREF(v);
}
@@ -991,11 +1029,8 @@ sock_call(PySocketSockObject *s,
/* Initialize a new socket object. */
-/* Default timeout for new sockets */
-static _PyTime_t defaulttimeout = _PYTIME_FROMSECONDS(-1);
-
static int
-init_sockobject(PySocketSockObject *s,
+init_sockobject(socket_state *state, PySocketSockObject *s,
SOCKET_T fd, int family, int type, int proto)
{
s->sock_fd = fd;
@@ -1025,13 +1060,14 @@ init_sockobject(PySocketSockObject *s,
else
#endif
{
- s->sock_timeout = defaulttimeout;
- if (defaulttimeout >= 0) {
+ s->sock_timeout = state->defaulttimeout;
+ if (state->defaulttimeout >= 0) {
if (internal_setblocking(s, 0) == -1) {
return -1;
}
}
}
+ s->state = state;
return 0;
}
@@ -1043,14 +1079,15 @@ init_sockobject(PySocketSockObject *s,
in NEWOBJ()). */
static PySocketSockObject *
-new_sockobject(SOCKET_T fd, int family, int type, int proto)
+new_sockobject(socket_state *state, SOCKET_T fd, int family, int type,
+ int proto)
{
- PySocketSockObject *s;
- s = (PySocketSockObject *)
- PyType_GenericNew(&sock_type, NULL, NULL);
- if (s == NULL)
+ PyTypeObject *tp = state->sock_type;
+ PySocketSockObject *s = (PySocketSockObject *)tp->tp_alloc(tp, 0);
+ if (s == NULL) {
return NULL;
- if (init_sockobject(s, fd, family, type, proto) == -1) {
+ }
+ if (init_sockobject(state, s, fd, family, type, proto) == -1) {
Py_DECREF(s);
return NULL;
}
@@ -1074,7 +1111,8 @@ static PyThread_type_lock netdb_lock;
an error occurred; then an exception is raised. */
static int
-setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int af)
+setipaddr(socket_state *state, const char *name, struct sockaddr *addr_ret,
+ size_t addr_ret_size, int af)
{
struct addrinfo hints, *res;
int error;
@@ -1095,7 +1133,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int
outcome of the first call. */
if (error) {
res = NULL; // no-op, remind us that it is invalid; gh-100795
- set_gaierror(error);
+ set_gaierror(state, error);
return -1;
}
switch (res->ai_family) {
@@ -1206,7 +1244,7 @@ setipaddr(const char *name, struct sockaddr *addr_ret, size_t addr_ret_size, int
Py_END_ALLOW_THREADS
if (error) {
res = NULL; // no-op, remind us that it is invalid; gh-100795
- set_gaierror(error);
+ set_gaierror(state, error);
return -1;
}
if (res->ai_addrlen < addr_ret_size)
@@ -1889,7 +1927,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
return 0;
}
struct sockaddr_in* addr = &addrbuf->in;
- result = setipaddr(host.buf, (struct sockaddr *)addr,
+ result = setipaddr(s->state, host.buf, (struct sockaddr *)addr,
sizeof(*addr), AF_INET);
idna_cleanup(&host);
if (result < 0)
@@ -1934,7 +1972,7 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
return 0;
}
struct sockaddr_in6* addr = &addrbuf->in6;
- result = setipaddr(host.buf, (struct sockaddr *)addr,
+ result = setipaddr(s->state, host.buf, (struct sockaddr *)addr,
sizeof(*addr), AF_INET6);
idna_cleanup(&host);
if (result < 0)
@@ -2813,10 +2851,6 @@ struct sock_accept {
};
#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
-#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
-/* accept4() is available on Linux 2.6.28+ and glibc 2.10 */
-static int accept4_works = -1;
-#endif
static int
sock_accept_impl(PySocketSockObject *s, void *data)
@@ -2835,15 +2869,16 @@ sock_accept_impl(PySocketSockObject *s, void *data)
#endif
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- if (accept4_works != 0) {
+ socket_state *state = s->state;
+ if (state->accept4_works != 0) {
ctx->result = accept4(s->sock_fd, addr, paddrlen,
SOCK_CLOEXEC);
- if (ctx->result == INVALID_SOCKET && accept4_works == -1) {
+ if (ctx->result == INVALID_SOCKET && state->accept4_works == -1) {
/* On Linux older than 2.6.28, accept4() fails with ENOSYS */
- accept4_works = (errno != ENOSYS);
+ state->accept4_works = (errno != ENOSYS);
}
}
- if (accept4_works == 0)
+ if (state->accept4_works == 0)
ctx->result = accept(s->sock_fd, addr, paddrlen);
#else
ctx->result = accept(s->sock_fd, addr, paddrlen);
@@ -2896,7 +2931,8 @@ sock_accept(PySocketSockObject *s, PyObject *Py_UNUSED(ignored))
#else
#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
- if (!accept4_works)
+ socket_state *state = s->state;
+ if (!state->accept4_works)
#endif
{
if (_Py_set_inheritable(newfd, 0, NULL) < 0) {
@@ -5219,13 +5255,23 @@ sock_finalize(PySocketSockObject *s)
PyErr_SetRaisedException(exc);
}
+static int
+sock_traverse(PySocketSockObject *s, visitproc visit, void *arg)
+{
+ Py_VISIT(Py_TYPE(s));
+ return 0;
+}
+
static void
sock_dealloc(PySocketSockObject *s)
{
- if (PyObject_CallFinalizerFromDealloc((PyObject *)s) < 0)
+ if (PyObject_CallFinalizerFromDealloc((PyObject *)s) < 0) {
return;
-
- Py_TYPE(s)->tp_free((PyObject *)s);
+ }
+ PyTypeObject *tp = Py_TYPE(s);
+ PyObject_GC_UnTrack(s);
+ tp->tp_free((PyObject *)s);
+ Py_DECREF(tp);
}
@@ -5277,12 +5323,6 @@ sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
/* Initialize a new socket object. */
-#ifdef SOCK_CLOEXEC
-/* socket() and socketpair() fail with EINVAL on Linux kernel older
- * than 2.6.27 if SOCK_CLOEXEC flag is set in the socket type. */
-static int sock_cloexec_works = -1;
-#endif
-
/*ARGSUSED*/
#ifndef HAVE_SOCKET
@@ -5310,10 +5350,11 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
{
SOCKET_T fd = INVALID_SOCKET;
+ socket_state *state = find_module_state_by_def(Py_TYPE(self));
#ifndef MS_WINDOWS
#ifdef SOCK_CLOEXEC
- int *atomic_flag_works = &sock_cloexec_works;
+ int *atomic_flag_works = &state->sock_cloexec_works;
#else
int *atomic_flag_works = NULL;
#endif
@@ -5468,15 +5509,15 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
/* UNIX */
Py_BEGIN_ALLOW_THREADS
#ifdef SOCK_CLOEXEC
- if (sock_cloexec_works != 0) {
+ if (state->sock_cloexec_works != 0) {
fd = socket(family, type | SOCK_CLOEXEC, proto);
- if (sock_cloexec_works == -1) {
+ if (state->sock_cloexec_works == -1) {
if (fd >= 0) {
- sock_cloexec_works = 1;
+ state->sock_cloexec_works = 1;
}
else if (errno == EINVAL) {
/* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
- sock_cloexec_works = 0;
+ state->sock_cloexec_works = 0;
fd = socket(family, type, proto);
}
}
@@ -5499,7 +5540,7 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
}
#endif
}
- if (init_sockobject(self, fd, family, type, proto) == -1) {
+ if (init_sockobject(state, self, fd, family, type, proto) == -1) {
SOCKETCLOSE(fd);
return -1;
}
@@ -5511,55 +5552,26 @@ sock_initobj_impl(PySocketSockObject *self, int family, int type, int proto,
/* Type object for socket objects. */
-static PyTypeObject sock_type = {
- PyVarObject_HEAD_INIT(0, 0) /* Must fill in type value later */
- "_socket.socket", /* tp_name */
- sizeof(PySocketSockObject), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor)sock_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_as_async */
- (reprfunc)sock_repr, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
- sock_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- sock_methods, /* tp_methods */
- sock_memberlist, /* tp_members */
- sock_getsetlist, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- sock_initobj, /* tp_init */
- PyType_GenericAlloc, /* tp_alloc */
- sock_new, /* tp_new */
- PyObject_Del, /* tp_free */
- 0, /* tp_is_gc */
- 0, /* tp_bases */
- 0, /* tp_mro */
- 0, /* tp_cache */
- 0, /* tp_subclasses */
- 0, /* tp_weaklist */
- 0, /* tp_del */
- 0, /* tp_version_tag */
- (destructor)sock_finalize, /* tp_finalize */
+static PyType_Slot sock_slots[] = {
+ {Py_tp_dealloc, sock_dealloc},
+ {Py_tp_traverse, sock_traverse},
+ {Py_tp_repr, sock_repr},
+ {Py_tp_doc, (void *)sock_doc},
+ {Py_tp_methods, sock_methods},
+ {Py_tp_members, sock_memberlist},
+ {Py_tp_getset, sock_getsetlist},
+ {Py_tp_init, sock_initobj},
+ {Py_tp_new, sock_new},
+ {Py_tp_finalize, sock_finalize},
+ {0, NULL},
+};
+
+static PyType_Spec sock_spec = {
+ .name = "_socket.socket",
+ .basicsize = sizeof(PySocketSockObject),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_IMMUTABLETYPE),
+ .slots = sock_slots,
};
@@ -5687,8 +5699,12 @@ socket_gethostbyname(PyObject *self, PyObject *args)
if (PySys_Audit("socket.gethostbyname", "O", args) < 0) {
goto finally;
}
- if (setipaddr(name, (struct sockaddr *)&addrbuf, sizeof(addrbuf), AF_INET) < 0)
+ socket_state *state = get_module_state(self);
+ int rc = setipaddr(state, name, (struct sockaddr *)&addrbuf,
+ sizeof(addrbuf), AF_INET);
+ if (rc < 0) {
goto finally;
+ }
ret = make_ipv4_addr(&addrbuf);
finally:
PyMem_Free(name);
@@ -5719,7 +5735,8 @@ sock_decode_hostname(const char *name)
/* Convenience function common to gethostbyname_ex and gethostbyaddr */
static PyObject *
-gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af)
+gethost_common(socket_state *state, struct hostent *h, struct sockaddr *addr,
+ size_t alen, int af)
{
char **pch;
PyObject *rtn_tuple = (PyObject *)NULL;
@@ -5730,7 +5747,7 @@ gethost_common(struct hostent *h, struct sockaddr *addr, size_t alen, int af)
if (h == NULL) {
/* Let's get real error message to return */
- set_herror(h_errno);
+ set_herror(state, h_errno);
return NULL;
}
@@ -5877,8 +5894,10 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args)
if (PySys_Audit("socket.gethostbyname", "O", args) < 0) {
goto finally;
}
- if (setipaddr(name, SAS2SA(&addr), sizeof(addr), AF_INET) < 0)
+ socket_state *state = get_module_state(self);
+ if (setipaddr(state, name, SAS2SA(&addr), sizeof(addr), AF_INET) < 0) {
goto finally;
+ }
Py_BEGIN_ALLOW_THREADS
#ifdef HAVE_GETHOSTBYNAME_R
#if defined(HAVE_GETHOSTBYNAME_R_6_ARG)
@@ -5904,7 +5923,7 @@ socket_gethostbyname_ex(PyObject *self, PyObject *args)
Therefore, we cast the sockaddr_storage into sockaddr to
access sa_family. */
sa = SAS2SA(&addr);
- ret = gethost_common(h, SAS2SA(&addr), sizeof(addr),
+ ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr),
sa->sa_family);
#ifdef USE_GETHOSTBYNAME_LOCK
PyThread_release_lock(netdb_lock);
@@ -5960,8 +5979,10 @@ socket_gethostbyaddr(PyObject *self, PyObject *args)
goto finally;
}
af = AF_UNSPEC;
- if (setipaddr(ip_num, sa, sizeof(addr), af) < 0)
+ socket_state *state = get_module_state(self);
+ if (setipaddr(state, ip_num, sa, sizeof(addr), af) < 0) {
goto finally;
+ }
af = sa->sa_family;
ap = NULL;
/* al = 0; */
@@ -6002,7 +6023,7 @@ socket_gethostbyaddr(PyObject *self, PyObject *args)
h = gethostbyaddr(ap, al, af);
#endif /* HAVE_GETHOSTBYNAME_R */
Py_END_ALLOW_THREADS
- ret = gethost_common(h, SAS2SA(&addr), sizeof(addr), af);
+ ret = gethost_common(state, h, SAS2SA(&addr), sizeof(addr), af);
#ifdef USE_GETHOSTBYNAME_LOCK
PyThread_release_lock(netdb_lock);
#endif
@@ -6221,8 +6242,9 @@ socket_socketpair(PyObject *self, PyObject *args)
SOCKET_T sv[2];
int family, type = SOCK_STREAM, proto = 0;
PyObject *res = NULL;
+ socket_state *state = get_module_state(self);
#ifdef SOCK_CLOEXEC
- int *atomic_flag_works = &sock_cloexec_works;
+ int *atomic_flag_works = &state->sock_cloexec_works;
#else
int *atomic_flag_works = NULL;
#endif
@@ -6240,15 +6262,15 @@ socket_socketpair(PyObject *self, PyObject *args)
/* Create a pair of socket fds */
Py_BEGIN_ALLOW_THREADS
#ifdef SOCK_CLOEXEC
- if (sock_cloexec_works != 0) {
+ if (state->sock_cloexec_works != 0) {
ret = socketpair(family, type | SOCK_CLOEXEC, proto, sv);
- if (sock_cloexec_works == -1) {
+ if (state->sock_cloexec_works == -1) {
if (ret >= 0) {
- sock_cloexec_works = 1;
+ state->sock_cloexec_works = 1;
}
else if (errno == EINVAL) {
/* Linux older than 2.6.27 does not support SOCK_CLOEXEC */
- sock_cloexec_works = 0;
+ state->sock_cloexec_works = 0;
ret = socketpair(family, type, proto, sv);
}
}
@@ -6268,10 +6290,10 @@ socket_socketpair(PyObject *self, PyObject *args)
if (_Py_set_inheritable(sv[1], 0, atomic_flag_works) < 0)
goto finally;
- s0 = new_sockobject(sv[0], family, type, proto);
+ s0 = new_sockobject(state, sv[0], family, type, proto);
if (s0 == NULL)
goto finally;
- s1 = new_sockobject(sv[1], family, type, proto);
+ s1 = new_sockobject(state, sv[1], family, type, proto);
if (s1 == NULL)
goto finally;
res = PyTuple_Pack(2, s0, s1);
@@ -6726,7 +6748,8 @@ socket_getaddrinfo(PyObject *self, PyObject *args, PyObject* kwargs)
Py_END_ALLOW_THREADS
if (error) {
res0 = NULL; // gh-100795
- set_gaierror(error);
+ socket_state *state = get_module_state(self);
+ set_gaierror(state, error);
goto err;
}
@@ -6825,7 +6848,8 @@ socket_getnameinfo(PyObject *self, PyObject *args)
Py_END_ALLOW_THREADS
if (error) {
res = NULL; // gh-100795
- set_gaierror(error);
+ socket_state *state = get_module_state(self);
+ set_gaierror(state, error);
goto fail;
}
if (res->ai_next) {
@@ -6857,7 +6881,8 @@ socket_getnameinfo(PyObject *self, PyObject *args)
error = getnameinfo(res->ai_addr, (socklen_t) res->ai_addrlen,
hbuf, sizeof(hbuf), pbuf, sizeof(pbuf), flags);
if (error) {
- set_gaierror(error);
+ socket_state *state = get_module_state(self);
+ set_gaierror(state, error);
goto fail;
}
@@ -6883,11 +6908,12 @@ Get host and port for a sockaddr.");
static PyObject *
socket_getdefaulttimeout(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- if (defaulttimeout < 0) {
+ socket_state *state = get_module_state(self);
+ if (state->defaulttimeout < 0) {
Py_RETURN_NONE;
}
else {
- double seconds = _PyTime_AsSecondsDouble(defaulttimeout);
+ double seconds = _PyTime_AsSecondsDouble(state->defaulttimeout);
return PyFloat_FromDouble(seconds);
}
}
@@ -6907,7 +6933,8 @@ socket_setdefaulttimeout(PyObject *self, PyObject *arg)
if (socket_parse_timeout(&timeout, arg) < 0)
return NULL;
- defaulttimeout = timeout;
+ socket_state *state = get_module_state(self);
+ state->defaulttimeout = timeout;
Py_RETURN_NONE;
}
@@ -7293,7 +7320,7 @@ sock_destroy_api(PyObject *capsule)
}
static PySocketModule_APIObject *
-sock_get_api(void)
+sock_get_api(socket_state *state)
{
PySocketModule_APIObject *capi = PyMem_Malloc(sizeof(PySocketModule_APIObject));
if (capi == NULL) {
@@ -7301,7 +7328,7 @@ sock_get_api(void)
return NULL;
}
- capi->Sock_Type = (PyTypeObject *)Py_NewRef(&sock_type);
+ capi->Sock_Type = (PyTypeObject *)Py_NewRef(state->sock_type);
capi->error = Py_NewRef(PyExc_OSError);
capi->timeout_error = Py_NewRef(PyExc_TimeoutError);
return capi;
@@ -7323,47 +7350,38 @@ PyDoc_STRVAR(socket_doc,
\n\
See the socket module for documentation.");
-static struct PyModuleDef socketmodule = {
- PyModuleDef_HEAD_INIT,
- PySocket_MODULE_NAME,
- socket_doc,
- -1,
- socket_methods,
- NULL,
- NULL,
- NULL,
- NULL
-};
-
-PyMODINIT_FUNC
-PyInit__socket(void)
+static int
+socket_exec(PyObject *m)
{
- PyObject *m = NULL;
-
if (!os_init()) {
goto error;
}
- Py_SET_TYPE(&sock_type, &PyType_Type);
- m = PyModule_Create(&socketmodule);
- if (m == NULL) {
- goto error;
- }
+ socket_state *state = get_module_state(m);
+ state->defaulttimeout = _PYTIME_FROMSECONDS(-1);
+
+#if defined(HAVE_ACCEPT) || defined(HAVE_ACCEPT4)
+#if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
+ state->accept4_works = -1;
+#endif
+#endif
+
+#ifdef SOCK_CLOEXEC
+ state->sock_cloexec_works = -1;
+#endif
#define ADD_EXC(MOD, NAME, VAR, BASE) do { \
VAR = PyErr_NewException("socket." NAME, BASE, NULL); \
if (VAR == NULL) { \
goto error; \
} \
- int rc = PyModule_AddObjectRef(MOD, NAME, VAR); \
- Py_DECREF(VAR); \
- if (rc < 0) { \
+ if (PyModule_AddObjectRef(MOD, NAME, VAR) < 0) { \
goto error; \
} \
} while (0)
- ADD_EXC(m, "herror", socket_herror, PyExc_OSError);
- ADD_EXC(m, "gaierror", socket_gaierror, PyExc_OSError);
+ ADD_EXC(m, "herror", state->socket_herror, PyExc_OSError);
+ ADD_EXC(m, "gaierror", state->socket_gaierror, PyExc_OSError);
#undef ADD_EXC
@@ -7373,10 +7391,16 @@ PyInit__socket(void)
if (PyModule_AddObjectRef(m, "timeout", PyExc_TimeoutError) < 0) {
goto error;
}
- if (PyModule_AddObjectRef(m, "SocketType", (PyObject *)&sock_type) < 0) {
+
+ PyObject *sock_type = PyType_FromMetaclass(NULL, m, &sock_spec, NULL);
+ if (sock_type == NULL) {
+ goto error;
+ }
+ state->sock_type = (PyTypeObject *)sock_type;
+ if (PyModule_AddObjectRef(m, "SocketType", sock_type) < 0) {
goto error;
}
- if (PyModule_AddType(m, &sock_type) < 0) {
+ if (PyModule_AddType(m, state->sock_type) < 0) {
goto error;
}
@@ -7391,7 +7415,7 @@ PyInit__socket(void)
}
/* Export C API */
- PySocketModule_APIObject *capi = sock_get_api();
+ PySocketModule_APIObject *capi = sock_get_api(state);
if (capi == NULL) {
goto error;
}
@@ -8813,9 +8837,57 @@ PyInit__socket(void)
#undef ADD_INT_CONST
#undef ADD_STR_CONST
- return m;
+ return 0;
error:
- Py_XDECREF(m);
- return NULL;
+ return -1;
+}
+
+static struct PyModuleDef_Slot socket_slots[] = {
+ {Py_mod_exec, socket_exec},
+ {0, NULL},
+};
+
+static int
+socket_traverse(PyObject *mod, visitproc visit, void *arg)
+{
+ socket_state *state = get_module_state(mod);
+ Py_VISIT(state->sock_type);
+ Py_VISIT(state->socket_herror);
+ Py_VISIT(state->socket_gaierror);
+ return 0;
+}
+
+static int
+socket_clear(PyObject *mod)
+{
+ socket_state *state = get_module_state(mod);
+ Py_CLEAR(state->sock_type);
+ Py_CLEAR(state->socket_herror);
+ Py_CLEAR(state->socket_gaierror);
+ return 0;
+}
+
+static void
+socket_free(void *mod)
+{
+ (void)socket_clear((PyObject *)mod);
+}
+
+static struct PyModuleDef socketmodule = {
+ .m_base = PyModuleDef_HEAD_INIT,
+ .m_name = PySocket_MODULE_NAME,
+ .m_doc = socket_doc,
+ .m_size = sizeof(socket_state),
+ .m_methods = socket_methods,
+ .m_slots = socket_slots,
+ .m_traverse = socket_traverse,
+ .m_clear = socket_clear,
+ .m_free = socket_free,
+};
+
+PyMODINIT_FUNC
+PyInit__socket(void)
+{
+ return PyModuleDef_Init(&socketmodule);
}
diff --git a/Modules/socketmodule.h b/Modules/socketmodule.h
index f31ba53..f5ca004 100644
--- a/Modules/socketmodule.h
+++ b/Modules/socketmodule.h
@@ -322,6 +322,7 @@ typedef struct {
sets a Python exception */
_PyTime_t sock_timeout; /* Operation timeout in seconds;
0.0 means non-blocking */
+ struct _socket_state *state;
} PySocketSockObject;
/* --- C API ----------------------------------------------------*/
diff --git a/Tools/c-analyzer/cpython/globals-to-fix.tsv b/Tools/c-analyzer/cpython/globals-to-fix.tsv
index 0620c7e..e36024a 100644
--- a/Tools/c-analyzer/cpython/globals-to-fix.tsv
+++ b/Tools/c-analyzer/cpython/globals-to-fix.tsv
@@ -392,7 +392,6 @@ Modules/_decimal/_decimal.c - PyDecSignalDictMixin_Type -
Modules/_decimal/_decimal.c - PyDec_Type -
Modules/ossaudiodev.c - OSSAudioType -
Modules/ossaudiodev.c - OSSMixerType -
-Modules/socketmodule.c - sock_type -
Modules/xxmodule.c - Null_Type -
Modules/xxmodule.c - Str_Type -
Modules/xxmodule.c - Xxo_Type -
@@ -416,8 +415,6 @@ Modules/_cursesmodule.c - PyCursesError -
Modules/_decimal/_decimal.c - DecimalException -
Modules/_tkinter.c - Tkinter_TclError -
Modules/ossaudiodev.c - OSSAudioError -
-Modules/socketmodule.c - socket_herror -
-Modules/socketmodule.c - socket_gaierror -
Modules/xxlimited_35.c - ErrorObject -
Modules/xxmodule.c - ErrorObject -
@@ -514,8 +511,6 @@ Modules/cjkcodecs/cjkcodecs.h - mapping_list -
Modules/readline.c - libedit_append_replace_history_offset -
Modules/readline.c - using_libedit_emulation -
Modules/readline.c - libedit_history_start -
-Modules/socketmodule.c - accept4_works -
-Modules/socketmodule.c - sock_cloexec_works -
##-----------------------
## state
@@ -541,4 +536,3 @@ Modules/readline.c - sigwinch_ohandler -
Modules/readline.c - completed_input_string -
Modules/rotatingtree.c - random_stream -
Modules/rotatingtree.c - random_value -
-Modules/socketmodule.c - defaulttimeout -