1// reproduce dead lock 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      if (mutex_.isLockedByThisThread())
70      {
71        printf("WARNING: mutex_ is already locked by this thread, deadlock will happen.\n");
72      }
73      muduo::MutexLockGuard lock(mutex_);
74      auto it = stocks_.find(stock->key());
75      assert(it != stocks_.end());
76#ifdef REPRODUCE_BUG
77      if (auto x = it->second.lock())
78      {
79        assert(stock != x.get());
80        printf("use_count = %ld\n", x.use_count());
81        sleepMs(500);
82        printf("use_count = %ld\n", x.use_count());
83      }
84      else
85      {
86        stocks_.erase(it);
87      }
88#else
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#endif
98    }
99    delete stock;  // sorry, I lied
100  }
101
102  mutable muduo::MutexLock mutex_;
103  std::unordered_map<string, std::weak_ptr<Stock> > stocks_;
104};
105
106void threadB(StockFactory* factory)
107{
108  sleepMs(250);
109  auto stock = factory->get("MS");
110  printf("%s: stockB %p\n", muduo::CurrentThread::name(), stock.get());
111
112  sleepMs(500);
113  printf("%s: stockB destructs\n", muduo::CurrentThread::name());
114}
115
116int main()
117{
118  StockFactory factory;
119  muduo::Thread thr([&factory] { threadB(&factory); }, "thrB");
120  thr.start();
121  {
122  auto stock = factory.get("MS");
123  printf("%s: stock %p\n", muduo::CurrentThread::name(), stock.get());
124  }
125  thr.join();
126}
127