1
#pragma once
2

            
3
#include <functional>
4
#include <memory>
5
#include <vector>
6

            
7
#include "envoy/thread/thread.h"
8

            
9
#include "source/common/common/logger.h"
10

            
11
#include "absl/base/thread_annotations.h"
12
#include "absl/container/flat_hash_set.h"
13
#include "absl/synchronization/mutex.h"
14

            
15
namespace Envoy {
16
namespace Extensions {
17
namespace HttpFilters {
18
namespace Cache {
19
namespace FileSystemHttpCache {
20

            
21
struct CacheShared;
22

            
23
/**
24
 * A class which controls a thread on which cache evictions for all instances
25
 * of FileSystemHttpCache are performed.
26
 *
27
 * The instance of CacheEvictionThread is owned by the `CacheSingleton`, which is
28
 * created only when a first cache instance is created, and destroyed only when
29
 * all cache instances have been destroyed.
30
 *
31
 * The class is final, as the thread may still be running during the destructor
32
 * - this is fine so long as no class members or vtable entries have yet been
33
 * destroyed, which can be guaranteed if the class is final.
34
 *
35
 * See DESIGN.md for more details of the eviction process.
36
 **/
37
class CacheEvictionThread final : public Logger::Loggable<Logger::Id::cache_filter> {
38
public:
39
  CacheEvictionThread(Thread::ThreadFactory& thread_factory);
40

            
41
  /**
42
   * The destructor may block until the cache eviction thread is joined.
43
   */
44
  ~CacheEvictionThread();
45

            
46
  /**
47
   * Adds the given cache to the caches that may be evicted from.
48
   * @param cache an unowned reference to the cache in question.
49
   */
50
  void addCache(std::shared_ptr<CacheShared> cache);
51

            
52
  /**
53
   * Removes the given cache from the caches that may be evicted from.
54
   * @param cache an unowned reference to the cache in question.
55
   */
56
  void removeCache(std::shared_ptr<CacheShared>& cache);
57

            
58
  /**
59
   * Signals the cache eviction thread that it's time to check the current cache
60
   * state against any configured limits, and perform eviction if necessary.
61
   */
62
  void signal();
63

            
64
private:
65
  /**
66
   * The function that runs on the thread.
67
   *
68
   * This thread is expected to spend most of its time blocked, waiting for either
69
   * `signal` or `terminate` to be called, or a configured period.
70
   *
71
   * When unblocked, the thread will exit if terminating_ is set.
72
   *
73
   * Otherwise, each cache instance's `needsEviction` function is called, in an
74
   * arbitrary order, and, if that returns true, the `evict` function is also called.
75
   *
76
   * If `signal` is called during the eviction process, the eviction
77
   * cycle may run a second time after completion, depending on configured
78
   * constraints.
79
   */
80
  void work();
81

            
82
  /**
83
   * @return false if terminating, true if `signalled_` is true or the run-again period
84
   * has passed.
85
   */
86
  bool waitForSignal();
87

            
88
  /**
89
   * Notifies the thread to terminate. If it is currently evicting, it will
90
   * complete the current eviction cycle before exiting. If it is currently
91
   * idle, it will exit immediately (terminate does not wait for exiting
92
   * to be complete).
93
   */
94
  void terminate();
95

            
96
  // These two mutexes are never held at the same time. We signify this by requiring
97
  // that both be 'acquired before' the other, since there is no exclusion annotation.
98
  absl::Mutex mu_ ABSL_ACQUIRED_BEFORE(cache_mu_);
99
  bool signalled_ ABSL_GUARDED_BY(mu_) = false;
100
  bool terminating_ ABSL_GUARDED_BY(mu_) = false;
101

            
102
  absl::Mutex cache_mu_ ABSL_ACQUIRED_BEFORE(mu_);
103
  // We must store the caches as unowned references so they can be destroyed
104
  // during config changes - that destruction is the only signal that a cache
105
  // instance should be removed.
106
  absl::flat_hash_set<std::shared_ptr<CacheShared>> caches_ ABSL_GUARDED_BY(cache_mu_);
107

            
108
  // Allow test access to waitForIdle for synchronization.
109
  friend class FileSystemCacheTestContext;
110
  bool idle_ ABSL_GUARDED_BY(mu_) = false;
111
  void waitForIdle();
112

            
113
  // It is important that thread_ be last, as the new thread runs with 'this' and
114
  // may access any other members. If thread_ is not last, there can be a race between
115
  // that thread and the initialization of other members.
116
  Thread::ThreadPtr thread_;
117
};
118

            
119
} // namespace FileSystemHttpCache
120
} // namespace Cache
121
} // namespace HttpFilters
122
} // namespace Extensions
123
} // namespace Envoy