Project

General

Profile

Actions

Bug #14660

closed
DM RM

Possible FixedSqlConnectionPool deadlock

Bug #14660: Possible FixedSqlConnectionPool deadlock

Added by Dries Mys 2 months ago. Updated 16 days ago.

Status:
Closed
Priority:
Normal
Target version:
Start date:
07/10/2026
Due date:
% Done:

0%

Estimated time:

Description

If multiple connections are returned fast after each other while multiple threads are waiting for a connection, a deadlock may occur due to only notifying another thread when impl_->freeList.size() == 1.

Consider the following example:

std::shared<SqlConnection> connection = ...; // setup sql connection
auto pool = std::make_shared<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), 2);

auto con1 = pool->getConnection();
auto con2 = pool->getConnection();

// all connections of pool are now used.

auto thread1 = std::thread([pool](){
  std::cout << "[thread1] Requesting connection ..." << std::endl;
  pool->getConnection();
  std::cout << "[thread1] Connection obtained" << std::endl;
});

auto thread2 = std::thread([pool](){
  std::cout << "[thread2] Requesting connection ..." << std::endl;
  pool->getConnection();
  std::cout << "[thread2] Connection obtained" << std::endl;
});

// wait for 1 second to ensure both threads are waiting for a connection to become available
std::this_thread::sleep_for(std::chrono::seconds(1));

// release two connections immediately after each other
std::cout << "Returning con1" << std::endl;
pool->returnConnection(std::move(con1)); // will wake a single thread (e.g. thread1)
std::cout << "Returning con2" << std::endl;
pool->returnConnection(std::move(con2)); // will probably not wake another thread (e.g. thread2) as `impl_->freeList.size()` is probably 2 due to thread1 not having taken yet the freed connection.

std::cout << "Waiting for threads to finish" << std::endl;

thread1.join(); 
thread2.join();

std::cout << "OK. No deadlock occured." << std::endl; // probably never reached.

A typical output is:

[thread1] Requesting connection ...
[thread2] Requesting connection ...
Dbo.FixedSqlConnectionPool: no free connections, waiting for connection
Dbo.FixedSqlConnectionPool: no free connections, waiting for connection
Returning con1
Returning con2
Waiting for threads to finish
[thread1] Connection obtained

Eventually, this may result in all request handling threads being busy waiting for an available connection even though all connections are available, effectively deadlocking the full Wt application.

A possible solution is to always call impl_->connectionAvailable.notify_one();, even if impl_->freeList.size() != 1.

As a side note, it may be better to release the lock before calling notify_one (see Notes section of condition_variable::notify_one).

RM Updated by Romain Mardulyn about 1 month ago Actions #1

  • Target version set to 4.14.2

RM Updated by Romain Mardulyn about 1 month ago Actions #2

  • Status changed from New to InProgress
  • Assignee set to Romain Mardulyn

RM Updated by Romain Mardulyn about 1 month ago Actions #3

  • Status changed from InProgress to New
  • Assignee deleted (Romain Mardulyn)

RM Updated by Romain Mardulyn about 1 month ago Actions #4

  • Status changed from New to InProgress
  • Assignee set to Romain Mardulyn

RM Updated by Romain Mardulyn about 1 month ago Actions #5

  • Status changed from InProgress to Review
  • Assignee deleted (Romain Mardulyn)

ED Updated by emil de keyser about 1 month ago Actions #6

  • Assignee set to emil de keyser

ED Updated by emil de keyser about 1 month ago Actions #7

  • Status changed from Review to Resolved
  • Assignee changed from emil de keyser to Romain Mardulyn

RM Updated by Romain Mardulyn about 1 month ago Actions #8

  • Status changed from Resolved to Implemented @Emweb

RM Updated by Romain Mardulyn 16 days ago Actions #9

  • Status changed from Implemented @Emweb to Closed
Actions

Also available in: PDF Atom