[odb-users] Strange behavior of odb::details::transfer_ptr

Reilly He reillyhekazusa at gmail.com
Wed Apr 26 23:02:00 EDT 2023


Dear Boris,

Sorry to bother you again, I wrote this to you just to make sure *the usage
of transfer_ptr*.

1.Env
The general environment is:

   - *Compiler*: CXX11
   - *OS*: Android&IOS
   - *Inner DB*: SQLite


2. Background information
Below is the code in our project for database opening. As you can see, we
use our own implementation of connection-pool.

>             //create database under wal
>
>             //create connection pool for database
>
>             auto connectionPoolDelegate = std::make_shared<
> StandardDelegate>();
>
>             //the pool will be access ONLY by ODB database after creation
>
>             std::unique_ptr<odb::sqlite::connection_factory> rwPool(new
> RWConnectionPool(connectionPoolDelegate, config));
>
>             //the transfer pointer is a WEIRD pointer used by ODB
>
>             odb::details::transfer_ptr<odb::sqlite::connection_factory>
> poolInTransP = odb::details::transfer_ptr<odb::sqlite::connection_factory
> >(std::move(rwPool));
>
>             auto db = std::shared_ptr<odb::sqlite::database>(new odb::
> sqlite::database(dbFile,
>
>
>             SQLITE_OPEN_READWRITE
>
>
>             | SQLITE_OPEN_CREATE
>
>
>             | SQLITE_OPEN_FULLMUTEX
>
>
>             | SQLITE_OPEN_PRIVATECACHE,
>
>
>             false,
>
>
>             "",
>
>
>             poolInTransP));
>
>             //the tricky part is you have to release the transfer pointer
> in here
>
>             //otherwise it will cause double release for this connection
> pool
>
>             poolInTransP.transfer();
>
>             return db;
>
I was really confused about transfer_ptr, I reckon this is *very buggy*
because you have to t*ransfer() the ptr everytime* in its scope.

Then reason why I have to do this is after checking the s*ource code
of transfer_ptr*, I realized that it will d*eallocate the inner ptr it was
holding in transfer_ptr`s deallocator*:

>       ~transfer_ptr () {delete p_;}
>
>
>       T*
>
>       transfer ()
>
>       {
>
>         T* r (p_);
>
>         p_ = 0;
>
>         return r;
>
>       }
>

And in the constructor of odb::sqlite::database, *it takes a transfer_ptr
via its copy constructor:*

> database::
> database (const string& name,
>           int flags,
>           bool foreign_keys,
>           const string& vfs,
>           transfer_ptr<connection_factory> factory)
>     : odb::database (id_sqlite),
>       name_ (name),
>       flags_ (flags),
>       foreign_keys_ (foreign_keys),
>       vfs_ (vfs),
>       factory_ (factory.transfer ())
> {
>   if (!factory_)
>     factory_.reset (new connection_pool_factory ());
>
>   factory_->database (*this);
> }
>
> Thus, during this scope, *2 transfer_ptrs existed in memory,* and as a
result, when we leave the current scope. The *real ptr will be deallocated
by the first transfer_ptr* if we do not transfer it explicitly.

3. Question

Is it *better* to take an *r-value* of transfer_ptr in the constructor of
odb::sqlite::database? And I reckon it is even better to just *delete
transfer_ptr`s copy constructor*.

Since odb supports C++ 11, why just allow us to move our connection pool
into the factory_ member via *std::unique_ptr*?

Thanks a lot for the help, really appreciated.

Best Regards,
Reilly He


More information about the odb-users mailing list