@@ -7,6 +7,8 @@ local mysql = require('mysql')
77local json = require (' json' )
88local tap = require (' tap' )
99local fiber = require (' fiber' )
10+ local fio = require (' fio' )
11+ local ffi = require (' ffi' )
1012
1113local host , port , user , password , db = string.match (os.getenv (' MYSQL' ) or ' ' ,
1214 " ([^:]*):([^:]*):([^:]*):([^:]*):([^:]*)" )
@@ -481,8 +483,41 @@ local function test_connection_reset(test, pool)
481483 assert (pool .queue :is_full (), ' test case postcondition fails' )
482484end
483485
486+ local function test_underlying_conn_closed_during_gc (test )
487+ test :plan (1 )
488+ if jit .os ~= ' Linux' then
489+ test :skip (' non-Linux OS' )
490+ return
491+ end
492+ -- Basing on the statement, that file descriptors are recycled
493+ -- in ascending order. It means, that if we call open() and
494+ -- immediately close(), we get the first vacant index for a
495+ -- new resource in the file descriptor table. It is important
496+ -- not to call any procedures between open() and close() that
497+ -- may affect the file descriptor table. After that, we
498+ -- immediately create connection, which creates a socket.
499+ -- Socket is a resource, which occupies the first vacant index
500+ -- in the file descriptor table. We check that the socket is
501+ -- indeed destroyed by using fcntl() for this index (handle).
502+ -- See: https://www.win.tue.nl/~aeb/linux/vfs/trail-2.html
503+ local fh , err = fio .open (' /dev/zero' , {' O_RDONLY' })
504+ if fh == nil then error (err ) end
505+ local handle = fh .fh
506+ fh :close ()
507+ local conn , err = mysql .connect ({ host = host , port = port , user = user ,
508+ password = password , db = db })
509+ if conn == nil then error (err ) end
510+
511+ -- Somehow we lost the connection handle.
512+ conn = nil
513+ collectgarbage ()
514+ ffi .cdef ([[ int fcntl(int fd, int cmd, ...); ]] )
515+ local F_GETFD = 1
516+ test :ok (ffi .C .fcntl (handle , F_GETFD ) == - 1 , ' descriptor is closed' )
517+ end
518+
484519local test = tap .test (' mysql connector' )
485- test :plan (7 )
520+ test :plan (8 )
486521
487522test :test (' connection old api' , test_old_api , conn )
488523local pool_conn = p :get ()
@@ -492,6 +527,8 @@ test:test('concurrent connections', test_conn_concurrent, p)
492527test :test (' int64' , test_mysql_int64 , p )
493528test :test (' connection pool' , test_connection_pool , p )
494529test :test (' connection reset' , test_connection_reset , p )
530+ test :test (' test_underlying_conn_closed_during_gc' ,
531+ test_underlying_conn_closed_during_gc , p )
495532p :close ()
496533
497534os.exit (test :check () and 0 or 1 )
0 commit comments