cleanable.cc 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
  2. // This source code is licensed under both the GPLv2 (found in the
  3. // COPYING file in the root directory) and Apache 2.0 License
  4. // (found in the LICENSE.Apache file in the root directory).
  5. //
  6. // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
  7. // Use of this source code is governed by a BSD-style license that can be
  8. // found in the LICENSE file. See the AUTHORS file for names of contributors.
  9. #include "rocksdb/cleanable.h"
  10. #include <atomic>
  11. #include <cassert>
  12. #include <utility>
  13. namespace ROCKSDB_NAMESPACE {
  14. Cleanable::Cleanable() {
  15. cleanup_.function = nullptr;
  16. cleanup_.next = nullptr;
  17. }
  18. Cleanable::~Cleanable() { DoCleanup(); }
  19. Cleanable::Cleanable(Cleanable&& other) noexcept { *this = std::move(other); }
  20. Cleanable& Cleanable::operator=(Cleanable&& other) noexcept {
  21. assert(this != &other); // https://stackoverflow.com/a/9322542/454544
  22. cleanup_ = other.cleanup_;
  23. other.cleanup_.function = nullptr;
  24. other.cleanup_.next = nullptr;
  25. return *this;
  26. }
  27. // If the entire linked list was on heap we could have simply add attach one
  28. // link list to another. However the head is an embeded object to avoid the cost
  29. // of creating objects for most of the use cases when the Cleanable has only one
  30. // Cleanup to do. We could put evernything on heap if benchmarks show no
  31. // negative impact on performance.
  32. // Also we need to iterate on the linked list since there is no pointer to the
  33. // tail. We can add the tail pointer but maintainin it might negatively impact
  34. // the perforamnce for the common case of one cleanup where tail pointer is not
  35. // needed. Again benchmarks could clarify that.
  36. // Even without a tail pointer we could iterate on the list, find the tail, and
  37. // have only that node updated without the need to insert the Cleanups one by
  38. // one. This however would be redundant when the source Cleanable has one or a
  39. // few Cleanups which is the case most of the time.
  40. // TODO(myabandeh): if the list is too long we should maintain a tail pointer
  41. // and have the entire list (minus the head that has to be inserted separately)
  42. // merged with the target linked list at once.
  43. void Cleanable::DelegateCleanupsTo(Cleanable* other) {
  44. assert(other != nullptr);
  45. if (cleanup_.function == nullptr) {
  46. return;
  47. }
  48. Cleanup* c = &cleanup_;
  49. other->RegisterCleanup(c->function, c->arg1, c->arg2);
  50. c = c->next;
  51. while (c != nullptr) {
  52. Cleanup* next = c->next;
  53. other->RegisterCleanup(c);
  54. c = next;
  55. }
  56. cleanup_.function = nullptr;
  57. cleanup_.next = nullptr;
  58. }
  59. void Cleanable::RegisterCleanup(Cleanable::Cleanup* c) {
  60. assert(c != nullptr);
  61. if (cleanup_.function == nullptr) {
  62. cleanup_.function = c->function;
  63. cleanup_.arg1 = c->arg1;
  64. cleanup_.arg2 = c->arg2;
  65. delete c;
  66. } else {
  67. c->next = cleanup_.next;
  68. cleanup_.next = c;
  69. }
  70. }
  71. void Cleanable::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) {
  72. assert(func != nullptr);
  73. Cleanup* c;
  74. if (cleanup_.function == nullptr) {
  75. c = &cleanup_;
  76. } else {
  77. c = new Cleanup;
  78. c->next = cleanup_.next;
  79. cleanup_.next = c;
  80. }
  81. c->function = func;
  82. c->arg1 = arg1;
  83. c->arg2 = arg2;
  84. }
  85. struct SharedCleanablePtr::Impl : public Cleanable {
  86. std::atomic<unsigned> ref_count{1}; // Start with 1 ref
  87. void Ref() { ref_count.fetch_add(1, std::memory_order_relaxed); }
  88. void Unref() {
  89. if (ref_count.fetch_sub(1, std::memory_order_relaxed) == 1) {
  90. // Last ref
  91. delete this;
  92. }
  93. }
  94. static void UnrefWrapper(void* arg1, void* /*arg2*/) {
  95. static_cast<SharedCleanablePtr::Impl*>(arg1)->Unref();
  96. }
  97. };
  98. void SharedCleanablePtr::Reset() {
  99. if (ptr_) {
  100. ptr_->Unref();
  101. ptr_ = nullptr;
  102. }
  103. }
  104. void SharedCleanablePtr::Allocate() {
  105. Reset();
  106. ptr_ = new Impl();
  107. }
  108. SharedCleanablePtr::SharedCleanablePtr(const SharedCleanablePtr& from) {
  109. *this = from;
  110. }
  111. SharedCleanablePtr::SharedCleanablePtr(SharedCleanablePtr&& from) noexcept {
  112. *this = std::move(from);
  113. }
  114. SharedCleanablePtr& SharedCleanablePtr::operator=(
  115. const SharedCleanablePtr& from) {
  116. if (this != &from) {
  117. Reset();
  118. ptr_ = from.ptr_;
  119. if (ptr_) {
  120. ptr_->Ref();
  121. }
  122. }
  123. return *this;
  124. }
  125. SharedCleanablePtr& SharedCleanablePtr::operator=(
  126. SharedCleanablePtr&& from) noexcept {
  127. assert(this != &from); // https://stackoverflow.com/a/9322542/454544
  128. Reset();
  129. ptr_ = from.ptr_;
  130. from.ptr_ = nullptr;
  131. return *this;
  132. }
  133. SharedCleanablePtr::~SharedCleanablePtr() { Reset(); }
  134. Cleanable& SharedCleanablePtr::operator*() {
  135. return *ptr_; // implicit upcast
  136. }
  137. Cleanable* SharedCleanablePtr::operator->() {
  138. return ptr_; // implicit upcast
  139. }
  140. Cleanable* SharedCleanablePtr::get() {
  141. return ptr_; // implicit upcast
  142. }
  143. void SharedCleanablePtr::RegisterCopyWith(Cleanable* target) {
  144. if (ptr_) {
  145. // "Virtual" copy of the pointer
  146. ptr_->Ref();
  147. target->RegisterCleanup(&Impl::UnrefWrapper, ptr_, nullptr);
  148. }
  149. }
  150. void SharedCleanablePtr::MoveAsCleanupTo(Cleanable* target) {
  151. if (ptr_) {
  152. // "Virtual" move of the pointer
  153. target->RegisterCleanup(&Impl::UnrefWrapper, ptr_, nullptr);
  154. ptr_ = nullptr;
  155. }
  156. }
  157. } // namespace ROCKSDB_NAMESPACE