Merge branch 'fix-829'

This commit is contained in:
Daniele Varrazzo 2019-03-18 01:53:59 +00:00
commit e569e49b8b
5 changed files with 83 additions and 10 deletions

13
NEWS
View File

@ -24,8 +24,6 @@ New features:
(:ticket:`#732`). (:ticket:`#732`).
- Added *fetch* parameter to `~psycopg2.extras.execute_values()` function - Added *fetch* parameter to `~psycopg2.extras.execute_values()` function
(:ticket:`#813`). (:ticket:`#813`).
- Fixed adaptation of numeric subclasses such as `~enum.IntEnum`
(:ticket:`#591`).
- `!str()` on `~psycopg2.extras.Range` produces a human-readable representation - `!str()` on `~psycopg2.extras.Range` produces a human-readable representation
(:ticket:`#773`). (:ticket:`#773`).
- `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows - `~psycopg2.extras.DictCursor` and `~psycopg2.extras.RealDictCursor` rows
@ -33,8 +31,15 @@ New features:
- Added `~psycopg2.extensions.Diagnostics.severity_nonlocalized` attribute on - Added `~psycopg2.extensions.Diagnostics.severity_nonlocalized` attribute on
the `~psycopg2.extensions.Diagnostics` object (:ticket:`#783`). the `~psycopg2.extensions.Diagnostics` object (:ticket:`#783`).
- More efficient `~psycopg2.extras.NamedTupleCursor` (:ticket:`#838`). - More efficient `~psycopg2.extras.NamedTupleCursor` (:ticket:`#838`).
- Async communication improved to fix blocking on results returned at
different times (:ticket:`#856`). Bug fixes:
- Fixed connections occasionally broken by the unrelated use of the
multiprocessing module (:ticket:`#829`).
- Fixed async communication blocking if results are returned in different
chunks, e.g. with notices interspersed to the results (:ticket:`#856`).
- Fixed adaptation of numeric subclasses such as `~enum.IntEnum`
(:ticket:`#591`).
Other changes: Other changes:

View File

@ -33,6 +33,18 @@
# define HIDDEN # define HIDDEN
#endif #endif
/* support for getpid() */
#if defined( __GNUC__)
#define CONN_CHECK_PID
#include <sys/types.h>
#include <unistd.h>
#endif
#ifdef _WIN32
/* Windows doesn't seem affected by bug #829: just make it compile. */
#define pid_t int
#endif
/* debug printf-like function */ /* debug printf-like function */
#ifdef PSYCOPG_DEBUG #ifdef PSYCOPG_DEBUG
extern HIDDEN int psycopg_debug_enabled; extern HIDDEN int psycopg_debug_enabled;
@ -40,8 +52,6 @@ extern HIDDEN int psycopg_debug_enabled;
#if defined( __GNUC__) && !defined(__APPLE__) #if defined( __GNUC__) && !defined(__APPLE__)
#ifdef PSYCOPG_DEBUG #ifdef PSYCOPG_DEBUG
#include <sys/types.h>
#include <unistd.h>
#define Dprintf(fmt, args...) \ #define Dprintf(fmt, args...) \
if (!psycopg_debug_enabled) ; else \ if (!psycopg_debug_enabled) ; else \
fprintf(stderr, "[%d] " fmt "\n", (int) getpid() , ## args) fprintf(stderr, "[%d] " fmt "\n", (int) getpid() , ## args)

View File

@ -141,6 +141,9 @@ struct connectionObject {
int isolevel; int isolevel;
int readonly; int readonly;
int deferrable; int deferrable;
/* the pid this connection was created into */
pid_t procpid;
}; };
/* map isolation level values into a numeric const */ /* map isolation level values into a numeric const */

View File

@ -1367,6 +1367,10 @@ connection_setup(connectionObject *self, const char *dsn, long int async)
self->isolevel = ISOLATION_LEVEL_DEFAULT; self->isolevel = ISOLATION_LEVEL_DEFAULT;
self->readonly = STATE_DEFAULT; self->readonly = STATE_DEFAULT;
self->deferrable = STATE_DEFAULT; self->deferrable = STATE_DEFAULT;
#ifdef CONN_CHECK_PID
self->procpid = getpid();
#endif
/* other fields have been zeroed by tp_alloc */ /* other fields have been zeroed by tp_alloc */
pthread_mutex_init(&(self->lock), NULL); pthread_mutex_init(&(self->lock), NULL);
@ -1420,7 +1424,15 @@ connection_dealloc(PyObject* obj)
* resulting in a double-free segfault (ticket #166). */ * resulting in a double-free segfault (ticket #166). */
PyObject_GC_UnTrack(self); PyObject_GC_UnTrack(self);
/* close the connection only if this is the same process it was created
* into, otherwise using multiprocessing we may close the connection
* belonging to another process. */
#ifdef CONN_CHECK_PID
if (self->procpid == getpid())
#endif
{
conn_close(self); conn_close(self);
}
if (self->weakreflist) { if (self->weakreflist) {
PyObject_ClearWeakRefs(obj); PyObject_ClearWeakRefs(obj);

View File

@ -22,14 +22,16 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
# License for more details. # License for more details.
import ctypes
import gc import gc
import os import os
import re import re
import subprocess as sp
import sys import sys
import threading
import time import time
import ctypes
import shutil
import tempfile
import threading
import subprocess as sp
from collections import deque from collections import deque
from operator import attrgetter from operator import attrgetter
from weakref import ref from weakref import ref
@ -360,6 +362,47 @@ class ConnectionTests(ConnectingTestCase):
conn.close() conn.close()
self.assert_(conn.pgconn_ptr is None) self.assert_(conn.pgconn_ptr is None)
@slow
def test_multiprocess_close(self):
dir = tempfile.mkdtemp()
try:
with open(os.path.join(dir, "mptest.py"), 'w') as f:
f.write("""\
import time
import psycopg2
def thread():
conn = psycopg2.connect(%(dsn)r)
curs = conn.cursor()
for i in range(10):
curs.execute("select 1")
time.sleep(0.1)
def process():
time.sleep(0.2)
""" % {'dsn': dsn})
script = ("""\
import sys
sys.path.insert(0, %(dir)r)
import time
import threading
import multiprocessing
import mptest
t = threading.Thread(target=mptest.thread, name='mythread')
t.start()
time.sleep(0.2)
multiprocessing.Process(target=mptest.process, name='myprocess').start()
t.join()
""" % {'dir': dir})
out = sp.check_output(
[sys.executable, '-c', script], stderr=sp.STDOUT)
self.assertEqual(out, b'', out)
finally:
shutil.rmtree(dir, ignore_errors=True)
class ParseDsnTestCase(ConnectingTestCase): class ParseDsnTestCase(ConnectingTestCase):
def test_parse_dsn(self): def test_parse_dsn(self):