Discussion:
[Wt-interest] Question about Wt::Dbo example code -- Wt::Dbo::ptr owenrship and exception safety
K. Frank
2015-08-03 19:40:59 UTC
Permalink
Hello List!

Another question, again looking at the Wt::Dbo tutorial:

http://www.webtoolkit.eu/wt/doc/tutorial/dbo.html

and, specifically, the example code:

...
/*
* A unit of work happens always within a transaction.
*/
dbo::Transaction transaction(session);

User *user = new User();
user->name = "Joe";
user->password = "Secret";
user->role = User::Visitor;
user->karma = 13;

dbo::ptr<User> userPtr = session.add(user);
}

I would like to check my understanding of Wt::Dbo::ptr.

When I say "ownership" I mean, primarily, the responsibility for
memory management, in particular, calling delete.

As I understand it it, when the local variable "userPtr" goes out
of scope (at the closing brace at the end of the example code)
delete will be called on "user" (of type User*). Is this correct?
(I understand that Wt::Dbo::ptr is a shared pointer, so that delete
will be called when the last of multiple shared pointers is destroyed,
but in this case userPtr is the only such pointer.)

So, in my language, userPtr (together with any other shared pointers
pointing to the same object) "owns" the instance of class User pointed
to by the variable "user". Is this correct?

So, in particular, something like:

{
User user;
user-.name = "Joe";
// ...
dbo::ptr<User> userPtr = session.add(&user);
}

would NOT be the right way to do this because when userPtr
goes out of scope, it would try to delete &user, (a pointer to)
an object that was not created by a call to new. (And furthermore
user's destructor would be called twice -- once by the incorrect
call to delete, and then again when user went out of scope.)
Is this correct?

I have a question about exception safety: What if the call to
session.add() throws? My understanding is that userPtr will
not yet have been assigned, and will therefore not be able to
delete (the object. Who then would have responsibility for calling
'delete user;"?

Is this issue just a simplification in the sample code and the (only?)
exception-safe way to do this would be:

{
User *user = new User();

// this stuff can't throw -- true?
user->name = "Joe";
// ...
dbo::ptr<User> userPtr((user);

// this could throw, but userPtr ensures that user will be deleted
session.add(userPtr);
}

Is there ever any fully exception-safe use of the original version?

dbo::ptr<User> userPtr = session.add(&user);


Thanks for any clarification.


K. Frank

------------------------------------------------------------------------------
Aaron Laws
2015-08-03 20:30:11 UTC
Permalink
Post by K. Frank
Hello List!
http://www.webtoolkit.eu/wt/doc/tutorial/dbo.html
...
/*
* A unit of work happens always within a transaction.
*/
dbo::Transaction transaction(session);
User *user = new User();
user->name = "Joe";
user->password = "Secret";
user->role = User::Visitor;
user->karma = 13;
dbo::ptr<User> userPtr = session.add(user);
}
I would like to check my understanding of Wt::Dbo::ptr.
When I say "ownership" I mean, primarily, the responsibility for
memory management, in particular, calling delete.
As I understand it it, when the local variable "userPtr" goes out
of scope (at the closing brace at the end of the example code)
delete will be called on "user" (of type User*). Is this correct?
(I understand that Wt::Dbo::ptr is a shared pointer, so that delete
will be called when the last of multiple shared pointers is destroyed,
but in this case userPtr is the only such pointer.)
So, in my language, userPtr (together with any other shared pointers
pointing to the same object) "owns" the instance of class User pointed
to by the variable "user". Is this correct?
That matches my experience, the documentation, convention, and shared
pointer semantics :-)
Post by K. Frank
{
User user;
user-.name = "Joe";
// ...
dbo::ptr<User> userPtr = session.add(&user);
}
would NOT be the right way to do this because when userPtr
goes out of scope, it would try to delete &user, (a pointer to)
an object that was not created by a call to new. (And furthermore
user's destructor would be called twice -- once by the incorrect
call to delete, and then again when user went out of scope.)
Is this correct?
Yup.
Post by K. Frank
I have a question about exception safety: What if the call to
session.add() throws? My understanding is that userPtr will
not yet have been assigned, and will therefore not be able to
delete (the object. Who then would have responsibility for calling
'delete user;"?
Is this issue just a simplification in the sample code and the (only?)
{
User *user = new User();
// this stuff can't throw -- true?
user->name = "Joe";
// ...
dbo::ptr<User> userPtr((user);
// this could throw, but userPtr ensures that user will be deleted
session.add(userPtr);
}
Is there ever any fully exception-safe use of the original version?
dbo::ptr<User> userPtr = session.add(&user);
Excellent question. Based on the current source, it should be okay to pass
a pointer to a business object even in an exceptional situation. The source
is:

228 template <class C>
229 ptr<C> Session::add(C *obj)
230 {
231 ptr<C> result(obj);
232 return add(result);
233 }

If Wt::Dbo::Session::add(Wt::Dbo::ptr<C>&) throws, the original object will
be deleted. Of course, I can't think of many ways that session.add(&user);
could be used, excepting something like

User * ptr_user = new User();
User & user {*ptr_user};
session.add(&user);
/*We don't own ptr_user anymore, so don't delete it!*/

Happy coding!

In Christ,
Aaron Laws
K. Frank
2015-08-04 01:39:23 UTC
Permalink
Hi Aaron!

Thanks again.
Post by K. Frank
Hello List!
http://www.webtoolkit.eu/wt/doc/tutorial/dbo.html
...
...
Post by K. Frank
I have a question about exception safety: What if the call to
session.add() throws? My understanding is that userPtr will
not yet have been assigned, and will therefore not be able to
delete (the object. Who then would have responsibility for calling
'delete user;"?
Is this issue just a simplification in the sample code and the (only?)
{
User *user = new User();
// this stuff can't throw -- true?
user->name = "Joe";
// ...
dbo::ptr<User> userPtr((user);
// this could throw, but userPtr ensures that user will be deleted
session.add(userPtr);
}
Is there ever any fully exception-safe use of the original version?
dbo::ptr<User> userPtr = session.add(&user);
(Oops ... Typo on my part. I meant to write "session.add(user)", where
user is of type User*.)
Excellent question. Based on the current source, it should be okay to pass a
pointer to a business object even in an exceptional situation. The source
228 template <class C>
229 ptr<C> Session::add(C *obj)
230 {
231 ptr<C> result(obj);
232 return add(result);
233 }
If Wt::Dbo::Session::add(Wt::Dbo::ptr<C>&) throws, the original object will
be deleted.
Ah, okay. The overload that takes a raw pointer, Session::add (C*), creates
a Wt::Dbo::ptr smart pointer owning the C* before it does anything that might
throw, so were safe.

Or to say it another way, both:

Wt::Dbo::ptr<User> userPtr (user);

and:

Wt::Dbo::Session::add (user);

take ownership of (the object pointed to by the raw pointer) user, and become
responsible for deleting user, even if an exception is thrown.
Of course, I can't think of many ways that session.add(&user);
could be used, excepting something like
(Yes, of course. The use of "&user" in this case was a typo on my part.)
User * ptr_user = new User();
User & user {*ptr_user};
session.add(&user);
/*We don't own ptr_user anymore, so don't delete it!*/
Happy coding!
In Christ,
Aaron Laws
Best regards.


K. Frank

------------------------------------------------------------------------------
Loading...