Patch #1680961: remove sys.exitfunc and replace it with a private C API. Also, reimplement atexit in C so it can take advantage of this private API.
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
index d60ca9e..31c8329 100644
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -56,7 +56,7 @@
 			      PyCompilerFlags *);
 static void err_input(perrdetail *);
 static void initsigs(void);
-static void call_sys_exitfunc(void);
+static void call_py_exitfuncs(void);
 static void call_ll_exitfuncs(void);
 extern void _PyUnicode_Init(void);
 extern void _PyUnicode_Fini(void);
@@ -355,7 +355,7 @@
 	 * threads created thru it, so this also protects pending imports in
 	 * the threads created via Threading.
 	 */
-	call_sys_exitfunc();
+	call_py_exitfuncs();
 	initialized = 0;
 
 	/* Get current thread state and interpreter pointer */
@@ -1557,6 +1557,23 @@
 #include "pythread.h"
 #endif
 
+static void (*pyexitfunc)(void) = NULL;
+/* For the atexit module. */
+void _Py_PyAtExit(void (*func)(void))
+{
+	pyexitfunc = func;
+}
+
+static void
+call_py_exitfuncs(void)
+{
+	if (pyexitfunc == NULL) 
+		return;
+
+	(*pyexitfunc)();
+	PyErr_Clear();
+}
+
 #define NEXITFUNCS 32
 static void (*exitfuncs[NEXITFUNCS])(void);
 static int nexitfuncs = 0;
@@ -1570,27 +1587,6 @@
 }
 
 static void
-call_sys_exitfunc(void)
-{
-	PyObject *exitfunc = PySys_GetObject("exitfunc");
-
-	if (exitfunc) {
-		PyObject *res;
-		Py_INCREF(exitfunc);
-		PySys_SetObject("exitfunc", (PyObject *)NULL);
-		res = PyEval_CallObject(exitfunc, (PyObject *)NULL);
-		if (res == NULL) {
-			if (!PyErr_ExceptionMatches(PyExc_SystemExit)) {
-				PySys_WriteStderr("Error in sys.exitfunc:\n");
-			}
-			PyErr_Print();
-		}
-		Py_DECREF(exitfunc);
-	}
-
-}
-
-static void
 call_ll_exitfuncs(void)
 {
 	while (nexitfuncs > 0)