Shaka Player Embedded
object_tracker.cc
Go to the documentation of this file.
1 // Copyright 2016 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
16 
18 #include "src/memory/heap_tracer.h"
19 #include "src/util/clock.h"
20 #include "src/util/utils.h"
21 
22 namespace shaka {
23 namespace memory {
24 
26  std::unique_lock<Mutex> lock(mutex_);
27  DCHECK(objects_.count(object) == 0 || to_delete_.count(object) == 1);
28  objects_.emplace(object, 0u);
29  to_delete_.erase(object);
30 
31  if (object->IsShortLived())
32  last_alive_time_.emplace(object, util::Clock::Instance.GetMonotonicTime());
33 }
34 
36  std::unique_lock<Mutex> lock(mutex_);
37  tracer_->ForceAlive(ptr);
38 }
39 
40 void ObjectTracker::AddRef(const Traceable* object) {
41  if (object) {
42  std::unique_lock<Mutex> lock(mutex_);
43  auto* key = const_cast<Traceable*>(object); // NOLINT
44  DCHECK_EQ(objects_.count(key), 1u);
45  objects_[key]++;
46 
47  tracer_->ForceAlive(object);
48  }
49 }
50 
51 void ObjectTracker::RemoveRef(const Traceable* object) {
52  if (object) {
53  std::unique_lock<Mutex> lock(mutex_);
54  auto* key = const_cast<Traceable*>(object); // NOLINT
55  DCHECK_EQ(objects_.count(key), 1u);
56  CHECK_GT(objects_[key], 0u);
57  objects_[key]--;
58 
59  // Don't use IsShortLived() here since |object| may be an invalid pointer.
60  // During Dispose(), objects may be destroyed with existing references to
61  // them. This means that |object| may be an invalid pointer.
62  if (last_alive_time_.count(key) > 0)
63  last_alive_time_[key] = util::Clock::Instance.GetMonotonicTime();
64  }
65 }
66 
67 std::unordered_set<const Traceable*> ObjectTracker::GetAliveObjects() const {
68  std::unique_lock<Mutex> lock(mutex_);
69  std::unordered_set<const Traceable*> ret;
70  ret.reserve(objects_.size());
71  for (auto& pair : objects_) {
72  if (pair.second != 0 || IsJsAlive(pair.first))
73  ret.insert(pair.first);
74  }
75  return ret;
76 }
77 
79  const std::unordered_set<const Traceable*>& alive) {
80  std::unique_lock<Mutex> lock(mutex_);
81  std::unordered_set<Traceable*> to_delete;
82  to_delete.reserve(objects_.size());
83  for (auto pair : objects_) {
84  // |alive| also contains objects that have a non-zero ref count. But we
85  // need to check against our ref count also to ensure new objects that
86  // are created while the GC is running are not deleted.
87  if (pair.second == 0u && alive.count(pair.first) == 0 &&
88  !IsJsAlive(pair.first)) {
89  to_delete.insert(pair.first);
90  }
91  }
92  to_delete_ = to_delete;
93 
94  DestroyObjects(to_delete, &lock);
95 }
96 
98  : mutex_("ObjectTracker"), tracer_(tracer) {}
99 
101  CHECK(objects_.empty());
102 }
103 
104 bool ObjectTracker::IsJsAlive(Traceable* object) const {
105  const uint64_t now = util::Clock::Instance.GetMonotonicTime();
106  if (object->IsShortLived()) {
107  if (last_alive_time_.count(object) == 0)
108  return false;
109 
110  return last_alive_time_.at(object) + Traceable::kShortLiveDurationMs > now;
111  }
112  return object->IsRootedAlive();
113 }
114 
115 uint32_t ObjectTracker::GetRefCount(Traceable* object) const {
116  std::unique_lock<Mutex> lock(mutex_);
117  DCHECK_EQ(1u, objects_.count(object));
118  return objects_.at(object);
119 }
120 
122  std::unique_lock<Mutex> lock(mutex_);
123  while (!objects_.empty()) {
124  std::unordered_set<Traceable*> to_delete;
125  for (auto& pair : objects_)
126  to_delete.insert(pair.first);
127  to_delete_ = to_delete;
128 
129  DestroyObjects(to_delete, &lock);
130  }
131 }
132 
133 void ObjectTracker::DestroyObjects(
134  const std::unordered_set<Traceable*>& to_delete,
135  std::unique_lock<Mutex>* lock) {
136  DCHECK(lock->owns_lock());
137  {
138  util::Unlocker<Mutex> unlock(lock);
139  // Don't hold lock so destructor can call AddRef.
140  for (Traceable* item : to_delete)
141  delete item;
142  }
143  VLOG(1) << "Deleted " << to_delete.size() << " object(s).";
144 
145  // Don't remove elements from |objects_| until after the destructor so the
146  // destructor can call AddRef.
147  for (auto it = objects_.begin(); it != objects_.end();) {
148  if (to_delete_.count(it->first) > 0) {
149  last_alive_time_.erase(it->first);
150  it = objects_.erase(it);
151  } else {
152  it++;
153  }
154  }
155 }
156 
157 } // namespace memory
158 } // namespace shaka
static constexpr const uint64_t kShortLiveDurationMs
Definition: heap_tracer.h:49
void AddRef(const Traceable *object)
std::unordered_set< const Traceable * > GetAliveObjects() const
void RegisterObject(Traceable *object)
void ForceAlive(const Traceable *ptr)
Definition: heap_tracer.cc:38
static const Clock Instance
Definition: clock.h:29
virtual uint64_t GetMonotonicTime() const
Definition: clock.cc:29
void FreeDeadObjects(const std::unordered_set< const Traceable *> &alive)
virtual bool IsShortLived() const
Definition: heap_tracer.cc:30
ObjectTracker(HeapTracer *tracer)
void RemoveRef(const Traceable *object)
void ForceAlive(const Traceable *ptr)