1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef MEDIA_BASE_CALLBACK_REGISTRY_H_
6 #define MEDIA_BASE_CALLBACK_REGISTRY_H_
7 
8 #include <stdint.h>
9 
10 #include <map>
11 #include <memory>
12 
13 #include "base/callback.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/synchronization/lock.h"
17 #include "base/thread_annotations.h"
18 #include "media/base/bind_to_current_loop.h"
19 
20 namespace media {
21 
22 // A class that keeps a callback registered. The callback will be unregistered
23 // upon destruction of this object.
24 class CallbackRegistration {
25  public:
26   CallbackRegistration() = default;
27   virtual ~CallbackRegistration() = default;
28 
29  private:
30   DISALLOW_COPY_AND_ASSIGN(CallbackRegistration);
31 };
32 
33 template <typename Sig>
34 class CallbackRegistry;
35 
36 // A helper class that can register, unregister callbacks, and notify registered
37 // callbacks. This class is thread safe: all methods can be called on any
38 // thread. The CallbackRegistry must outlive all CallbackRegistrations returned
39 // by Register().
40 // TODO(xhwang): This class is similar to base::CallbackList, but is simpler,
41 // and provides thread safty. Consider merging these two.
42 template <typename... Args>
43 class CallbackRegistry<void(Args...)> {
44  public:
45   using CallbackType = base::RepeatingCallback<void(Args...)>;
46 
47   CallbackRegistry() = default;
48   ~CallbackRegistry() = default;
49 
Register(CallbackType cb)50   std::unique_ptr<CallbackRegistration> Register(CallbackType cb)
51       WARN_UNUSED_RESULT {
52     base::AutoLock lock(lock_);
53     DCHECK(cb);
54     uint32_t registration_id = ++next_registration_id_;
55     DVLOG(1) << __func__ << ": registration_id = " << registration_id;
56 
57     // Use BindToCurrentLoop so that the callbacks are always posted to the
58     // thread where Register() is called. Also, this helps avoid reentrancy
59     // and deadlock issues, e.g. Register() is called in one of the callbacks.
60     callbacks_[registration_id] = BindToCurrentLoop(std::move(cb));
61 
62     return std::make_unique<RegistrationImpl>(this, registration_id);
63   }
64 
Notify(Args &&...args)65   void Notify(Args&&... args) {
66     DVLOG(1) << __func__;
67     base::AutoLock lock(lock_);
68     for (auto const& entry : callbacks_)
69       entry.second.Run(std::forward<Args>(args)...);
70   }
71 
72  private:
73   class RegistrationImpl : public CallbackRegistration {
74    public:
RegistrationImpl(CallbackRegistry<void (Args...)> * registry,uint32_t registration_id)75     RegistrationImpl(CallbackRegistry<void(Args...)>* registry,
76                      uint32_t registration_id)
77         : registry_(registry), registration_id_(registration_id) {}
78 
~RegistrationImpl()79     ~RegistrationImpl() override { registry_->Unregister(registration_id_); }
80 
81    private:
82     CallbackRegistry<void(Args...)>* registry_ = nullptr;
83     uint32_t registration_id_ = 0;
84 
85     DISALLOW_COPY_AND_ASSIGN(RegistrationImpl);
86   };
87 
Unregister(uint32_t registration_id)88   void Unregister(uint32_t registration_id) {
89     DVLOG(1) << __func__ << ": registration_id = " << registration_id;
90     base::AutoLock lock(lock_);
91     size_t num_callbacks_removed = callbacks_.erase(registration_id);
92     DCHECK_EQ(num_callbacks_removed, 1u);
93   }
94 
95   base::Lock lock_;
96   uint32_t next_registration_id_ GUARDED_BY(lock_) = 0;
97   std::map<uint32_t, CallbackType> callbacks_ GUARDED_BY(lock_);
98 
99   DISALLOW_COPY_AND_ASSIGN(CallbackRegistry);
100 };
101 
102 using ClosureRegistry = CallbackRegistry<void()>;
103 
104 }  // namespace media
105 
106 #endif  // MEDIA_BASE_CALLBACK_REGISTRY_H_
107