1#include <map>
2
3#include <boost/bind.hpp>
4#include <boost/enable_shared_from_this.hpp>
5#include <boost/shared_ptr.hpp>
6#include <boost/weak_ptr.hpp>
7
8#include "../Mutex.h"
9
10#include <assert.h>
11#include <stdio.h>
12
13using std::string;
14
15class Stock : boost::noncopyable
16{
17 public:
18  Stock(const string& name)
19    : name_(name)
20  {
21    printf(" Stock[%p] %s\n", this, name_.c_str());
22  }
23
24  ~Stock()
25  {
26    printf("~Stock[%p] %s\n", this, name_.c_str());
27  }
28
29  const string& key() const { return name_; }
30
31 private:
32  string name_;
33};
34
35namespace version1
36{
37
38// questionable code
39class StockFactory : boost::noncopyable
40{
41 public:
42
43  boost::shared_ptr<Stock> get(const string& key)
44  {
45    muduo::MutexLockGuard lock(mutex_);
46    boost::shared_ptr<Stock>& pStock = stocks_[key];
47    if (!pStock)
48    {
49      pStock.reset(new Stock(key));
50    }
51    return pStock;
52  }
53
54
55 private:
56  mutable muduo::MutexLock mutex_;
57  std::map<string, boost::shared_ptr<Stock> > stocks_;
58};
59
60}
61
62namespace version2
63{
64
65class StockFactory : boost::noncopyable
66{
67 public:
68  boost::shared_ptr<Stock> get(const string& key)
69  {
70    boost::shared_ptr<Stock> pStock;
71    muduo::MutexLockGuard lock(mutex_);
72    boost::weak_ptr<Stock>& wkStock = stocks_[key];
73    pStock = wkStock.lock();
74    if (!pStock)
75    {
76      pStock.reset(new Stock(key));
77      wkStock = pStock;
78    }
79    return pStock;
80  }
81
82 private:
83  mutable muduo::MutexLock mutex_;
84  std::map<string, boost::weak_ptr<Stock> > stocks_;
85};
86
87}
88
89namespace version3
90{
91
92class StockFactory : boost::noncopyable
93{
94 public:
95
96  boost::shared_ptr<Stock> get(const string& key)
97  {
98    boost::shared_ptr<Stock> pStock;
99    muduo::MutexLockGuard lock(mutex_);
100    boost::weak_ptr<Stock>& wkStock = stocks_[key];
101    pStock = wkStock.lock();
102    if (!pStock)
103    {
104      pStock.reset(new Stock(key),
105                   boost::bind(&StockFactory::deleteStock, this, _1));
106      wkStock = pStock;
107    }
108    return pStock;
109  }
110
111 private:
112
113  void deleteStock(Stock* stock)
114  {
115    printf("deleteStock[%p]\n", stock);
116    if (stock)
117    {
118      muduo::MutexLockGuard lock(mutex_);
119      stocks_.erase(stock->key());  // This is wrong, see removeStock below for correct implementation.
120    }
121    delete stock;  // sorry, I lied
122  }
123  mutable muduo::MutexLock mutex_;
124  std::map<string, boost::weak_ptr<Stock> > stocks_;
125};
126
127}
128
129namespace version4
130{
131
132class StockFactory : public boost::enable_shared_from_this<StockFactory>,
133                     boost::noncopyable
134{
135 public:
136
137  boost::shared_ptr<Stock> get(const string& key)
138  {
139    boost::shared_ptr<Stock> pStock;
140    muduo::MutexLockGuard lock(mutex_);
141    boost::weak_ptr<Stock>& wkStock = stocks_[key];
142    pStock = wkStock.lock();
143    if (!pStock)
144    {
145      pStock.reset(new Stock(key),
146                   boost::bind(&StockFactory::deleteStock,
147                               shared_from_this(),
148                               _1));
149      wkStock = pStock;
150    }
151    return pStock;
152  }
153
154 private:
155
156  void deleteStock(Stock* stock)
157  {
158    printf("deleteStock[%p]\n", stock);
159    if (stock)
160    {
161      muduo::MutexLockGuard lock(mutex_);
162      stocks_.erase(stock->key());  // This is wrong, see removeStock below for correct implementation.
163    }
164    delete stock;  // sorry, I lied
165  }
166  mutable muduo::MutexLock mutex_;
167  std::map<string, boost::weak_ptr<Stock> > stocks_;
168};
169
170}
171
172class StockFactory : public boost::enable_shared_from_this<StockFactory>,
173                     boost::noncopyable
174{
175 public:
176  boost::shared_ptr<Stock> get(const string& key)
177  {
178    boost::shared_ptr<Stock> pStock;
179    muduo::MutexLockGuard lock(mutex_);
180    boost::weak_ptr<Stock>& wkStock = stocks_[key];
181    pStock = wkStock.lock();
182    if (!pStock)
183    {
184      pStock.reset(new Stock(key),
185                   boost::bind(&StockFactory::weakDeleteCallback,
186                               boost::weak_ptr<StockFactory>(shared_from_this()),
187                               _1));
188      wkStock = pStock;
189    }
190    return pStock;
191  }
192
193 private:
194  static void weakDeleteCallback(const boost::weak_ptr<StockFactory>& wkFactory,
195                                 Stock* stock)
196  {
197    printf("weakDeleteStock[%p]\n", stock);
198    boost::shared_ptr<StockFactory> factory(wkFactory.lock());
199    if (factory)
200    {
201      factory->removeStock(stock);
202    }
203    else
204    {
205      printf("factory died.\n");
206    }
207    delete stock;  // sorry, I lied
208  }
209
210  void removeStock(Stock* stock)
211  {
212    if (stock)
213    {
214      muduo::MutexLockGuard lock(mutex_);
215      auto it = stocks_.find(stock->key());
216      if (it != stocks_.end() && it->second.expired())
217      {
218        stocks_.erase(stock->key());
219      }
220    }
221  }
222
223 private:
224  mutable muduo::MutexLock mutex_;
225  std::map<string, boost::weak_ptr<Stock> > stocks_;
226};
227
228void testLongLifeFactory()
229{
230  boost::shared_ptr<StockFactory> factory(new StockFactory);
231  {
232    boost::shared_ptr<Stock> stock = factory->get("NYSE:IBM");
233    boost::shared_ptr<Stock> stock2 = factory->get("NYSE:IBM");
234    assert(stock == stock2);
235    // stock destructs here
236  }
237  // factory destructs here
238}
239
240void testShortLifeFactory()
241{
242  boost::shared_ptr<Stock> stock;
243  {
244    boost::shared_ptr<StockFactory> factory(new StockFactory);
245    stock = factory->get("NYSE:IBM");
246    boost::shared_ptr<Stock> stock2 = factory->get("NYSE:IBM");
247    assert(stock == stock2);
248    // factory destructs here
249  }
250  // stock destructs here
251}
252
253int main()
254{
255  version1::StockFactory sf1;
256  version2::StockFactory sf2;
257  version3::StockFactory sf3;
258  boost::shared_ptr<version3::StockFactory> sf4(new version3::StockFactory);
259  boost::shared_ptr<StockFactory> sf5(new StockFactory);
260
261  {
262  boost::shared_ptr<Stock> s1 = sf1.get("stock1");
263  }
264
265  {
266  boost::shared_ptr<Stock> s2 = sf2.get("stock2");
267  }
268
269  {
270  boost::shared_ptr<Stock> s3 = sf3.get("stock3");
271  }
272
273  {
274  boost::shared_ptr<Stock> s4 = sf4->get("stock4");
275  }
276
277  {
278  boost::shared_ptr<Stock> s5 = sf5->get("stock5");
279  }
280
281  testLongLifeFactory();
282  testShortLifeFactory();
283}
284