@@ -40,79 +40,121 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/
40
40
namespace tpool
41
41
{
42
42
43
- enum cache_notification_mode
44
- {
45
- NOTIFY_ONE,
46
- NOTIFY_ALL
47
- };
48
-
49
43
/* *
50
44
Generic "pointer" cache of a fixed size
51
45
with fast put/get operations.
52
46
53
- Compared to STL containers, is faster/does not
54
- do allocations. However, put() operation will wait
55
- if there is no free items.
47
+ Compared to STL containers,e.g stack or queue
48
+ is faster/does not do allocations.
49
+
50
+ However, get() operation will wait if there is no free items.
51
+
52
+ We assume that put() will only put back the elements that
53
+ were retrieved previously with get().
56
54
*/
57
55
template <typename T> class cache
58
56
{
57
+ /* * Protects updates of m_pos and m_cache members */
59
58
std::mutex m_mtx;
59
+
60
+ /* *
61
+ Notify waiting threads about "cache full" or "cache not empty" conditions
62
+ @see get() and wait()
63
+ */
60
64
std::condition_variable m_cv;
61
- std::vector<T> m_base;
65
+
66
+ /* * Cached items vector.Does not change after construction */
67
+ std::vector<T> m_base;
68
+
69
+ /* *
70
+ Pointers to cached items. Protected by m_mtx. Does not grow after
71
+ construction. Elements in position [0,m_pos-1] are "borrowed",
72
+ elements in position [m_pos,capacity()-1] are "free"
73
+ */
62
74
std::vector<T*> m_cache;
63
- cache_notification_mode m_notification_mode;
75
+
76
+ /* * Number of threads waiting for "cache full" condition (s. wait())
77
+ Protected by m_mtx */
64
78
int m_waiters;
65
79
80
+ /* * Current cache size. Protected by m_mtx*/
81
+ size_t m_pos;
82
+
83
+ private:
84
+
85
+ inline size_t capacity ()
86
+ {
87
+ return m_base.size ();
88
+ }
89
+
90
+ /* *
91
+ @return true if cache is full (no items are borrowed)
92
+ */
66
93
bool is_full ()
67
94
{
68
- return m_cache.size () == m_base.size ();
95
+ return m_pos == 0 ;
96
+ }
97
+
98
+ /* *
99
+ @return true if cache is empty (all items are borrowed)
100
+ */
101
+ bool is_empty ()
102
+ {
103
+ return m_pos == capacity ();
69
104
}
70
105
71
106
public:
72
- cache (size_t count, cache_notification_mode mode= tpool::cache_notification_mode::NOTIFY_ALL):
73
- m_mtx (), m_cv(), m_base(count),m_cache(count), m_notification_mode(mode),m_waiters()
107
+ /* *
108
+ Constructor
109
+ @param size - maximum number of items in cache
110
+ */
111
+ cache (size_t size) : m_mtx(), m_cv(), m_base(size), m_cache(size),
112
+ m_waiters (), m_pos(0 )
74
113
{
75
- for (size_t i = 0 ; i < count ; i++)
76
- m_cache[i]=&m_base[i];
114
+ for (size_t i= 0 ; i < size ; i++)
115
+ m_cache[i]= &m_base[i];
77
116
}
78
117
79
- T* get (bool blocking=true )
118
+ /* *
119
+ Retrieve an item from cache. Waits for free item, if cache is
120
+ currently empty.
121
+ @return borrowed item
122
+ */
123
+ T* get ()
80
124
{
81
125
std::unique_lock<std::mutex> lk (m_mtx);
82
- if (blocking)
83
- {
84
- while (m_cache.empty ())
85
- m_cv.wait (lk);
86
- }
87
- else
88
- {
89
- if (m_cache.empty ())
90
- return nullptr ;
91
- }
92
- T* ret = m_cache.back ();
93
- m_cache.pop_back ();
94
- return ret;
126
+ while (is_empty ())
127
+ m_cv.wait (lk);
128
+ assert (m_pos < capacity ());
129
+ // return last element
130
+ return m_cache[m_pos++];
95
131
}
96
132
97
-
133
+ /* *
134
+ Put back an item to cache.
135
+ @param item - item to put back
136
+ */
98
137
void put (T *ele)
99
138
{
100
139
std::unique_lock<std::mutex> lk (m_mtx);
101
- m_cache.push_back (ele);
102
- if (m_notification_mode == NOTIFY_ONE)
103
- m_cv.notify_one ();
104
- else if (m_cache.size () == 1 )
105
- m_cv.notify_all (); // Signal cache is not empty
106
- else if (m_waiters && is_full ())
107
- m_cv.notify_all (); // Signal cache is full
140
+ assert (!is_full ());
141
+ // put element to the logical end of the array
142
+ m_cache[--m_pos] = ele;
143
+
144
+ /* Notify waiters when the cache becomes
145
+ not empty, or when it becomes full */
146
+ if (m_pos == 1 || (m_waiters && is_full ()))
147
+ m_cv.notify_all ();
108
148
}
109
149
150
+ /* * Check if pointer represents cached element */
110
151
bool contains (T* ele)
111
152
{
112
- return ele >= &m_base[0 ] && ele <= &m_base[m_base.size () -1 ];
153
+ // No locking required, m_base does not change after construction.
154
+ return ele >= &m_base[0 ] && ele <= &m_base[capacity () - 1 ];
113
155
}
114
156
115
- /* Wait until cache is full.*/
157
+ /* * Wait until cache is full.*/
116
158
void wait ()
117
159
{
118
160
std::unique_lock<std::mutex> lk (m_mtx);
@@ -122,9 +164,13 @@ template<typename T> class cache
122
164
m_waiters--;
123
165
}
124
166
125
- TPOOL_SUPPRESS_TSAN size_t size ()
167
+ /* *
168
+ @return approximate number of "borrowed" items.
169
+ A "dirty" read, not used in any critical functionality.
170
+ */
171
+ TPOOL_SUPPRESS_TSAN size_t pos ()
126
172
{
127
- return m_cache. size () ;
173
+ return m_pos ;
128
174
}
129
175
};
130
176
0 commit comments