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 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
6 // Use of this source code is governed by a BSD-style license that can be
7 // found in the LICENSE file. See the AUTHORS file for names of contributors.
8 //
9 
10 #include "util/compression_context_cache.h"
11 
12 #include "util/compression.h"
13 #include "util/core_local.h"
14 
15 #include <atomic>
16 
17 namespace ROCKSDB_NAMESPACE {
18 namespace compression_cache {
19 
20 void* const SentinelValue = nullptr;
21 // Cache ZSTD uncompression contexts for reads
22 // if needed we can add ZSTD compression context caching
23 // which is currently is not done since BlockBasedTableBuilder
24 // simply creates one compression context per new SST file.
25 struct ZSTDCachedData {
26   // We choose to cache the below structure instead of a ptr
27   // because we want to avoid a) native types leak b) make
28   // cache use transparent for the user
29   ZSTDUncompressCachedData uncomp_cached_data_;
30   std::atomic<void*> zstd_uncomp_sentinel_;
31 
32   char
33       padding[(CACHE_LINE_SIZE -
34                (sizeof(ZSTDUncompressCachedData) + sizeof(std::atomic<void*>)) %
35                    CACHE_LINE_SIZE)];  // unused padding field
36 
ZSTDCachedDataROCKSDB_NAMESPACE::compression_cache::ZSTDCachedData37   ZSTDCachedData() : zstd_uncomp_sentinel_(&uncomp_cached_data_) {}
38   ZSTDCachedData(const ZSTDCachedData&) = delete;
39   ZSTDCachedData& operator=(const ZSTDCachedData&) = delete;
40 
GetUncompressDataROCKSDB_NAMESPACE::compression_cache::ZSTDCachedData41   ZSTDUncompressCachedData GetUncompressData(int64_t idx) {
42     ZSTDUncompressCachedData result;
43     void* expected = &uncomp_cached_data_;
44     if (zstd_uncomp_sentinel_.compare_exchange_strong(expected,
45                                                       SentinelValue)) {
46       uncomp_cached_data_.CreateIfNeeded();
47       result.InitFromCache(uncomp_cached_data_, idx);
48     } else {
49       // Creates one time use data
50       result.CreateIfNeeded();
51     }
52     return result;
53   }
54   // Return the entry back into circulation
55   // This is executed only when we successfully obtained
56   // in the first place
ReturnUncompressDataROCKSDB_NAMESPACE::compression_cache::ZSTDCachedData57   void ReturnUncompressData() {
58     if (zstd_uncomp_sentinel_.exchange(&uncomp_cached_data_) != SentinelValue) {
59       // Means we are returning while not having it acquired.
60       assert(false);
61     }
62   }
63 };
64 static_assert(sizeof(ZSTDCachedData) % CACHE_LINE_SIZE == 0,
65               "Expected CACHE_LINE_SIZE alignment");
66 }  // namespace compression_cache
67 
68 using namespace compression_cache;
69 
70 class CompressionContextCache::Rep {
71  public:
Rep()72   Rep() {}
GetZSTDUncompressData()73   ZSTDUncompressCachedData GetZSTDUncompressData() {
74     auto p = per_core_uncompr_.AccessElementAndIndex();
75     int64_t idx = static_cast<int64_t>(p.second);
76     return p.first->GetUncompressData(idx);
77   }
ReturnZSTDUncompressData(int64_t idx)78   void ReturnZSTDUncompressData(int64_t idx) {
79     assert(idx >= 0);
80     auto* cn = per_core_uncompr_.AccessAtCore(static_cast<size_t>(idx));
81     cn->ReturnUncompressData();
82   }
83 
84  private:
85   CoreLocalArray<ZSTDCachedData> per_core_uncompr_;
86 };
87 
CompressionContextCache()88 CompressionContextCache::CompressionContextCache() : rep_(new Rep()) {}
89 
Instance()90 CompressionContextCache* CompressionContextCache::Instance() {
91   static CompressionContextCache instance;
92   return &instance;
93 }
94 
InitSingleton()95 void CompressionContextCache::InitSingleton() { Instance(); }
96 
97 ZSTDUncompressCachedData
GetCachedZSTDUncompressData()98 CompressionContextCache::GetCachedZSTDUncompressData() {
99   return rep_->GetZSTDUncompressData();
100 }
101 
ReturnCachedZSTDUncompressData(int64_t idx)102 void CompressionContextCache::ReturnCachedZSTDUncompressData(int64_t idx) {
103   rep_->ReturnZSTDUncompressData(idx);
104 }
105 
~CompressionContextCache()106 CompressionContextCache::~CompressionContextCache() { delete rep_; }
107 
108 }  // namespace ROCKSDB_NAMESPACE
109