1// reproduce race condition of Factory.cc if compiled with -DREPRODUCE_BUG
2
3#include "../Mutex.h"
4
5#include <boost/noncopyable.hpp>
6
7#include <memory>
8#include <unordered_map>
9
10#include <assert.h>
11#include <stdio.h>
12#include <unistd.h>
13
14using std::string;
15
16void sleepMs(int ms)
17{
18  usleep(ms * 1000);
19}
20
21class Stock : boost::noncopyable
22{
23 public:
24  Stock(const string& name)
25    : name_(name)
26  {
27    printf("%s: Stock[%p] %s\n", muduo::CurrentThread::name(), this, name_.c_str());
28  }
29
30  ~Stock()
31  {
32    printf("%s: ~Stock[%p] %s\n", muduo::CurrentThread::name(), this, name_.c_str());
33  }
34
35  const string& key() const { return name_; }
36
37 private:
38  string name_;
39};
40
41
42class StockFactory : boost::noncopyable
43{
44 public:
45
46  std::shared_ptr<Stock> get(const string& key)
47  {
48    std::shared_ptr<Stock> pStock;
49    muduo::MutexLockGuard lock(mutex_);
50    std::weak_ptr<Stock>& wkStock = stocks_[key];
51    pStock = wkStock.lock();
52    if (!pStock)
53    {
54      pStock.reset(new Stock(key),
55                   [this] (Stock* stock) { deleteStock(stock); });
56      wkStock = pStock;
57    }
58    return pStock;
59  }
60
61 private:
62
63  void deleteStock(Stock* stock)
64  {
65    printf("%s: deleteStock[%p]\n", muduo::CurrentThread::name(), stock);
66    if (stock)
67    {
68      sleepMs(500);
69      muduo::MutexLockGuard lock(mutex_);
70#ifdef REPRODUCE_BUG
71      auto it = stocks_.find(stock->key());
72      assert(it != stocks_.end());
73      if (it->second.expired())
74      {
75        stocks_.erase(it);
76      }
77      else
78      {
79        printf("%s: %s is not expired\n", muduo::CurrentThread::name(), stock->key().c_str());
80      }
81#else
82      auto it = stocks_.find(stock->key());
83      if (it == stocks_.end())
84      {
85        printf("%s: %s had been deleted\n", muduo::CurrentThread::name(), stock->key().c_str());
86      }
87      else
88      {
89        if (it->second.expired())
90        {
91          stocks_.erase(it);
92        }
93        else
94        {
95          printf("%s: %s is not expired\n", muduo::CurrentThread::name(), stock->key().c_str());
96        }
97      }
98#endif
99    }
100    delete stock;  // sorry, I lied
101  }
102
103  mutable muduo::MutexLock mutex_;
104  std::unordered_map<string, std::weak_ptr<Stock> > stocks_;
105};
106
107void threadB(StockFactory* factory)
108{
109  sleepMs(250);
110  auto stock = factory->get("MS");
111  printf("%s: stock %p\n", muduo::CurrentThread::name(), stock.get());
112}
113
114int main()
115{
116  StockFactory factory;
117  muduo::Thread thr([&factory] { threadB(&factory); }, "thrB");
118  thr.start();
119  {
120  auto stock = factory.get("MS");
121  printf("%s: stock %p\n", muduo::CurrentThread::name(), stock.get());
122  }
123  thr.join();
124}
125