Skip to content

Commit a3fd9e6

Browse files
committed
MDEV-29367 Refactor tpool::cache
Removed use std::vector's ba push_back(), pop_back() to make it more obvious that memory in the vectors won't be reallocated. Also, "borrowed" elements can be debugged a little better now, they are put into the start of the m_cache vector.
1 parent 720fa05 commit a3fd9e6

File tree

2 files changed

+89
-43
lines changed

2 files changed

+89
-43
lines changed

storage/innobase/os/os0file.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ class io_slots
121121

122122
size_t pending_io_count()
123123
{
124-
return (size_t)m_max_aio - m_cache.size();
124+
return m_cache.pos();
125125
}
126126

127127
tpool::task_group* get_task_group()

tpool/tpool_structs.h

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -40,79 +40,121 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 - 1301 USA*/
4040
namespace tpool
4141
{
4242

43-
enum cache_notification_mode
44-
{
45-
NOTIFY_ONE,
46-
NOTIFY_ALL
47-
};
48-
4943
/**
5044
Generic "pointer" cache of a fixed size
5145
with fast put/get operations.
5246
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().
5654
*/
5755
template<typename T> class cache
5856
{
57+
/** Protects updates of m_pos and m_cache members */
5958
std::mutex m_mtx;
59+
60+
/**
61+
Notify waiting threads about "cache full" or "cache not empty" conditions
62+
@see get() and wait()
63+
*/
6064
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+
*/
6274
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 */
6478
int m_waiters;
6579

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+
*/
6693
bool is_full()
6794
{
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();
69104
}
70105

71106
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)
74113
{
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];
77116
}
78117

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()
80124
{
81125
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++];
95131
}
96132

97-
133+
/**
134+
Put back an item to cache.
135+
@param item - item to put back
136+
*/
98137
void put(T *ele)
99138
{
100139
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();
108148
}
109149

150+
/** Check if pointer represents cached element */
110151
bool contains(T* ele)
111152
{
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];
113155
}
114156

115-
/* Wait until cache is full.*/
157+
/** Wait until cache is full.*/
116158
void wait()
117159
{
118160
std::unique_lock<std::mutex> lk(m_mtx);
@@ -122,9 +164,13 @@ template<typename T> class cache
122164
m_waiters--;
123165
}
124166

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()
126172
{
127-
return m_cache.size();
173+
return m_pos;
128174
}
129175
};
130176

0 commit comments

Comments
 (0)