[odb-users] Question: Multithreading with odb::msql on Centos 7

Jean Guegant jean.guegant at qliro.com
Wed Dec 16 12:26:17 EST 2015


Hi,

According to my understanding of the documentation, odb::database and 
hence odb::mssql::database are thread-safe objects.
We can share one instance of a a odb::mssql::database object (either a 
global variable, or an allocated instance on the heap carefully 
encapsulated by a shared_pointer) within multiple threads. A transaction 
on a thread will use a connection from a pool, and if none are available 
the call to db_->begin() will wait until a connection is free.

Yet it looks like I can't do two transactions from the same db objects 
on different threads, I end-up with the following stack trace:

#0  0x0000000000000000 in ?? ()
#1  0x00007ffff6fc0f33 in __connect_part_two 
(connection=connection at entry=0x7fffe4000a10) at SQLConnect.c:1891
#2  0x00007ffff6fc5d66 in SQLDriverConnect (hdbc=0x7fffe4000a10, 
hwnd=<optimized out>, conn_str_in=<optimized out>, 
len_conn_str_in=<optimized out>, conn_str_out=<optimized out>, 
conn_str_out_max=<optimized out>, ptr_conn_str_out=0x7ffff137d058, 
driver_completion=0) at SQLDriverConnect.c:1530
#3  0x00007ffff7231b2a in odb::mssql::connection::connection 
(this=0x7fffe40008f0, db=...) at connection.cxx:120
#4  0x00007ffff72321c9 in 
odb::mssql::connection_pool_factory::pooled_connection::pooled_connection (this=0x7fffe40008f0, 
db=...) at connection-factory.cxx:153
#5  0x00007ffff7232234 in odb::mssql::connection_pool_factory::create 
(this=0x69bd50) at connection-factory.cxx:50
#6  0x00007ffff723204c in odb::mssql::connection_pool_factory::connect 
(this=0x69bd50) at connection-factory.cxx:91
#7  0x00007ffff7233023 in odb::mssql::database::connection_ 
(this=<optimized out>) at database.cxx:488
#8  0x00007ffff724341a in connection (this=<optimized out>) at 
../../odb/mssql/database.ixx:18
#9  odb::mssql::transaction_impl::start (this=0x7fffe40008c0) at 
transaction-impl.cxx:41
#10 0x00007ffff746e312 in odb::transaction::reset (this=0x7ffff137d230, 
impl=0x7fffe40008c0, make_current=<optimized out>) at transaction.cxx:42
#11 0x0000000000459125 in odb::transaction::transaction 
(this=0x7ffff137d230, impl=0x7fffe40008c0, make_current=true) at 
/usr/local/include/odb/transaction.ixx:25
#12 0x000000000045e529 in Crasher::run (this=0x7ffff137fd30) at 
/home/jeangu/Coding/braveheart/src/test/cpp/bravecrash/main.cpp:61
#13 0x0000000000457554 in startCrasher (db=std::shared_ptr (count 5, 
weak 0) 0x69b9b0) at 
/home/jeangu/Coding/braveheart/src/test/cpp/bravecrash/main.cpp:96
#14 0x0000000000477dff in std::_Bind_simple<void 
(*(std::shared_ptr<odb::mssql::database>))(std::shared_ptr<odb::mssql::database>)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) 
(this=0x698320) at /usr/include/c++/4.8.2/functional:1732
#15 0x0000000000477b91 in std::_Bind_simple<void 
(*(std::shared_ptr<odb::mssql::database>))(std::shared_ptr<odb::mssql::database>)>::operator()() 
(this=0x698320) at /usr/include/c++/4.8.2/functional:1720
#16 0x00000000004777c6 in std::thread::_Impl<std::_Bind_simple<void 
(*(std::shared_ptr<odb::mssql::database>))(std::shared_ptr<odb::mssql::database>)> 
 >::_M_run() (this=0x698308) at /usr/include/c++/4.8.2/thread:115
#17 0x00007ffff62f6220 in ?? () from /lib64/libstdc++.so.6
#18 0x00007ffff7bc6dc5 in start_thread () from /lib64/libpthread.so.0
#19 0x00007ffff5a5e21d in clone () from /lib64/libc.so.6

Using the following (slightly stripped) code:

======================================
// variablevaluemodel.h

#include <odb/core.hxx>
#include <odb/query.hxx>


#pragma db view query("EXEC astored_procedure (?)")
struct variable_value {
     std::string variable;
     std::string value;
};

// main.cpp
#include <atomic>
#include <thread>

#include <odb/mssql/database.hxx>
#include <variablevaluemodel-odb.hxx>

typedef odb::query<variable_value> query;
typedef odb::result<variable_value> result;
typedef std::shared_ptr<odb::mssql::database> shared_database;

class Crasher
{
public:
     Crasher(shared_database db) :
             db_(db)
     {

     }

     void run()
     {
         while (true) {

             odb::transaction t(db_->begin());

             result r(db_->query<variable_value>(query::_val("A 
parameter"));

             t.commit();
         }
     }

private:
     shared_database db_;
};

void startCrasher(shared_database db)
{
     Crasher c(db);
     c.run();
}

int main(int argc, char* argv[])
{

     auto db = shared_database(new odb::mssql::database("", "", 
"YourDB", "YourHost\\YourInstance",  1433));

     int nThread= std::thread::hardware_concurrency();

     std::vector<std::unique_ptr<std::thread>> workers;

     for (int i = 0; i < 2; ++i)
     {
         auto worker = std::unique_ptr<std::thread>(new 
std::thread(startCrasher, db));
         workers.push_back(std::move(worker));
     }

     for (auto& worker: workers) {
         worker->join();
     }

     return EXIT_SUCCESS;
}
======================================

My specs are the following:
Kernel:
Linux marsupilami 3.10.0-327.3.1.el7.x86_64 #1 SMP Wed Dec 9 14:09:15 
UTC 2015 x86_64 x86_64 x86_64 GNU/Linux

Compiler:
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-4)

Libs:
libodb-2.4.0
libodb-mssql-2.4.0
unixODBC-2.3.0
msodbcsql-11.0.2270.0

Same goes if I use a global database object.

Is it actually possible to do two transaction on the same db objects 
from different threads? Could the usage of a stored procedure be the 
problem? Is there any limitation for the mssql library only?

If I use a different database object per thread targeting the same 
database and doing the same transaction, could it induce any 
race-condition somewhere in the database or in the drivers, or in odb? 
Any performance impact?

Any help would be really helpful!

Sincerely,
Jean Guegant


More information about the odb-users mailing list