errno is sometimes read too late after the error: After another call may have modified it. Here's a patch against py3k. Most of it or a variant applies to 2.7 too, but I haven't really looked at that. I've not looked at math code, where I don't even know what sets errno. There might also be a late errno read at Modules/_ctypes/callproc.c line 794: "space[0] = errno;". Does it want the errno from before _ctypes_get_errobj()? I'm unsure what's going on there. Modules/_multiprocessing/semaphore.c doesn't need the patch's extra variable, it can instead be rearranged with the commented-out patch.
This stuff is hard to write automated tests for, hence there are none. The patch is mostly straightforward: capture errno with new variable err at point of possible error when intervening calculation is needed before testing the value of errno. This seems like a good idea. There is one relocation of memory freeing and the additions of + if (res >= 0) + break; which I cannot evaluate.
Terry J. Reedy writes: > There is one relocation of memory freeing Modules/timemodule.c does '#if,if(..errno..)' after PyMem_Free(outbuf), which can overwrite the desired errno. Instead of reading errno into a temporary, I moved the free into both branches taken by the #if,if(). > and the additions of > + if (res >= 0) > + break; > which I cannot evaluate. errno is only needed after an error., so I moved 'res < 0' out of the 'while'. if (res == MP_EXCEPTION_HAS_BEEN_SET) break; } while (res < 0 && errno == EINTR && !PyErr_CheckSignals()); --> if (res == MP_EXCEPTION_HAS_BEEN_SET) break; if (! (res < 0)) break; } while (errno == EINTR && !PyErr_CheckSignals()); --> if (res >= 0) break; err = errno; if (res == MP_EXCEPTION_HAS_BEEN_SET) break; } while (err == EINTR && !PyErr_CheckSignals());