Skip to content

Commit 46793a9

Browse files
committed
release lock in pool:put(conn)
The absence of this operation could lead to an infinite bloсking when used with fiber as describerd in the case below. A connection conn is acquired from a pool and we acquire a lock. Then we start the first fiber with pool:put(conn)`, which try to acquire lock and yield. Then the second fiber is started to perform request with `conn:execute()` which try to acquire lock, because the connection is still marked as `usable` and yield too. Before this patch, it's appeared that the lock is never released. Closes #34
1 parent 62e58cf commit 46793a9

File tree

2 files changed

+37
-6
lines changed

2 files changed

+37
-6
lines changed

mysql/init.lua

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,10 @@ end
6666
local function conn_put(conn)
6767
local mysqlconn = conn.conn
6868
ffi.gc(conn.__gc_hook, nil)
69-
if not conn.queue:get() then
70-
conn.usable = false
71-
return POOL_EMPTY_SLOT
72-
end
69+
local result = (conn.queue:get() and mysqlconn) or POOL_EMPTY_SLOT
7370
conn.usable = false
74-
return mysqlconn
71+
conn.queue:put(false)
72+
return result
7573
end
7674

7775
conn_mt = {
@@ -84,6 +82,12 @@ conn_mt = {
8482
self.queue:put(false)
8583
error('Connection is broken')
8684
end
85+
-- Connection could become unusable, so that release
86+
-- lock taken above and throw error.
87+
if not self.usable then
88+
self.queue:put(false)
89+
error('Connection is not usable')
90+
end
8791
local status, datas
8892
if select('#', ...) > 0 then
8993
status, datas = self.conn:execute_prepared(sql, ...)

test/mysql.test.lua

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -516,8 +516,34 @@ local function test_underlying_conn_closed_during_gc(test)
516516
test:ok(ffi.C.fcntl(handle, F_GETFD) == -1, 'descriptor is closed')
517517
end
518518

519+
--- gh-34: Check that fiber is not blocked in the following case.
520+
-- A connection conn is acquired from a pool and we acquire a
521+
-- lock. Then we start the first fiber with pool:put(conn)`, which
522+
-- try to acquire lock and yield. Then the second fiber is started
523+
-- to perform request with `conn:execute()` which try to acquire
524+
-- lock, because the connection is still marked as `usable` and
525+
-- yield too. It's appeared that the lock is never released. Now,
526+
-- it's fixed.
527+
local function test_block_fiber_inf(test, pool)
528+
test:plan(1)
529+
local conn = pool:get()
530+
local fiber_blocked
531+
local clock = require("clock")
532+
local function test_fiber_block_request(conn)
533+
fiber_blocked = fiber.self()
534+
conn:execute('SELECT "a"')
535+
end
536+
conn.queue:get()
537+
fiber.create(pool.put, pool, conn)
538+
local start_time = clock.monotonic()
539+
local timeout = 1
540+
fiber.create(test_fiber_block_request, conn)
541+
conn.queue:put(true)
542+
test:ok(clock.monotonic() - start_time < timeout, 'fiber is not blocked')
543+
end
544+
519545
local test = tap.test('mysql connector')
520-
test:plan(8)
546+
test:plan(9)
521547

522548
test:test('connection old api', test_old_api, conn)
523549
local pool_conn = p:get()
@@ -529,6 +555,7 @@ test:test('connection pool', test_connection_pool, p)
529555
test:test('connection reset', test_connection_reset, p)
530556
test:test('test_underlying_conn_closed_during_gc',
531557
test_underlying_conn_closed_during_gc, p)
558+
test:test('test_block_fiber_inf', test_block_fiber_inf, p)
532559
p:close()
533560

534561
os.exit(test:check() and 0 or 1)

0 commit comments

Comments
 (0)