summaryrefslogtreecommitdiffstats
path: root/src/common/simple_cache.hpp
blob: 07d19a731b1fc1ea5c8a73a34819ee188821582d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- 
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software 
 * Foundation.  See file COPYING.
 * 
 */

#ifndef CEPH_SIMPLECACHE_H
#define CEPH_SIMPLECACHE_H

#include <list>
#include <map>
#include <unordered_map>
#include <utility>

#include "common/ceph_mutex.h"

template <class K, class V, class C = std::less<K>, class H = std::hash<K> >
class SimpleLRU {
  ceph::mutex lock = ceph::make_mutex("SimpleLRU::lock");
  size_t max_size;
  size_t max_bytes = 0;
  size_t total_bytes = 0;
  std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator, H> contents;
  std::list<std::pair<K, V> > lru;
  std::map<K, V, C> pinned;

  void trim_cache() {
    while (contents.size() > max_size) {
      contents.erase(lru.back().first);
      lru.pop_back();
    }
  }

  void trim_cache_bytes() {
    while(total_bytes > max_bytes) {
      total_bytes -= lru.back().second.length();
      contents.erase(lru.back().first);
      lru.pop_back();
    }
  }

  void _add(K key, V&& value) {
    lru.emplace_front(key, std::move(value)); // can't move key because we access it below
    contents[key] = lru.begin();
    trim_cache();
  }

  void _add_bytes(K key, V&& value) {
    lru.emplace_front(key, std::move(value)); // can't move key because we access it below
    contents[key] = lru.begin();
    trim_cache_bytes();
  }

public:
  SimpleLRU(size_t max_size) : max_size(max_size) {
    contents.rehash(max_size);
  }

  void pin(K key, V val) {
    std::lock_guard l(lock);
    pinned.emplace(std::move(key), std::move(val));
  }

  void clear_pinned(K e) {
    std::lock_guard l(lock);
    for (auto i = pinned.begin();
	 i != pinned.end() && i->first <= e;
	 pinned.erase(i++)) {
      auto iter = contents.find(i->first);
      if (iter == contents.end())
	_add(i->first, std::move(i->second));
      else
	lru.splice(lru.begin(), lru, iter->second);
    }
  }

  void clear(K key) {
    std::lock_guard l(lock);
    auto i = contents.find(key);
    if (i == contents.end())
      return;
    total_bytes -= i->second->second.length();
    lru.erase(i->second);
    contents.erase(i);
  }

  void set_size(size_t new_size) {
    std::lock_guard l(lock);
    max_size = new_size;
    trim_cache();
  }

  size_t get_size() {
    std::lock_guard l(lock);
    return contents.size();
  }

  void set_bytes(size_t num_bytes) {
    std::lock_guard l(lock);
    max_bytes = num_bytes;
    trim_cache_bytes();
  }

  size_t get_bytes() {
    std::lock_guard l(lock);
    return total_bytes;
  }

  bool lookup(K key, V *out) {
    std::lock_guard l(lock);
    auto i = contents.find(key);
    if (i != contents.end()) {
      *out = i->second->second;
      lru.splice(lru.begin(), lru, i->second);
      return true;
    }
    auto i_pinned = pinned.find(key);
    if (i_pinned != pinned.end()) {
      *out = i_pinned->second;
      return true;
    }
    return false;
  }

  void add(K key, V value) {
    std::lock_guard l(lock);
    _add(std::move(key), std::move(value));
  }

  void add_bytes(K key, V value) {
    std::lock_guard l(lock);
    total_bytes += value.length();
    _add_bytes(std::move(key), std::move(value));
  }
};

#endif