clock_cache.cc 18.7 KB
Newer Older
Y
Yi Wu 已提交
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
S
Siying Dong 已提交
2 3 4
//  This source code is licensed under both the GPLv2 (found in the
//  COPYING file in the root directory) and Apache 2.0 License
//  (found in the LICENSE.Apache file in the root directory).
Y
Yi Wu 已提交
5 6 7 8 9
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

10
#include "cache/clock_cache.h"
Y
Yi Wu 已提交
11

G
Guido Tagliavini Ponce 已提交
12 13 14 15
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <functional>
Y
Yi Wu 已提交
16

G
Guido Tagliavini Ponce 已提交
17 18 19 20 21 22 23
#include "monitoring/perf_context_imp.h"
#include "monitoring/statistics.h"
#include "port/lang.h"
#include "util/distributed_mutex.h"
#include "util/hash.h"
#include "util/math.h"
#include "util/random.h"
Y
Yi Wu 已提交
24

G
Guido Tagliavini Ponce 已提交
25
namespace ROCKSDB_NAMESPACE {
Y
Yi Wu 已提交
26

G
Guido Tagliavini Ponce 已提交
27
namespace clock_cache {
Y
Yi Wu 已提交
28

G
Guido Tagliavini Ponce 已提交
29 30 31 32 33 34 35 36 37
ClockHandleTable::ClockHandleTable(int hash_bits)
    : length_bits_(hash_bits),
      length_bits_mask_((uint32_t{1} << length_bits_) - 1),
      occupancy_(0),
      occupancy_limit_(static_cast<uint32_t>((uint32_t{1} << length_bits_) *
                                             kStrictLoadFactor)),
      array_(new ClockHandle[size_t{1} << length_bits_]) {
  assert(hash_bits <= 32);
}
Y
Yi Wu 已提交
38

G
Guido Tagliavini Ponce 已提交
39 40 41
ClockHandleTable::~ClockHandleTable() {
  ApplyToEntriesRange([](ClockHandle* h) { h->FreeData(); }, 0, GetTableSize());
}
Y
Yi Wu 已提交
42

G
Guido Tagliavini Ponce 已提交
43 44 45 46 47
ClockHandle* ClockHandleTable::Lookup(const Slice& key) {
  int probe = 0;
  int slot = FindVisibleElement(key, probe, 0);
  return (slot == -1) ? nullptr : &array_[slot];
}
Y
Yi Wu 已提交
48

G
Guido Tagliavini Ponce 已提交
49 50 51 52 53 54 55
ClockHandle* ClockHandleTable::Insert(ClockHandle* h, ClockHandle** old) {
  int probe = 0;
  int slot =
      FindVisibleElementOrAvailableSlot(h->key(), probe, 1 /*displacement*/);
  *old = nullptr;
  if (slot == -1) {
    return nullptr;
Y
Yi Wu 已提交
56
  }
57

G
Guido Tagliavini Ponce 已提交
58 59 60 61 62 63 64 65 66 67 68 69 70 71
  if (array_[slot].IsEmpty() || array_[slot].IsTombstone()) {
    bool empty = array_[slot].IsEmpty();
    Assign(slot, h);
    ClockHandle* new_entry = &array_[slot];
    if (empty) {
      // This used to be an empty slot.
      return new_entry;
    }
    // It used to be a tombstone, so there may already be a copy of the
    // key in the table.
    slot = FindVisibleElement(h->key(), probe, 0 /*displacement*/);
    if (slot == -1) {
      // No existing copy of the key.
      return new_entry;
72
    }
G
Guido Tagliavini Ponce 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
    *old = &array_[slot];
    return new_entry;
  } else {
    // There is an existing copy of the key.
    *old = &array_[slot];
    // Find an available slot for the new element.
    array_[slot].displacements++;
    slot = FindAvailableSlot(h->key(), probe, 1 /*displacement*/);
    if (slot == -1) {
      // No available slots. Roll back displacements.
      probe = 0;
      slot = FindVisibleElement(h->key(), probe, -1);
      array_[slot].displacements--;
      FindAvailableSlot(h->key(), probe, -1);
      return nullptr;
    }
    Assign(slot, h);
    return &array_[slot];
91
  }
G
Guido Tagliavini Ponce 已提交
92
}
93

G
Guido Tagliavini Ponce 已提交
94 95 96 97 98 99 100 101 102 103
void ClockHandleTable::Remove(ClockHandle* h) {
  assert(!h->IsInClockList());  // Already off the clock list.
  int probe = 0;
  FindSlot(
      h->key(), [&h](ClockHandle* e) { return e == h; }, probe,
      -1 /*displacement*/);
  h->SetIsVisible(false);
  h->SetIsElement(false);
  occupancy_--;
}
Y
Yi Wu 已提交
104

G
Guido Tagliavini Ponce 已提交
105 106 107 108 109 110 111 112 113 114
void ClockHandleTable::Assign(int slot, ClockHandle* h) {
  ClockHandle* dst = &array_[slot];
  uint32_t disp = dst->displacements;
  *dst = *h;
  dst->displacements = disp;
  dst->SetIsVisible(true);
  dst->SetIsElement(true);
  dst->SetPriority(ClockHandle::ClockPriority::NONE);
  occupancy_++;
}
Y
Yi Wu 已提交
115

G
Guido Tagliavini Ponce 已提交
116
void ClockHandleTable::Exclude(ClockHandle* h) { h->SetIsVisible(false); }
Y
Yi Wu 已提交
117

G
Guido Tagliavini Ponce 已提交
118 119 120 121 122 123
int ClockHandleTable::FindVisibleElement(const Slice& key, int& probe,
                                         int displacement) {
  return FindSlot(
      key, [&](ClockHandle* h) { return h->Matches(key) && h->IsVisible(); },
      probe, displacement);
}
Y
Yi Wu 已提交
124

G
Guido Tagliavini Ponce 已提交
125 126 127 128 129 130
int ClockHandleTable::FindAvailableSlot(const Slice& key, int& probe,
                                        int displacement) {
  return FindSlot(
      key, [](ClockHandle* h) { return h->IsEmpty() || h->IsTombstone(); },
      probe, displacement);
}
Y
Yi Wu 已提交
131

G
Guido Tagliavini Ponce 已提交
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
int ClockHandleTable::FindVisibleElementOrAvailableSlot(const Slice& key,
                                                        int& probe,
                                                        int displacement) {
  return FindSlot(
      key,
      [&](ClockHandle* h) {
        return h->IsEmpty() || h->IsTombstone() ||
               (h->Matches(key) && h->IsVisible());
      },
      probe, displacement);
}

inline int ClockHandleTable::FindSlot(const Slice& key,
                                      std::function<bool(ClockHandle*)> cond,
                                      int& probe, int displacement) {
  uint32_t base = ModTableSize(Hash(key.data(), key.size(), kProbingSeed1));
  uint32_t increment =
      ModTableSize((Hash(key.data(), key.size(), kProbingSeed2) << 1) | 1);
  uint32_t current = ModTableSize(base + probe * increment);
  while (true) {
    ClockHandle* h = &array_[current];
    probe++;
    if (current == base && probe > 1) {
      // We looped back.
      return -1;
    }
    if (cond(h)) {
      return current;
    }
    if (h->IsEmpty()) {
      // We check emptyness after the condition, because
      // the condition may be emptyness.
      return -1;
Y
Yi Wu 已提交
165
    }
G
Guido Tagliavini Ponce 已提交
166 167
    h->displacements += displacement;
    current = ModTableSize(current + increment);
Y
Yi Wu 已提交
168 169 170
  }
}

G
Guido Tagliavini Ponce 已提交
171 172 173 174 175 176 177 178 179 180 181
ClockCacheShard::ClockCacheShard(
    size_t capacity, size_t estimated_value_size, bool strict_capacity_limit,
    CacheMetadataChargePolicy metadata_charge_policy)
    : capacity_(capacity),
      strict_capacity_limit_(strict_capacity_limit),
      clock_pointer_(0),
      table_(
          CalcHashBits(capacity, estimated_value_size, metadata_charge_policy)),
      usage_(0),
      clock_usage_(0) {
  set_metadata_charge_policy(metadata_charge_policy);
Y
Yi Wu 已提交
182 183
}

G
Guido Tagliavini Ponce 已提交
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
void ClockCacheShard::EraseUnRefEntries() {
  autovector<ClockHandle> last_reference_list;
  {
    DMutexLock l(mutex_);
    uint32_t slot = 0;
    do {
      ClockHandle* old = &(table_.array_[slot]);
      if (!old->IsInClockList()) {
        continue;
      }
      ClockRemove(old);
      table_.Remove(old);
      assert(usage_ >= old->total_charge);
      usage_ -= old->total_charge;
      last_reference_list.push_back(*old);
      slot = table_.ModTableSize(slot + 1);
    } while (slot != 0);
  }

  // Free the entries here outside of mutex for performance reasons.
  for (auto& h : last_reference_list) {
    h.FreeData();
  }
Y
Yi Wu 已提交
207 208
}

209 210 211 212
void ClockCacheShard::ApplyToSomeEntries(
    const std::function<void(const Slice& key, void* value, size_t charge,
                             DeleterFn deleter)>& callback,
    uint32_t average_entries_per_lock, uint32_t* state) {
G
Guido Tagliavini Ponce 已提交
213 214 215
  // The state is essentially going to be the starting hash, which works
  // nicely even if we resize between calls because we use upper-most
  // hash bits for table indexes.
216
  DMutexLock l(mutex_);
G
Guido Tagliavini Ponce 已提交
217 218
  uint32_t length_bits = table_.GetLengthBits();
  uint32_t length = table_.GetTableSize();
219

G
Guido Tagliavini Ponce 已提交
220 221 222 223 224 225 226 227 228 229
  assert(average_entries_per_lock > 0);
  // Assuming we are called with same average_entries_per_lock repeatedly,
  // this simplifies some logic (index_end will not overflow).
  assert(average_entries_per_lock < length || *state == 0);

  uint32_t index_begin = *state >> (32 - length_bits);
  uint32_t index_end = index_begin + average_entries_per_lock;
  if (index_end >= length) {
    // Going to end
    index_end = length;
230 231
    *state = UINT32_MAX;
  } else {
G
Guido Tagliavini Ponce 已提交
232
    *state = index_end << (32 - length_bits);
Y
Yi Wu 已提交
233
  }
234

G
Guido Tagliavini Ponce 已提交
235 236 237 238 239 240 241
  table_.ApplyToEntriesRange(
      [callback,
       metadata_charge_policy = metadata_charge_policy_](ClockHandle* h) {
        callback(h->key(), h->value, h->GetCharge(metadata_charge_policy),
                 h->deleter);
      },
      index_begin, index_end);
Y
Yi Wu 已提交
242 243
}

G
Guido Tagliavini Ponce 已提交
244 245 246 247 248
void ClockCacheShard::ClockRemove(ClockHandle* h) {
  assert(h->IsInClockList());
  h->SetPriority(ClockHandle::ClockPriority::NONE);
  assert(clock_usage_ >= h->total_charge);
  clock_usage_ -= h->total_charge;
Y
Yi Wu 已提交
249 250
}

G
Guido Tagliavini Ponce 已提交
251 252 253 254
void ClockCacheShard::ClockInsert(ClockHandle* h) {
  assert(!h->IsInClockList());
  h->SetPriority(ClockHandle::ClockPriority::HIGH);
  clock_usage_ += h->total_charge;
Y
Yi Wu 已提交
255 256
}

G
Guido Tagliavini Ponce 已提交
257 258 259 260 261 262 263 264 265
void ClockCacheShard::EvictFromClock(size_t charge,
                                     autovector<ClockHandle>* deleted) {
  assert(charge <= capacity_);
  while (clock_usage_ > 0 && (usage_ + charge) > capacity_) {
    ClockHandle* old = &table_.array_[clock_pointer_];
    clock_pointer_ = table_.ModTableSize(clock_pointer_ + 1);
    // Clock list contains only elements which can be evicted.
    if (!old->IsInClockList()) {
      continue;
Y
Yi Wu 已提交
266
    }
G
Guido Tagliavini Ponce 已提交
267 268 269 270 271 272 273 274 275
    if (old->GetPriority() == ClockHandle::ClockPriority::LOW) {
      ClockRemove(old);
      table_.Remove(old);
      assert(usage_ >= old->total_charge);
      usage_ -= old->total_charge;
      deleted->push_back(*old);
      return;
    }
    old->DecreasePriority();
Y
Yi Wu 已提交
276 277 278
  }
}

G
Guido Tagliavini Ponce 已提交
279 280 281 282 283 284
size_t ClockCacheShard::CalcEstimatedHandleCharge(
    size_t estimated_value_size,
    CacheMetadataChargePolicy metadata_charge_policy) {
  ClockHandle h;
  h.CalcTotalCharge(estimated_value_size, metadata_charge_policy);
  return h.total_charge;
Y
Yi Wu 已提交
285 286
}

G
Guido Tagliavini Ponce 已提交
287 288 289 290 291
int ClockCacheShard::CalcHashBits(
    size_t capacity, size_t estimated_value_size,
    CacheMetadataChargePolicy metadata_charge_policy) {
  size_t handle_charge =
      CalcEstimatedHandleCharge(estimated_value_size, metadata_charge_policy);
292
  assert(handle_charge > 0);
G
Guido Tagliavini Ponce 已提交
293
  uint32_t num_entries =
294 295 296
      static_cast<uint32_t>(capacity / (kLoadFactor * handle_charge)) + 1;
  assert(num_entries <= uint32_t{1} << 31);
  return FloorLog2((num_entries << 1) - 1);
Y
Yi Wu 已提交
297 298 299
}

void ClockCacheShard::SetCapacity(size_t capacity) {
G
Guido Tagliavini Ponce 已提交
300 301
  assert(false);  // Not supported. TODO(Guido) Support it?
  autovector<ClockHandle> last_reference_list;
Y
Yi Wu 已提交
302
  {
303
    DMutexLock l(mutex_);
G
Guido Tagliavini Ponce 已提交
304 305 306 307 308 309 310
    capacity_ = capacity;
    EvictFromClock(0, &last_reference_list);
  }

  // Free the entries here outside of mutex for performance reasons.
  for (auto& h : last_reference_list) {
    h.FreeData();
Y
Yi Wu 已提交
311 312 313 314
  }
}

void ClockCacheShard::SetStrictCapacityLimit(bool strict_capacity_limit) {
315
  DMutexLock l(mutex_);
G
Guido Tagliavini Ponce 已提交
316
  strict_capacity_limit_ = strict_capacity_limit;
Y
Yi Wu 已提交
317 318 319
}

Status ClockCacheShard::Insert(const Slice& key, uint32_t hash, void* value,
G
Guido Tagliavini Ponce 已提交
320 321
                               size_t charge, Cache::DeleterFn deleter,
                               Cache::Handle** handle,
A
Andrew Kryczka 已提交
322
                               Cache::Priority /*priority*/) {
G
Guido Tagliavini Ponce 已提交
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
  if (key.size() != kCacheKeySize) {
    return Status::NotSupported("ClockCache only supports key size " +
                                std::to_string(kCacheKeySize) + "B");
  }

  ClockHandle tmp;
  tmp.value = value;
  tmp.deleter = deleter;
  tmp.hash = hash;
  tmp.CalcTotalCharge(charge, metadata_charge_policy_);
  for (int i = 0; i < kCacheKeySize; i++) {
    tmp.key_data[i] = key.data()[i];
  }

  Status s = Status::OK();
  autovector<ClockHandle> last_reference_list;
  {
    DMutexLock l(mutex_);
    assert(table_.GetOccupancy() <= table_.GetOccupancyLimit());
    // Free the space following strict clock policy until enough space
    // is freed or the clock list is empty.
    EvictFromClock(tmp.total_charge, &last_reference_list);
    if ((usage_ + tmp.total_charge > capacity_ &&
         (strict_capacity_limit_ || handle == nullptr)) ||
        table_.GetOccupancy() == table_.GetOccupancyLimit()) {
      if (handle == nullptr) {
        // Don't insert the entry but still return ok, as if the entry inserted
        // into cache and get evicted immediately.
        last_reference_list.push_back(tmp);
      } else {
        if (table_.GetOccupancy() == table_.GetOccupancyLimit()) {
354 355 356
          // TODO: Consider using a distinct status for this case, but usually
          // it will be handled the same way as reaching charge capacity limit
          s = Status::MemoryLimit(
G
Guido Tagliavini Ponce 已提交
357 358
              "Insert failed because all slots in the hash table are full.");
        } else {
359
          s = Status::MemoryLimit(
G
Guido Tagliavini Ponce 已提交
360 361 362 363
              "Insert failed because the total charge has exceeded the "
              "capacity.");
        }
      }
364
    } else {
G
Guido Tagliavini Ponce 已提交
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
      // Insert into the cache. Note that the cache might get larger than its
      // capacity if not enough space was freed up.
      ClockHandle* old;
      ClockHandle* h = table_.Insert(&tmp, &old);
      assert(h != nullptr);  // We're below occupancy, so this insertion should
                             // never fail.
      usage_ += h->total_charge;
      if (old != nullptr) {
        s = Status::OkOverwritten();
        assert(old->IsVisible());
        table_.Exclude(old);
        if (!old->HasRefs()) {
          // old is in clock because it's in cache and its reference count is 0.
          ClockRemove(old);
          table_.Remove(old);
          assert(usage_ >= old->total_charge);
          usage_ -= old->total_charge;
          last_reference_list.push_back(*old);
        }
      }
      if (handle == nullptr) {
        ClockInsert(h);
      } else {
        // If caller already holds a ref, no need to take one here.
        if (!h->HasRefs()) {
          h->Ref();
        }
        *handle = reinterpret_cast<Cache::Handle*>(h);
      }
394
    }
Y
Yi Wu 已提交
395
  }
G
Guido Tagliavini Ponce 已提交
396 397 398 399

  // Free the entries here outside of mutex for performance reasons.
  for (auto& h : last_reference_list) {
    h.FreeData();
400
  }
G
Guido Tagliavini Ponce 已提交
401

Y
Yi Wu 已提交
402 403 404
  return s;
}

G
Guido Tagliavini Ponce 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418
Cache::Handle* ClockCacheShard::Lookup(const Slice& key, uint32_t /* hash */) {
  ClockHandle* h = nullptr;
  {
    DMutexLock l(mutex_);
    h = table_.Lookup(key);
    if (h != nullptr) {
      assert(h->IsVisible());
      if (!h->HasRefs()) {
        // The entry is in clock since it's in the hash table and has no
        // external references.
        ClockRemove(h);
      }
      h->Ref();
    }
Y
Yi Wu 已提交
419
  }
G
Guido Tagliavini Ponce 已提交
420
  return reinterpret_cast<Cache::Handle*>(h);
Y
Yi Wu 已提交
421 422
}

G
Guido Tagliavini Ponce 已提交
423 424 425 426 427 428 429
bool ClockCacheShard::Ref(Cache::Handle* h) {
  ClockHandle* e = reinterpret_cast<ClockHandle*>(h);
  DMutexLock l(mutex_);
  // To create another reference - entry must be already externally referenced.
  assert(e->HasRefs());
  e->Ref();
  return true;
Y
Yi Wu 已提交
430 431
}

G
Guido Tagliavini Ponce 已提交
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
bool ClockCacheShard::Release(Cache::Handle* handle, bool erase_if_last_ref) {
  if (handle == nullptr) {
    return false;
  }
  ClockHandle* h = reinterpret_cast<ClockHandle*>(handle);
  ClockHandle copy;
  bool last_reference = false;
  assert(!h->IsInClockList());
  {
    DMutexLock l(mutex_);
    last_reference = h->Unref();
    if (last_reference && h->IsVisible()) {
      // The item is still in cache, and nobody else holds a reference to it.
      if (usage_ > capacity_ || erase_if_last_ref) {
        // The clock list must be empty since the cache is full.
        assert(clock_usage_ == 0 || erase_if_last_ref);
        // Take this opportunity and remove the item.
        table_.Remove(h);
      } else {
        // Put the item back on the clock list, and don't free it.
        ClockInsert(h);
        last_reference = false;
      }
    }
    // If it was the last reference, then decrement the cache usage.
    if (last_reference) {
      assert(usage_ >= h->total_charge);
      usage_ -= h->total_charge;
      copy = *h;
    }
  }
Y
Yi Wu 已提交
463

G
Guido Tagliavini Ponce 已提交
464 465 466
  // Free the entry here outside of mutex for performance reasons.
  if (last_reference) {
    copy.FreeData();
467
  }
G
Guido Tagliavini Ponce 已提交
468
  return last_reference;
469 470
}

G
Guido Tagliavini Ponce 已提交
471 472 473
void ClockCacheShard::Erase(const Slice& key, uint32_t /* hash */) {
  ClockHandle copy;
  bool last_reference = false;
Y
Yi Wu 已提交
474
  {
475
    DMutexLock l(mutex_);
G
Guido Tagliavini Ponce 已提交
476 477 478 479 480 481 482 483 484 485 486 487 488
    ClockHandle* h = table_.Lookup(key);
    if (h != nullptr) {
      table_.Exclude(h);
      if (!h->HasRefs()) {
        // The entry is in Clock since it's in cache and has no external
        // references.
        ClockRemove(h);
        table_.Remove(h);
        assert(usage_ >= h->total_charge);
        usage_ -= h->total_charge;
        last_reference = true;
        copy = *h;
      }
Y
Yi Wu 已提交
489 490
    }
  }
G
Guido Tagliavini Ponce 已提交
491 492 493 494
  // Free the entry here outside of mutex for performance reasons.
  // last_reference will only be true if e != nullptr.
  if (last_reference) {
    copy.FreeData();
Y
Yi Wu 已提交
495
  }
G
Guido Tagliavini Ponce 已提交
496
}
Y
Yi Wu 已提交
497

G
Guido Tagliavini Ponce 已提交
498 499 500 501
size_t ClockCacheShard::GetUsage() const {
  DMutexLock l(mutex_);
  return usage_;
}
Y
Yi Wu 已提交
502

G
Guido Tagliavini Ponce 已提交
503 504 505 506 507
size_t ClockCacheShard::GetPinnedUsage() const {
  DMutexLock l(mutex_);
  assert(usage_ >= clock_usage_);
  return usage_ - clock_usage_;
}
Y
Yi Wu 已提交
508

G
Guido Tagliavini Ponce 已提交
509 510 511
std::string ClockCacheShard::GetPrintableOptions() const {
  return std::string{};
}
Y
Yi Wu 已提交
512

G
Guido Tagliavini Ponce 已提交
513 514 515 516
ClockCache::ClockCache(size_t capacity, size_t estimated_value_size,
                       int num_shard_bits, bool strict_capacity_limit,
                       CacheMetadataChargePolicy metadata_charge_policy)
    : ShardedCache(capacity, num_shard_bits, strict_capacity_limit) {
517 518
  assert(estimated_value_size > 0 ||
         metadata_charge_policy != kDontChargeCacheMetadata);
G
Guido Tagliavini Ponce 已提交
519 520 521 522 523 524 525 526
  num_shards_ = 1 << num_shard_bits;
  shards_ = reinterpret_cast<ClockCacheShard*>(
      port::cacheline_aligned_alloc(sizeof(ClockCacheShard) * num_shards_));
  size_t per_shard = (capacity + (num_shards_ - 1)) / num_shards_;
  for (int i = 0; i < num_shards_; i++) {
    new (&shards_[i])
        ClockCacheShard(per_shard, estimated_value_size, strict_capacity_limit,
                        metadata_charge_policy);
Y
Yi Wu 已提交
527
  }
G
Guido Tagliavini Ponce 已提交
528
}
Y
Yi Wu 已提交
529

G
Guido Tagliavini Ponce 已提交
530 531 532 533 534 535 536
ClockCache::~ClockCache() {
  if (shards_ != nullptr) {
    assert(num_shards_ > 0);
    for (int i = 0; i < num_shards_; i++) {
      shards_[i].~ClockCacheShard();
    }
    port::cacheline_aligned_free(shards_);
Y
Yi Wu 已提交
537
  }
G
Guido Tagliavini Ponce 已提交
538
}
Y
Yi Wu 已提交
539

G
Guido Tagliavini Ponce 已提交
540 541 542
CacheShard* ClockCache::GetShard(uint32_t shard) {
  return reinterpret_cast<CacheShard*>(&shards_[shard]);
}
Y
Yi Wu 已提交
543

G
Guido Tagliavini Ponce 已提交
544 545 546
const CacheShard* ClockCache::GetShard(uint32_t shard) const {
  return reinterpret_cast<CacheShard*>(&shards_[shard]);
}
Y
Yi Wu 已提交
547

G
Guido Tagliavini Ponce 已提交
548 549 550
void* ClockCache::Value(Handle* handle) {
  return reinterpret_cast<const ClockHandle*>(handle)->value;
}
551

G
Guido Tagliavini Ponce 已提交
552 553 554 555
size_t ClockCache::GetCharge(Handle* handle) const {
  CacheMetadataChargePolicy metadata_charge_policy = kDontChargeCacheMetadata;
  if (num_shards_ > 0) {
    metadata_charge_policy = shards_[0].metadata_charge_policy_;
556
  }
G
Guido Tagliavini Ponce 已提交
557 558 559
  return reinterpret_cast<const ClockHandle*>(handle)->GetCharge(
      metadata_charge_policy);
}
Y
Yi Wu 已提交
560

G
Guido Tagliavini Ponce 已提交
561 562 563 564
Cache::DeleterFn ClockCache::GetDeleter(Handle* handle) const {
  auto h = reinterpret_cast<const ClockHandle*>(handle);
  return h->deleter;
}
565

G
Guido Tagliavini Ponce 已提交
566 567 568
uint32_t ClockCache::GetHash(Handle* handle) const {
  return reinterpret_cast<const ClockHandle*>(handle)->hash;
}
Y
Yi Wu 已提交
569

G
Guido Tagliavini Ponce 已提交
570 571 572 573 574 575 576 577 578
void ClockCache::DisownData() {
  // Leak data only if that won't generate an ASAN/valgrind warning.
  if (!kMustFreeHeapAllocations) {
    shards_ = nullptr;
    num_shards_ = 0;
  }
}

}  // namespace clock_cache
Y
Yi Wu 已提交
579

580
std::shared_ptr<Cache> NewClockCache(
G
Guido Tagliavini Ponce 已提交
581 582
    size_t capacity, size_t estimated_value_size, int num_shard_bits,
    bool strict_capacity_limit,
583
    CacheMetadataChargePolicy metadata_charge_policy) {
G
Guido Tagliavini Ponce 已提交
584 585 586
  if (num_shard_bits >= 20) {
    return nullptr;  // The cache cannot be sharded into too many fine pieces.
  }
587 588 589
  if (num_shard_bits < 0) {
    num_shard_bits = GetDefaultCacheShardBits(capacity);
  }
G
Guido Tagliavini Ponce 已提交
590 591 592
  return std::make_shared<clock_cache::ClockCache>(
      capacity, estimated_value_size, num_shard_bits, strict_capacity_limit,
      metadata_charge_policy);
Y
Yi Wu 已提交
593 594
}

595
}  // namespace ROCKSDB_NAMESPACE