shared_ptr thr-safety: confusions %%take

At least 2 interviewers pointed out thread safety issues …

http://stackoverflow.com/questions/14482830/stdshared-ptr-thread-safety first answer shows many correct MT usages and incorrect MT usages. Looking at that answer, I see at least 3 distinct”objects” that could be shared mutable:

  1. global_instance variable —  a shared mutable object. race condition 😦
  2. control block — shared by different club members. Any one club member, like global_instance could be a shared mutable object. Concurrent access the control block is managed by the shared_ptr implementation
  3. pointee on heap — is shared  mutable. If 2 threads call mutator methods on this object, you can hit race condition.

 

 

 

unique_ptr and move()

When I use a unique_ptr instance, I frequently copy it around. Unique_ptr ‘s copying is actual moving.

The are different ways to code it.

  • sometimes you need to use someFunc(move(myUniquePtr));
  • sometimes you can omit move() and the semantics remain the same.

http://stackoverflow.com/questions/9827183/why-am-i-allowed-to-copy-unique-ptr has some examples. Note none of the functions have a parameter/return type showing “&&”. That’s because there is pbclone in play. The copying uses the move-constructor, which does have a && parameter.

I think some developers simply copy sample working code, without understanding why, like an ape. Some use threading constructs the same way. Nothing shame. I feel interviewers are interested in your understanding.

unique^shared^auto_ptr

unique_ptr shared_ptr auto_ptr scoped_ptr
container can put into container forbidden illegal
instance field yes [1] yes ? Yes!
stack obj YES yes OK? Yes
copyable no. move-only yes releases ownership illegal
as return type YES yes

[1] https://katyscode.wordpress.com/2012/10/04/c11-using-stdunique_ptr-as-a-class-member-initialization-move-semantics-and-custom-deleters/ and http://stackoverflow.com/questions/15648844/using-smart-pointers-for-class-members

shared_ptr(+unique_ptr)pbclone^pbref#Sutter

Practical question. In practice, the safe and lazy choice is pbclone. In IV, I think pbclone is “acceptable”. I feel pbref (const ref) is a risky micro-optimization, but Herb Sutter advised differently:

  • Express that a function will store and share ownership of a heap object using a by-value shared_ptr parameter
  • Use a const shared_ptr& as a parameter only if you’re not sure whether or not you’ll take a copy and share ownership. Perhaps the default choice IMO.

In my apps, the  receiving function often saves the smart_ptr in some container. Both options above actually work fine.

If you just need to access the underlying raw pointer, then Sutter said just pass in the raw pointer. I feel a pbref is also acceptable.

Smart pointer objects are designed to mimic raw pointers, which are usually passed by clone.

see http://stackoverflow.com/questions/8385457/should-i-pass-a-shared-ptr-by-reference and http://stackoverflow.com/questions/3310737/shared-ptr-by-reference-or-by-value

—-With unique_ptr, rules are simpler. Pass by clone as much as you want. It will be move-constructed. I don’t think pbref is needed.

See other posts about unique_ptr, such as https://bintanvictor.wordpress.com/2017/03/31/unique_ptr-and-move/

my simple autoPtr class template

#include <iostream>
using namespace std;

struct Dummy {
	void play() {
		cout << "play " << payload << endl;
	}
	float payload = 0.40490;
};
ostream& operator<<(ostream& os, Dummy const & d){
	os << "[ Dummy object "<<d.payload<<" ]";
	return os;
}
template<typename T> class autoPtr {
public:
	autoPtr(): autoPtr(nullptr){}
	autoPtr(T* raw): needle(raw) {
		needle = raw;
		if (raw)
			cout << "ctor called with *raw == " << *raw << endl;
	}
	autoPtr(autoPtr<T> & other) { //steal the resource
		cout << "copier called with other.real ";
		needle = other.needle;
		if (other.needle) {
			cout << "pointing to "<<*needle;
			other.needle = nullptr;
		}else {
			cout << "== nullptr";
		}
		cout << endl;
	}
	autoPtr<T> & operator=(autoPtr<T> & other) {
		if (&other == this) return *this;
		cout << "op= called ...";
		if (needle) {
			cout << " ... deleting original raw ptr *real = " << *needle;
			delete needle;
		}
		cout << endl; this->needle = other.needle;
		other.needle = nullptr;
		return *this;
	}
	T* operator->() {
		cout << "calling operator->"<<endl;
		return needle;
	}
	T& operator* () {
		cout << "dereferencing this ...";
		if (needle)
			cout << ".... *real == " << *needle << endl;
		else {
			cout << ".... returning a null ptr" << endl; throw "dereferencing a null autoPtr!"; } return *needle; //how do we return a T when real is nullptr? } void set(T * const raw) { delete this->needle;
		this->needle = raw;
	}
	~autoPtr() {
		cout << "Dtor called for *real == ";
		if (needle)
			cout << *needle;
		else
			cout << "(null ptr)";
		cout<< endl;
		delete needle;
	}
private:
	T* needle;
};
int main() {
	autoPtr<Dummy> pdummy(new Dummy);
	pdummy->play();
	autoPtr<int> p1 = (autoPtr<int>)new int(12);
	{
		autoPtr<int> p2(new int(35));
		cout << "Testing deference: *ptr == "<<*p2<< endl;
		autoPtr<int> p3(p2);
		p1 = p3;
	}
	return 0;
}

MyType a=77 what if conversion constructor is explicit

Background:

  explict MyType(int); // would disallow
  MyType a = 77;

http://en.cppreference.com/w/cpp/language/converting_constructor has a solution:

  MyType a = (MyType) 77; // Static cast would invoke the explicit conversion constructor!

Most custom types should make conversion constructors explicit to avoid hidden bugs, but smart pointer need an implicit conversion constructor, to support

  SmartPtr<int> myPtr = new int(77);

–A real example from CFM quant code

 FwdCurve::const_iterator iter = find( key ); //non-const itr

 QL_ASSERT( iter != ((FwdCurve * const) this)->end(), "not found" ); // original code probably before "explicit". 

// I had to change it to
 FwdCurve_iterator endItr = ((FwdCurve * const) this)->end();
 QL_ASSERT( iter != FwdCurve_const_iterator(endItr), "not found" ); //call the conversion ctor explicitly

factory returning a (smart) ptr

Background — This is one example of “too many variations”. A simple idea become popular. People start mix-n-match it with other ideas and create many, many interesting questions. In practice we should stick to a few best practices and avoid the hundreds of other variations.

I think it’s quite common (see [[safe c++]] P51) to have a factory function creating a heap “resource”. Given it’s on heap, factory must return the object by pointer. Easy to hit memory leak. Standard solution is return by ref count smart ptr.

Q: what if I forget to destruct the obj returned?
A: I feel it’s uncommon and unnatural. (More common to forget deletion.) Often the object returned i.e. the smart pointer object is on the stack – no way to “forget”.

Note if a raw pointer variable is on the stack, then the stack unwinding will deallocate/reclaim the memory, but won’t call delete on it!

Q: what if the object returned is saved as a field like myUmbrella.resource1
A: I feel this is fine because the myUmbrella object will be destructed by someone’s responsibility. That would automatically destruct the smart pointer field this->resource1.

Alternative to the ref count smart ptr, we can also return a scoped pointer. See [[safe c++]].

Q: what if factory need to return a pointer to stack?
A: I don’t think so. Pointee goes out of scope….
A: I feel most “resources” are represented by a heapy thingy. Alternatively, it’s also conceivable to hold a “permanent” resource like globally. Memory management is non-issue.

By the way, [[safe c++]] also mentions the __common__ pattern of a (non-factory) function returning a pointer to an existing object, rather than a “new SomeClass”. The challenges and solutions are different.

barebones RAII smart ptr classes

If RAII and memory management is the main purpose, I feel a better word for the class template is a “capsule class”. I feel the boost implementations are too complicated and hard to modify. Here are some small but realistic implementations

[[c++FAQ]] has a one-pager RAII capsule class.

[[safe c++]] has a 2-page-short source code for a real-world capsule. Includes both ref count and scoped versions.

http://www.artima.com/cppsource/bigtwo.html introduces a very simple RAII capsule. One of the simplest yet useful smart pointers. I think the entire class needs nothing but … big3 + ctor.

A few observations —

* It’s not reliable enough if the capsule ctor can throw. For RAII to provide the ironclad guarantee, the ctor of the “capsule” class (smart ptr or not) should be simple and robust, and no-throw. If the capsule is not even locked during construction, then it offers no protection no guarantee.

* (Smart pointer always needs a copy policy) It’s meaningless unless every client “goes through” the wrapper to get to the raw pointer. Raw pointer direct access is dangerous and should be completely avoided.

* It’s meaningless without the exception risk. RAII is mostly for exceptions and programmer forgetfulness.
* It’s meaningless if there’s no need for an ironclad “guarantee“.
* It’s meaningless if there’s no need for “automatic” cleanup.
* It relies on the dtor feature. RAII relies on dtor. Smart pointer relies on dtor.
* It relies on local stack variables,but …
* It’s unusable unless the original pointer is for a heap object.
… These keywords are the heart of RAII idiom.

Given the heavy reliance on dtor, the dtor should be fairly simple. I don’t think it should be virtual.

STL containers and smart ptr – pbclone on the outside

STL containers should store only values and smart ptr in containers, so said [[c++ coding standards]].

Essentially, store “value types” not reference types, borrowing c# terminology. Containers pass element objects (including 32-bit ptr objects) by value, as a rule. By-reference is less supported and a bit dangerous.

Now let’s look at smart ptr. Except inside the smart ptr class itself, do we ever pass smart ptr Instances by reference/ptr, or we always pass smart ptr Instances by value? I feel by-value is absolutely essential otherwise the special logic in copiers is bypassed.
I feel for STL containers and smart ptr, internal operations may pass element objects by reference or by ptr , but public API is mostly pass-by-value (pbclone) or pass-by-ptr, which is the C tradition. C++ style pass-by-reference is popular mostly in standard constructs such as copier and assignment operator or passing large objects without pointers.