persistence library proposal (with prototype implementation)

Is there any interest to include a persistence library in boost, built upon the boost serialization library? the basic idea is: transaction tx; change_objects(); tx.commit(); all changes made by change_objects() should be undone if tx is destroyed without calling commit(), e.g. because of an exception thrown by change_objects(). the library should provide ACID transaction properties, so that multiple transactions (in different threads) can work on the same objects and conflicts are discovered by the library. further, an application crash should be recovered, with the changes made by a transaction either committed as a whole or undone. to partition your object model into many different objects, which can be saved to disk and loaded back on demand, there is a new smart pointer introduced, which behaves like smart_ptr and is convertible to smart_ptr. objects that are handled by this smart pointer and are no longer used in memory are not destroyed but saved to disk. only objects which are no longer in use either in memory or on disk are destroyed. the only requirement on persistent objects would be to model the concept "Serializable" as defined by boost serialization. e.g. a linked list, whose elements are loaded from disk on demand could look like: struct node{ db_ptr<node> next; private: //Serializable requirements }; void traverse(){ while(current) current=current->next; //operator-> loads the next node } prototype implementation --- i've attached a test case using the current implementation. it demonstrates multiple concurrent transactions.running isolated from each other. it models a bank, with multiple threads constantly withdrawing from one (random) bank account and depositing the amount on another. when two threads are using the same bank account at the same time, resulting in money being lost, an isolation_exception is thrown in one of the threads and the transaction can be restarted. there is no manual thread synchronization involved. the current implementation uses an optimistic multiversion approach, meaning that each transaction running concurrently is working on its own copy of an object. when the transaction is committed conflicts are discovered and an isolation_exception is thrown. several things like reference counting on disk and the recovery process after a crash aren't implemented yet, but the basics are in place and should give you an idea of what kind of a library I'm proposing.

Stefan Strasser wrote:
Is there any interest to include a persistence library in boost, built upon the boost serialization library?
[...]
the library should provide ACID transaction properties, so that multiple transactions (in different threads) can work on the same objects and conflicts are discovered by the library. further, an application crash should be recovered, with the changes made by a transaction either committed as a whole or undone.
[...]
I think that I may find this kind of library very useful for a particular kind of applications. Can you please post links to the source code? Maybe you have a web-site you use to host the library?

I'm wondering if a better name might be something like transaction process library or? rather than persistence library. Robert Ramey Ilya Bobir wrote:
Stefan Strasser wrote:
Is there any interest to include a persistence library in boost, built upon the boost serialization library?
[...]
the library should provide ACID transaction properties, so that multiple transactions (in different threads) can work on the same objects and conflicts are discovered by the library. further, an application crash should be recovered, with the changes made by a transaction either committed as a whole or undone.
[...]
I think that I may find this kind of library very useful for a particular kind of applications. Can you please post links to the source code? Maybe you have a web-site you use to host the library? _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

Robert Ramey wrote:
I'm wondering if a better name might be something like
transaction process library
or?
rather than persistence library.
IIUC, MVCC can be used to structure complex multithreaded applications, like Postgres or Oracle (http://en.wikipedia.org/wiki/Multiversion_concurrency_control#Databases_with...). It also seems to me that Clojure uses similar approach as a general way to do multithreading (http://clojure.org/concurrent_programming) also there it is called Software Transactional Memory (http://clojure.org/refs). While other aspects of the library, like automatic loading and unloading of objects, maybe of interest to potential users, the multithreaded aspect seems to be very interesting and may be of great demand as the number of CPU cores per machine is increasing. By the way the Wikipedia article on STM (http://en.wikipedia.org/wiki/Software_transactional_memory) has a reference to a library that already implements STM for C++ and is aiming to become part of Boost: http://eces.colorado.edu/~gottschl/tboostSTM/index.html In any case it would be interesting to look at the library code or at least at the documentation. I was thinking about writing something similar several years ago but came to a conclusion that in order to make a rather efficient implementation the library will need to track all reads and writes on the shared object fields. As C++ does not have reflection the user will have to describe shared objects in a very special manner (like wraping all field types in some library provided wrappers) in order for the library to be able to track all the operations. But you seems to solve this issue somehow. How do you track that a balance field is modified? And how do you know which account objects were modified in a given transaction? And it is interesting to know what level of isolation the library provides or is supposed to provide. IIUC, if it aims at serializable isolation level then you are actually providing an STM solution. But if your isolation level is lower (or is changeable) then your library may have different field of application than an STM providing one. Ilya Bobir

I'm wondering if a better name might be something like
transaction process library
the term "persistence" was taken from the boost.serialization documentation, explaining why boost.serialization was not called boost.persistence: <<I found that persistence is often used to refer to something quite different. Examples are storage of class instances (objects) in database schema [4] This library will be useful in other contexts besides implementing persistence.">> http://www.boost.org/doc/libs/1_36_0/libs/serialization/doc/rationale.html#s... I'm open to name changes of course, but transaction process library misses the "transparent persistence" part. transaction processing and transparent persistence may seem like two very different libraries, but persistence isn't very useful without atomic transactions, especially when you add reference counting to the mix: one application crash and the database never will get garbage collected again.
By the way the Wikipedia article on STM (http://en.wikipedia.org/wiki/Software_transactional_memory) has a reference to a library that already implements STM for C++ and is aiming to become part of Boost:
thanks for the link.. I only took a short look at it so far, here's an example using boostSTM: template <typename T> class native_trans : public transaction_object< native_trans<T> >{ ... }; native_trans<int> global_int; int increment_global() { transaction t; transaction_state state = e_no_state; int val = 0; do { try { t.write(global_int).value()++; val = t.read(global_int).value(); state = t.end_transaction(); } catch (aborted_transaction_exception&) { t.restart_transaction(); } } while (state != e_committed); return val; }
How do you track that a balance field is modified? And how do you know which account objects were modified in a given transaction?
my focus was on providing a user interface as close as possible to conventional memory usage, so I decided against tracking the modification of individual fields (and thus requiring the user to provide such information). changes are tracked on a per-object basis rather than per-field. this is accomplished via the smart pointer I mentioned, db_ptr. it provides almost the same interface as smart_ptr does, plus: class db_ptr{ //explicitely: shared_ptr<T const> read() const; shared_ptr<T> write() const; //implicitely: operator shared_ptr<T const>(); operator shared_ptr<T>() const; shared_ptr<T> operator->() const; ... } . when converting db_ptr to a non-const shared_ptr or using operator->, the intended use (read or write) can not be determined at this point so the library notes that the object may or may not have been modified. on commit, those objects are compared to the original object instance to determine whether the object has been modified (to avoid unnecessary object updates and isolation_exceptions)
And it is interesting to know what level of isolation the library provides or is supposed to provide.
serializable, for successful transactions.
you are actually providing an STM solution
I must admit that I've never heard that term before. I'll have a closer look at BoostSTM, but at first glance it looks like two very different approaches (with similar underlying principles)
Can you please post links to the source code? Maybe you have a web-site you use to host the library?
no website. I can upload it somewhere or mail it to someone with access to a repository, e.g. boost sandbox? as complex as this might sound, the whole thing currently is only about 1500 lines of code. with very poor performance, but that's mostly due to the current storage backend, using one file for each object, so you end up with thousands of files, a few bytes each.

Hi, happy to hear people is concerned with transactions. Bugzilla from strasser@uni-bremen.de wrote:
I'm wondering if a better name might be something like
transaction process library
the term "persistence" was taken from the boost.serialization documentation, explaining why boost.serialization was not called boost.persistence:
<> http://www.boost.org/doc/libs/1_36_0/libs/serialization/doc/rationale.html#s...
I'm open to name changes of course, but transaction process library misses the "transparent persistence" part.
transaction processing and transparent persistence may seem like two very different libraries, but persistence isn't very useful without atomic transactions, especially when you add reference counting to the mix: one application crash and the database never will get garbage collected again.
Please could you resume which are the main goals of the library? Bugzilla from strasser@uni-bremen.de wrote:
By the way the Wikipedia article on STM (http://en.wikipedia.org/wiki/Software_transactional_memory) has a reference to a library that already implements STM for C++ and is aiming to become part of Boost:
thanks for the link.. I only took a short look at it so far, here's an example using boostSTM:
template <typename T> class native_trans : public transaction_object< native_trans<T> >{ ... }; native_trans<int> global_int; int increment_global() { transaction t; transaction_state state = e_no_state; int val = 0; do { try { t.write(global_int).value()++; val = t.read(global_int).value(); state = t.end_transaction(); } catch (aborted_transaction_exception&) { t.restart_transaction(); } } while (state != e_committed); return val; }
The current version include as well smart pointers, read pointers rd_ptr<T> and wr_ptr<<T>. Here it is how we can do this now: native_trans<int> global_int; int increment_global() { atomic(t) { wr_ptr<int> tx_global_int(global_int,t); (*tx_global_int)++; return *tx_global_int; } } we are working on allowing the global_int to be declared just as an int, i.e. no need to inherit from a transactional object to participate in a transaction. Other improvements will concern the definition of any operator and the implicit conversion so we can write tx_global_int++; return tx_global_int; how this is written with your library? Bugzilla from strasser@uni-bremen.de wrote:
How do you track that a balance field is modified? And how do you know which account objects were modified in a given transaction?
my focus was on providing a user interface as close as possible to conventional memory usage, so I decided against tracking the modification of individual fields (and thus requiring the user to provide such information). changes are tracked on a per-object basis rather than per-field.
this is accomplished via the smart pointer I mentioned, db_ptr. it provides almost the same interface as smart_ptr does, plus:
class db_ptr{ //explicitely: shared_ptr<T const> read() const; shared_ptr<T> write() const;
//implicitely: operator shared_ptr<T const>(); operator shared_ptr<T>() const; shared_ptr<T> operator->() const; ... } .
when converting db_ptr to a non-const shared_ptr or using operator->, the intended use (read or write) can not be determined at this point so the library notes that the object may or may not have been modified. on commit, those objects are compared to the original object instance to determine whether the object has been modified (to avoid unnecessary object updates and isolation_exceptions)
TBoost.STM use different pointers to track this. * rd_ptr allows only to read the value * wr_ptr allows to read/write the value we are adding a upgrd_ptr that allows to read the value and also to be upgraded to a writable pointer. So, we know some memory can be written by a transaction when a wr_ptr is initialized. Bugzilla from strasser@uni-bremen.de wrote:
And it is interesting to know what level of isolation the library provides or is supposed to provide.
serializable, for successful transactions.
you are actually providing an STM solution
I must admit that I've never heard that term before. I'll have a closer look at BoostSTM, but at first glance it looks like two very different approaches (with similar underlying principles)
Can you please post links to the source code? Maybe you have a web-site you use to host the library?
no website. I can upload it somewhere or mail it to someone with access to a repository, e.g. boost sandbox? as complex as this might sound, the whole thing currently is only about 1500 lines of code.
I'm interested. Could you add the code to the vault? Bugzilla from strasser@uni-bremen.de wrote:
with very poor performance, but that's mostly due to the current storage backend, using one file for each object, so you end up with thousands of files, a few bytes each.
Why do you need to use files to track the memory changes, why don't just memory? Bugzilla from strasser@uni-bremen.de wrote:
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Best, Vicente -- View this message in context: http://www.nabble.com/persistence-library-proposal-%28with-prototype-impleme... Sent from the Boost - Dev mailing list archive at Nabble.com.

Am Monday 10 August 2009 10:44:42 schrieb Vicente Botet Escriba:
Please could you resume which are the main goals of the library?
the main goal is to be able to extend the lifetime of an object, simply by using another smart pointer: int main(){ shared_ptr<A> shared(new A); db_ptr<A> db(new A); ... } // "shared" is destroyed, "db" is saved to disk (given that it is referenced somewhere) I should have explained this more clearly in my first mail, before going into transaction details, but atomic transactions are an essential part of this kind of transparent persistence, because you don't want to end up with corrupted objects on disk.
Why do you need to use files to track the memory changes, why don't just memory?
the library is supposed to recover the database from application crashes. to accomplish this, object instances and a transaction log must be saved to disk, even if all objects in use are cached in memory. see e.g. http://en.wikipedia.org/wiki/Redo_log
we are working on allowing the global_int to be declared just as an int, i.e. no need to inherit from a transactional object to participate in a transaction. Other improvements will concern the definition of any operator and the implicit conversion so we can write
tx_global_int++; return tx_global_int;
how do you track changes of tx_global_int without any wrapper? (or did you refer to two different solutions, a) declaring as an int, or b) defining operators for an int-wrapper?)
how this is written with your library?
it doesn't really apply because once a db_ptr is dereferenced the transaction is working on a local copy of the object: struct A{ int mem; void do_something(){ ++mem; } }; main(){ db_ptr<A> a; a->do_something(); ++a->mem; } only if read-only access is requested the original object is passed to the user, e.g.: void do_something(shared<A const> a); main(){ db_ptr<A> a; do_something(a); cerr << a.read()->mem; } what is the intended use of native_trans/transaction_object in your library? is it: struct A{ native_trans<int> mem1; native_trans<int> mem2; }; A a; or is it: struct A : transaction_object{ int mem1; int mem2; }; A a; ?
I'm interested.
me too, do have an example which shows the final intended use of the library? I've looked over some of the examples but they seem to be test cases with a lot of library internals. the example I quoted was from one of the papers on your website.
Could you add the code to the vault?
I didn't know about the boost vault, here it is: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=persistence.zip&directory=&

Bugzilla from strasser@uni-bremen.de wrote:
Am Monday 10 August 2009 10:44:42 schrieb Vicente Botet Escriba:
Please could you resume which are the main goals of the library?
the main goal is to be able to extend the lifetime of an object, simply by using another smart pointer:
int main(){ shared_ptr shared(new A); db_ptr db(new A); ... } // "shared" is destroyed, "db" is saved to disk (given that it is referenced somewhere)
I should have explained this more clearly in my first mail, before going into transaction details, but atomic transactions are an essential part of this kind of transparent persistence, because you don't want to end up with corrupted objects on disk.
I have some questions: When the serializable object is saved/loaded to/from the disk? Can we have persistent object without transactions on your library? Does this depends on whether the smart pointer is created on a transactional context?
Why do you need to use files to track the memory changes, why don't just memory?
the library is supposed to recover the database from application crashes. to accomplish this, object instances and a transaction log must be saved to disk, even if all objects in use are cached in memory. see e.g. http://en.wikipedia.org/wiki/Redo_log
Ok, I see that the files are not an implementation detail, and that your are really looking for persistence. I would like a transactional system that is able to manage with object in memory and object in a persistent storage. I need to think much more on that.
we are working on allowing the global_int to be declared just as an int, i.e. no need to inherit from a transactional object to participate in a transaction. Other improvements will concern the definition of any operator and the implicit conversion so we can write
tx_global_int++; return tx_global_int;
how do you track changes of tx_global_int without any wrapper? (or did you refer to two different solutions, a) declaring as an int, or b) defining operators for an int-wrapper?)
the wrapper of global_int is tx_global_int. The question is if global_int must inherit from transactional_object or not. Up to now it works only for objects inheriting from transactional_object (which is the more efficient), but we are working on removing this constraint (which will be less efficient because the transactional information need to be looked up from a cache map).
how this is written with your library?
it doesn't really apply because once a db_ptr is dereferenced the transaction is working on a local copy of the object:
struct A{ int mem; void do_something(){ ++mem; } }; main(){ db_ptr a; a->do_something(); ++a->mem; }
only if read-only access is requested the original object is passed to the user, e.g.:
void do_something(shared a); main(){ db_ptr a; do_something(a); cerr << a.read()->mem; }
what is the intended use of native_trans/transaction_object in your library? is it: struct A{ native_trans<int> mem1; native_trans<int> mem2; }; A a;
or is it:
struct A : transaction_object{ int mem1; int mem2; }; A a;
?
It is up to you to decide what is a transactional object. native_trans<int> is just a transactional object storing a int and defining the arithmetic operations. Your first example define a non transactional object containing two transactional objects. The second defines a transactional object with two fields.
I'm interested.
me too, do have an example which shows the final intended use of the library? I've looked over some of the examples but they seem to be test cases with a lot of library internals. the example I quoted was from one of the papers on your website.
TBoost.STM intends to implement a lock based SoftwareTransactionalMemory so there is not need at the user level to use complicated synchronization mechanism when different threads access some shared data (note that STM do not support the Durable part of ACID). The user needs to protect whatever needs to be atomic with atomic {} and use STM smart pointers to access the transactional data.
Could you add the code to the vault?
I didn't know about the boost vault, here it is: http://www.boostpro.com/vault/index.php?action=downloadfile&filename=persistence.zip&directory=& _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Thanks, Vicente -- View this message in context: http://www.nabble.com/persistence-library-proposal-%28with-prototype-impleme... Sent from the Boost - Dev mailing list archive at Nabble.com.

Stefan Strasser wrote:
[...]
with very poor performance, but that's mostly due to the current storage backend, using one file for each object, so you end up with thousands of files, a few bytes each.
I think it is OK for a prototype to be slow. Postgres uses a hierarchy of caches for all the data stored it tables. And when it does read from the disk it reads a "page" that may contain data that belongs to another tuples in addition to the data for the target tuple. Other databases probably do something similar. The data read from the disk is not transformed unless some logic requires value of the exact attribute of a tuple. To get an attribute value you have to ask the cache subsystem for the attribute. During this call the value may be transformed from the on disk representation or may be just found in the cache. I know that writes are also done in pages. But I do not know the exact details. What I'm trying to say is that the performance can be considerably improved without changing the user interface but a fast implementation will probably require a lot of effort. Thought unless you track individual fields you will have to load objects as a whole. And you will probably have to write your own serialization layer as the boost::serialization library will not allow you to do this kind of optimizations. Ilya Bobir

Am Tuesday 11 August 2009 01:03:32 schrieb Ilya Bobir:
Thought unless you track individual fields you will have to load objects as a whole. And you will probably have to write your own serialization layer as the boost::serialization library will not allow you to do this kind of optimizations.
I don't see a problem here. I implemented a similar (more complex) library for .NET, and there the limiting factor was harddrive speed. obviously, with reflection and a just-in-time-compiler you can generate serialization code at runtime which improves performance, but I certainly wouldn't want to ditch boost.serialization for a little better performance. runtime code generation isn't practical in C++ anyway. Am Tuesday 11 August 2009 11:05:28 schrieb Vicente Botet Escriba:
I have some questions: When the serializable object is saved/loaded to/from the disk?
that's an implementation detail, an object can be saved and removed from memory at any time, as soon as the library user doesn't use it anymore(i.e. holding a shared_ptr to it) the prototype currently uses boost::weak_ptr to refer to an object and saves on object destruction, with a very simlpe caching mechanism in place to avoid objects constantly being loaded and saved. see cache.hpp
Can we have persistent object without transactions on your library?
objects can be read outside of a transaction. the prototype doesn't allow writing outside of transactions. although that would be possible, it requires a lot of effort (esp. with regard to reference counting) and I don't see the benefit. there is no limit to the size of a transaction, so you could begin a transaction at application startup and commit at shutdown, if you don't want to bother with transactions.
Ok, I see that the files are not an implementation detail, and that your are really looking for persistence.
the way I see it now is that the libraries have 2 very different goals, with transactional memory being a requirement for reaching the goal persistence. so once BoostSTM is part of boost, the transactional part of persistence could be reimplemented to use BoostSTM and possibly improve performance over the current object cloning mechanism used. do you agree with that?
the wrapper of global_int is tx_global_int. The question is if global_int must inherit from transactional_object or not. Up to now it works only for objects inheriting from transactional_object (which is the more efficient), but we are working on removing this constraint (which will be less efficient because the transactional information need to be looked up from a cache map).
I took a short look at transactional_object. 1. it seems it's there to store some object-specific information. why don't you wrap that around the object instead of deriving from it? that wouldn't hide it completely from the user, but at least it would be non-intrusive to user types, without affecting performance. e.g. template<class T> struct transaction_object{ T user_object; //object-specific info }; //user-space: transaction_object<A> a; and your smart pointers would "dereference" the transaction_objcet to T. or: template<class T> struct transaction_object : T{ //object-specific-info }; I use the first approach, see object_instance<> in object.hpp. 2. I noticed that in transaction_object, you call the user-supplied operator= of T, accomplishing only a shallow copy of the object. does that mean that: struct A : transaction_object<A>{ B *b; }; A a; write(a)->b->member++; //non-transactional memory?

Bugzilla from strasser@uni-bremen.de wrote:
Am Tuesday 11 August 2009 11:05:28 schrieb Vicente Botet Escriba:
I have some questions: When the serializable object is saved/loaded to/from the disk?
that's an implementation detail, an object can be saved and removed from memory at any time, as soon as the library user doesn't use it anymore(i.e. holding a shared_ptr to it) the prototype currently uses boost::weak_ptr to refer to an object and saves on object destruction, with a very simlpe caching mechanism in place to avoid objects constantly being loaded and saved. see cache.hpp
Can we have persistent object without transactions on your library?
objects can be read outside of a transaction. the prototype doesn't allow writing outside of transactions. although that would be possible, it requires a lot of effort (esp. with regard to reference counting) and I don't see the benefit. there is no limit to the size of a transaction, so you could begin a transaction at application startup and commit at shutdown, if you don't want to bother with transactions.
Does this means that a persistent object is writen to disk only when a db_ptr is reachable while commiting a transaction?
Ok, I see that the files are not an implementation detail, and that your are really looking for persistence.
the way I see it now is that the libraries have 2 very different goals, with transactional memory being a requirement for reaching the goal persistence. so once BoostSTM is part of boost, the transactional part of persistence could be reimplemented to use BoostSTM and possibly improve performance over the current object cloning mechanism used. do you agree with that?
BoostSTM has not made with persistence in mind. I think we need some deep redesign (separating what is related to the transaction and what is related to the storage, either persistent or not) to allow this.
the wrapper of global_int is tx_global_int. The question is if global_int must inherit from transactional_object or not. Up to now it works only for objects inheriting from transactional_object (which is the more efficient), but we are working on removing this constraint (which will be less efficient because the transactional information need to be looked up from a cache map).
I took a short look at transactional_object. 1. it seems it's there to store some object-specific information. why don't you wrap that around the object instead of deriving from it? that wouldn't hide it completely from the user, but at least it would be non-intrusive to user types, without affecting performance.
e.g. template<class T> struct transaction_object{ T user_object; //object-specific info }; //user-space: transaction_object a;
and your smart pointers would "dereference" the transaction_objcet to T.
or: template<class T> struct transaction_object : T{ //object-specific-info };
I use the first approach, see object_instance<> in object.hpp.
The wrapper has the drawback that doesn't define the same interface as the type T. So when the smart pointer is dereferenced the result will not provides these interface. native_trans<T> wraps already any arithmetic builting type, but the interface is fixed and known.
2. I noticed that in transaction_object, you call the user-supplied operator= of T, accomplishing only a shallow copy of the object. does that mean that:
struct A : transaction_object { B *b; };
A a;
write(a)->b->member++; //non-transactional memory?
Whether member is transactional or not depend on whether B inherits from transactional_object < B > or not. Anyway you are right, you need to access the member b using a transactional smart pointer wr_ptr< B > tx_a_b(a.b, tx); tx_a_b->member++; Now the update of member will be taken in account by the transaction tx. Best, Vicente -- View this message in context: http://www.nabble.com/persistence-library-proposal-%28with-prototype-impleme... Sent from the Boost - Dev mailing list archive at Nabble.com.

Am Wednesday 12 August 2009 11:18:26 schrieb Vicente Botet Escriba:
Does this means that a persistent object is writen to disk only when a db_ptr is reachable while commiting a transaction?
yes. an object only survives a transaction if it is reachable from the database root. see the banking example. there the bank is the database root, and it hold's db_ptr's to the individual accounts, so the accounts are reachable and saved to disk. just like you'd expect from a boost::shared_ptr.
The wrapper has the drawback that doesn't define the same interface as the type T. So when the smart pointer is dereferenced the result will not provides these interface.
why not? the smart pointer would either return a int & or an int const &, depending on read or write access.
2. I noticed that in transaction_object, you call the user-supplied operator= of T, accomplishing only a shallow copy of the object. does that mean that:
struct A : transaction_object { B *b; };
A a;
write(a)->b->member++; //non-transactional memory?
Whether member is transactional or not depend on whether B inherits from transactional_object < B > or not. Anyway you are right, you need to access the member b using a transactional smart pointer
wr_ptr< B > tx_a_b(a.b, tx); tx_a_b->member++;
Now the update of member will be taken in account by the transaction tx.
I'd reconsider that. struct A{ void do(){ a=1; } int a; }; struct B{ void do(){ *a=1; } //error int *a; } A and B imho should behave in the same way when used as a transaction object. If you disagree with that, or if that's impractical for some reason, there is really no point in removing the requirement to derive from transaction_object. the implementor of the type must be very aware that he's implementing a transaction object anyway, so I'd consider it not a bug but a feature, that he must express this intention explicitely by deriving from transaction_object.

Bugzilla from strasser@uni-bremen.de wrote:
Am Wednesday 12 August 2009 11:18:26 schrieb Vicente Botet Escriba:
Does this means that a persistent object is writen to disk only when a db_ptr is reachable while commiting a transaction?
yes. an object only survives a transaction if it is reachable from the database root. see the banking example. there the bank is the database root, and it hold's db_ptr's to the individual accounts, so the accounts are reachable and saved to disk. just like you'd expect from a boost::shared_ptr.
The wrapper has the drawback that doesn't define the same interface as the type T. So when the smart pointer is dereferenced the result will not provides these interface.
why not? the smart pointer would either return a int & or an int const &, depending on read or write access.
Again, you are right, the smart pointer can return a pointer to the instance T wrapped.
2. I noticed that in transaction_object, you call the user-supplied operator= of T, accomplishing only a shallow copy of the object. does that mean that:
struct A : transaction_object { B *b; };
A a;
write(a)->b->member++; //non-transactional memory?
Whether member is transactional or not depend on whether B inherits from transactional_object < B > or not. Anyway you are right, you need to access the member b using a transactional smart pointer
wr_ptr< B > tx_a_b(a.b, tx); tx_a_b->member++;
Now the update of member will be taken in account by the transaction tx.
I'd reconsider that.
struct A{ void do(){ a=1; } int a; }; struct B{ void do(){ *a=1; } //error int *a; }
Neither A nor B are errors. In both cases the modification is done on a non-transactional bases, even if done on the context of a transaction. The STM system has no means to know whether an access to a given variable is modifying it or not.
A and B imho should behave in the same way when used as a transaction object.
We need to say to the STM system that some data participates on a transaction. The question is if we require that a type must be able to be used on a non transactional context and on a transactional context. In this case the type should not Note that transactional objects can not be embedded as the BoostSTM is object based and the backup will overlap. The code we could write could be something like struct B{ void do(){ *a=1; } tx_ptr<int> a; }; and define the operator* on tx_ptr as including the object on the transactional cache and returning the reference to this catch. This has syntactical advantages, but performance disadvantages. I will explore a little more this possibility.
If you disagree with that, or if that's impractical for some reason, there is really no point in removing the requirement to derive from transaction_object. the implementor of the type must be very aware that he's implementing a transaction object anyway, so I'd consider it not a bug but a feature, that he must express this intention explicitely by deriving from transaction_object. _______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Best, Vicente -- View this message in context: http://www.nabble.com/persistence-library-proposal-%28with-prototype-impleme... Sent from the Boost - Dev mailing list archive at Nabble.com.
participants (4)
-
Ilya Bobir
-
Robert Ramey
-
Stefan Strasser
-
Vicente Botet Escriba