summaryrefslogtreecommitdiff
path: root/README.SMART-POINTERS
blob: abd65d8cc68f77915c4261065e8c4b085d6a27a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
  Smart pointers in aptitude


  IMPORTANT NOTE:

  All (or most of) the smart pointers documented here are deprecated
in favor of boost::shared_ptr, which is simpler, more flexible, and
probably more efficient (although I haven't checked that).  New code
should use shared_ptr and/or scoped_ptr.

NEW STUFF:

  The preferred convention for new code is to use boost's shared_ptr
and scoped_ptr classes.  If you just have a private pointer that
should never be "owned" by other code, you might consider scoped_ptr.
Otherwise, shared_ptr is a better bet.  You should think twice before
writing bare pointers.

  shared_ptr is NOT MAGIC.  You can still get memory leaks and crashes
if you misuse it; it is merely a tool to help you manage your object
lifetimes.  The following notes will help.

  * Do not write this code:

      boost::shared_ptr<T> t = new T(...);

    It is (potentially) exception-unsafe.  Instead of thinking hard
    about whether it's safe or not, do this, which is both safe and
    more efficient:

      boost::shared_ptr<T> t = boost::make_shared<T>(...);

  * Be very careful about creating circular chains of shared_ptrs.
    These cannot be reclaimed by the shared_ptr mechanism and will
    result in memory leaks.  Many of the notes below are just
    ways of avoiding this.

  * Although shared_ptr technically allows "sharing" of pointers, most
    shared_ptr objects in aptitude should have a single owner at any
    given time, just like bare pointers.  For instance, look at how
    search patterns are normally handled.  pattern::create() might be
    invoked from some subroutine.  It creates a pattern in a
    shared_ptr and returns it, passing ownership to its caller.  Its
    caller in turn invokes the constructor of another object, passing
    the pattern's shared_ptr in.  The constructor takes ownership and
    stores the pattern in a member variable for later use.

    The difference between shared_ptr and bare pointers is that
    violating the above pattern will at worst produce a memory leak
    rather than a crash.

  * For "leaf" objects that don't store smart pointers to anything
    else, using shared_ptr to freely share references is reasonable.

  * Objects that form an acyclic graph can be safely
    reference-counted.  You can force the objects in a module to form
    an acyclic graph by making their constructor(s) take pointers to
    other objects, and by not providing a way to modify links after
    the fact.  For instance, the match pattern objects and the (not
    shared) fragment objects use this approach.  Be sure that you
    don't have strong references to objects outside the module,
    though, or you can still be bitten by this problem.

  * Be very careful about embedding shared_ptrs into callbacks.  If
    the type you're embedding can't contain strong references, this is
    OK.  If it can, you can end up creating a hidden reference cycle
    that will be hard to debug.  Consider using boost's weak_ptr
    object, which automatically becomes invalid when its parent
    shared_ptr is destroyed.

    Callbacks passed to the main loop are a special case, since those
    are normally not stored permanently anywhere (i.e., they have no
    strong incoming references), and there may be lifetime issues that
    make weak references unacceptable.  If you rely on this, be sure
    to document it in comments and ensure that you really don't take a
    strong reference to the callback.  It's better not to do this, but
    I know there were a few places that I ran into where it was handy.

  * Because there is a cost to using shared_ptr, don't use it
    willy-nilly, particularly for low-level code.

OLD DEPRECATED STUFF:

  aptitude uses smart pointers in a number of ways internally.  These
techniques generally simplify the code and make it easy to program
safely, but C++ being what it is, you should be aware of how they work
and what the caveats are.

  * threading

  As of this writing, none of the smart pointers lock their reference
  counts (implementing locks in the imm::wtree class caused the
  problem resolver's running time to increase by 50%!).  Some of the
  less-commonly-used ones may get locking in the future, but for the
  time being you should handle objects that are given to another
  thread carefully -- do deep copies of anything that's
  reference-counted.  (this is only the imm::* stuff at the moment, so
  the problem is managable) For more information on threading, see
  README.THREADS.

  * reference-counted immutable values

  aptitude employs reference-counting of immutable objects in several
  places.  For the most part this is invisible to clients; the typical
  implementation looks like this:

      class foo
      {
	class foo_impl
	{
	  ...
	public:
	  void m1();
	  void m2(int);
	  ...
	};
      public:
	void m1();
	void m2(int);
	...
      };

  When you create a new "foo" object, a corresponding foo_impl is
  created with reference-count 1.  Reference counts are managed
  in all the ways you expect, and the object is deleted when you're
  done with it.

  This use of reference-counting can be viewed as a performance hack:
  since the objects are immutable, pass-by-value is indistinguishable
  from pass-by-reference; if we can prove that no strong cycles will
  be created, it's always safe to reference-count objects like this.


  * auto_ptr for temporary return values

  When building up a temporary return value, several routines use
  auto_ptrs to store intermediates:

      auto_ptr<T> v1 = parse_T_from_string(str1);
      auto_ptr<T> v2 = parse_T_from_string(str2);

  The advantage here is that you can freely throw exceptions and/or
  break out of the function with an early "return" without worrying
  about which of {v1,v2} have to be deleted.  To actually return them,
  you'd do something like this:

      return pair<T*,T*>(v1.release(), v2.release());

  The "release" calls tell v1 and v2 that they no longer "own" their
  respective pointers.



  * reference-counting of display widgets

  Display widgets are also reference-counted.  This is a
  generalization of the idea that "the enclosing widget owns this
  one"; it provides more support for extending the lifetime of a
  widget if, for instance, you want to extract values from it after
  its parent is done with it.  Unfortunately, this reference counting
  is NOT fully transparent, and you should be aware of some basic
  principles:

    - As with any reference counting scheme, it is up to you to
      avoid creating strong cycles.  By default, the only
      non-transient strong references are references from a parent to
      a child; your best bet is to ensure that all additional strong
      references either have stack lifetime or do not close cycles.

      If you must create a cycle, make sure it gets explicitly broken
      before all external references to it are lost.

    - Reference-counting is done via the generic ref_ptr class
      (ref_ptr.h).  This is a templated class, like auto_ptr, and
      generally works as you expect.  To enforce the use of ref_ptr,
      all widget constructors are protected; static ::create methods
      are provided to actually allocate a new widget.  Because the
      cwidget::widget class initializes its reference count to 1, the
      ::create method should explicitly decref() its return value; see
      the existing ::create routines for examples.

      To make code a bit more readable, adopt the convention of
      creating typedefs for ref_ptr wrappers around new classes.  For
      instance, if you have just created the class vs_moo, add the
      following line to its header file:

          typedef ref_ptr<vs_moo> vs_moo_ref;

    - Watch out for deletion of "this".  If you aren't sure whether
      "this" will be deleted in a method, I recommend creating a
      method-scoped strong reference to it:

          ref_ptr<this_type> thisref = this;

      Doing so will prevent "this" from being deleted until the
      current method terminates and is probably good practice in
      general.

    - Beware sigc::bind.  sigc::bind is an easy way to create bad
      circularities; moreover, it's actually unsafe to bind a ref_ptr
      as a slot argument.  The solution adopted in cwidget is to
      exploit sigc++ weak references.  If w is a ref_ptr, then rather
      than closing w, you should close over w.weak_ref().

      Unfortunately, w.weak_ref() will appear to the callee as a C++
      reference, not a ref_ptr: if w has type T, you need a slot that
      accepts a T&, not a ref_ptr<T>.  To solve this problem, it is
      conventional for widgets defining public interfaces that accept
      a ref_ptr<T> to define a corresponding _bare method that accepts
      a T&; the _bare method should simply instantiate a ref_ptr and
      call the main interface.  For instance,

          void add_widget(const ref_ptr<cwidget::widget> &w);
	  void add_widget_bare(cwidget::widget &w)
	  {
	    add_widget(ref_ptr<cwidget::widget>(&w));
	  }

      Obviously this is less than ideal, but it will work.  Be aware,
      though, that the bound argument is a *weak* reference: if there
      are no strong references to a bound widget, the signal
      connection will simply disappear.