]> git.sesse.net Git - casparcg/blob - protocol/util/lock_container.cpp
Merge pull request #427 from krzyc/patch-1
[casparcg] / protocol / util / lock_container.cpp
1 #include "../StdAfx.h"
2
3 #include <tbb/spin_rw_mutex.h>
4 #include "lock_container.h"
5
6 namespace caspar { namespace IO {
7
8         struct lock_container::impl
9         {
10                 std::wstring                                                                            lifecycle_key_;
11         private:
12                 std::set<std::weak_ptr<client_connection<wchar_t>>, std::owner_less<std::weak_ptr<client_connection<wchar_t>>>> locks_;
13                 std::wstring                                                                            lock_phrase_;
14                 mutable tbb::spin_rw_mutex                                                      mutex_;
15
16         public:
17                 impl(const std::wstring& lifecycle_key) : lifecycle_key_(lifecycle_key) {}
18
19                 bool check_access(client_connection<wchar_t>::ptr conn)
20                 {
21                         tbb::spin_rw_mutex::scoped_lock lock(mutex_, false);
22                         std::weak_ptr<client_connection<wchar_t>> weak_ptr(conn);
23                         return locks_.empty() ? true : locks_.find(weak_ptr) != locks_.end();
24                 }
25
26                 bool try_lock(const std::wstring& lock_phrase, client_connection<wchar_t>::ptr conn)
27                 {
28                         tbb::spin_rw_mutex::scoped_lock lock(mutex_, false);
29                         if(lock_phrase_.empty() || lock_phrase == lock_phrase_)
30                         {
31                                 std::weak_ptr<client_connection<wchar_t>> weak_ptr(conn);
32                                 if(locks_.find(weak_ptr) == locks_.end())
33                                 {
34                                         {
35                                                 lock.upgrade_to_writer();
36                                                 lock_phrase_ = lock_phrase;
37                                                 locks_.insert(weak_ptr);
38                                         }
39
40                                         lock.release(); //risk of reentrancy-deadlock if we don't release prior to trying to attach lifecycle-bound object to connection
41                                         
42                                         CASPAR_LOG(info) << lifecycle_key_ << " acquired";
43
44                                         {
45                                                 std::shared_ptr<void> obj(nullptr, [=](void*) { do_release_lock(weak_ptr); });
46                                                 conn->add_lifecycle_bound_object(lifecycle_key_, obj);
47                                         }
48                                 }
49
50                                 return true;
51                         }
52                         return false;
53                 }
54
55                 void clear_locks()      //TODO: add a function-object parameter to be called for each clients that has it's lock released
56                 {
57                         std::vector<std::weak_ptr<client_connection<wchar_t>>> clients;
58
59                         {       //copy the connections locally and then clear the set
60                                 tbb::spin_rw_mutex::scoped_lock lock(mutex_, true);
61                                 clients.resize(locks_.size());
62                                 std::copy(locks_.begin(), locks_.end(), clients.begin());
63
64                                 locks_.clear();
65                                 lock_phrase_.clear();
66                         }
67
68                         //now we can take our time to inform the clients that their locks have been released.
69                         for (auto& conn : clients)
70                         {
71                                 auto ptr = conn.lock();
72                                 if(ptr)
73                                 {
74                                         ptr->remove_lifecycle_bound_object(lifecycle_key_);     //this calls do_relase_lock, which takes a write-lock
75                                         //TODO: invoke callback
76                                 }
77                         }
78                 }
79
80                 void release_lock(client_connection<wchar_t>::ptr conn)
81                 {
82                         conn->remove_lifecycle_bound_object(lifecycle_key_); //this calls do_relase_lock, which takes a write-lock
83                 }
84
85         private:
86                 void do_release_lock(std::weak_ptr<client_connection<wchar_t>> conn)
87                 {
88                         {
89                                 tbb::spin_rw_mutex::scoped_lock lock(mutex_, true);
90                                 if(!locks_.empty())
91                                 {
92                                         locks_.erase(conn);
93                                         if(locks_.empty())
94                                                 lock_phrase_.clear();
95                                 }
96                         }
97
98                         CASPAR_LOG(info) << lifecycle_key_ << " released";
99                 }
100
101         };
102
103         lock_container::lock_container(const std::wstring& lifecycle_key) : impl_(spl::make_unique<impl>(lifecycle_key)) {}
104         lock_container::~lock_container() {}
105
106         bool lock_container::check_access(client_connection<wchar_t>::ptr conn) { return impl_->check_access(conn); }
107         bool lock_container::try_lock(const std::wstring& lock_phrase, client_connection<wchar_t>::ptr conn) { return impl_->try_lock(lock_phrase, conn); }
108         void lock_container::release_lock(client_connection<wchar_t>::ptr conn) { impl_->release_lock(conn); }
109         void lock_container::clear_locks() { return impl_->clear_locks(); }
110 }}