1/*
2Copyright (c) 2012, Oracle and/or its affiliates. All Rights Reserved.
3This program is free software; you can redistribute it and/or modify it under
4the terms of the GNU General Public License as published by the Free Software
5Foundation; version 2 of the License.
6This program is distributed in the hope that it will be useful, but WITHOUT
7ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9You should have received a copy of the GNU General Public License along with
10this program; if not, write to the Free Software Foundation, Inc.,
1151 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
12*****************************************************************************/
13
14/**************************************************//**
15@file include/ut0counter.h
16Counter utility class
17Created 2012/04/12 by Sunny Bains
18*******************************************************/
19
20#ifndef UT0COUNTER_H
21#define UT0COUNTER_H
22
23#include <string.h>
24
25/** CPU cache line size */
26#define CACHE_LINE_SIZE 64
27
28/** Default number of slots to use in ib_counter_t */
29#define IB_N_SLOTS 64
30
31#ifdef __WIN__
32#define get_curr_thread_id() GetCurrentThreadId()
33#else
34#define get_curr_thread_id() pthread_self()
35#endif
36
37#define UT_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
38
39/** Get the offset into the counter array. */
40template <typename Type, int N>
41struct generic_indexer_t {
42 /** Default constructor/destructor should be OK. */
43
44 /** @return offset within m_counter */
45 size_t offset(size_t index) const {
46 return(((index % N) + 1) * (CACHE_LINE_SIZE / sizeof(Type)));
47 }
48};
49
50#ifdef HAVE_SCHED_GETCPU
51//#include <utmpx.h> // Including this causes problems with EMPTY symbol
52#include <sched.h> // Include this instead
53/** Use the cpu id to index into the counter array. If it fails then
54use the thread id. */
55template <typename Type, int N>
56struct get_sched_indexer_t : public generic_indexer_t<Type, N> {
57 /** Default constructor/destructor should be OK. */
58
59 /* @return result from sched_getcpu(), the thread id if it fails. */
60 size_t get_rnd_index() const {
61
62 size_t cpu = sched_getcpu();
63 if (cpu == (size_t) -1) {
64 cpu = get_curr_thread_id();
65 }
66
67 return(cpu);
68 }
69};
70#endif /* HAVE_SCHED_GETCPU */
71
72/** Use the thread id to index into the counter array. */
73template <typename Type, int N>
74struct thread_id_indexer_t : public generic_indexer_t<Type, N> {
75 /** Default constructor/destructor should are OK. */
76
77 /* @return a random number, currently we use the thread id. Where
78 thread id is represented as a pointer, it may not work as
79 effectively. */
80 size_t get_rnd_index() const {
81 return (size_t)get_curr_thread_id();
82 }
83};
84
85/** For counters wher N=1 */
86template <typename Type, int N=1>
87struct single_indexer_t {
88 /** Default constructor/destructor should are OK. */
89
90 /** @return offset within m_counter */
91 size_t offset(size_t index) const {
92 DBUG_ASSERT(N == 1);
93 return((CACHE_LINE_SIZE / sizeof(Type)));
94 }
95
96 /* @return 1 */
97 size_t get_rnd_index() const {
98 DBUG_ASSERT(N == 1);
99 return(1);
100 }
101};
102
103/** Class for using fuzzy counters. The counter is not protected by any
104mutex and the results are not guaranteed to be 100% accurate but close
105enough. Creates an array of counters and separates each element by the
106CACHE_LINE_SIZE bytes */
107template <
108 typename Type,
109 int N = IB_N_SLOTS,
110 template<typename, int> class Indexer = thread_id_indexer_t>
111class ib_counter_t {
112public:
113 ib_counter_t() { memset(m_counter, 0x0, sizeof(m_counter)); }
114
115 ~ib_counter_t()
116 {
117 DBUG_ASSERT(validate());
118 }
119
120 bool validate() {
121#ifdef UNIV_DEBUG
122 size_t n = (CACHE_LINE_SIZE / sizeof(Type));
123
124 /* Check that we aren't writing outside our defined bounds. */
125 for (size_t i = 0; i < UT_ARRAY_SIZE(m_counter); i += n) {
126 for (size_t j = 1; j < n - 1; ++j) {
127 DBUG_ASSERT(m_counter[i + j] == 0);
128 }
129 }
130#endif /* UNIV_DEBUG */
131 return(true);
132 }
133
134 /** If you can't use a good index id. Increment by 1. */
135 void inc() { add(1); }
136
137 /** If you can't use a good index id.
138 * @param n - is the amount to increment */
139 void add(Type n) {
140 size_t i = m_policy.offset(m_policy.get_rnd_index());
141
142 DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter));
143
144 m_counter[i] += n;
145 }
146
147 /** Use this if you can use a unique indentifier, saves a
148 call to get_rnd_index().
149 @param i - index into a slot
150 @param n - amount to increment */
151 void add(size_t index, Type n) {
152 size_t i = m_policy.offset(index);
153
154 DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter));
155
156 m_counter[i] += n;
157 }
158
159 /** If you can't use a good index id. Decrement by 1. */
160 void dec() { sub(1); }
161
162 /** If you can't use a good index id.
163 * @param - n is the amount to decrement */
164 void sub(Type n) {
165 size_t i = m_policy.offset(m_policy.get_rnd_index());
166
167 DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter));
168
169 m_counter[i] -= n;
170 }
171
172 /** Use this if you can use a unique indentifier, saves a
173 call to get_rnd_index().
174 @param i - index into a slot
175 @param n - amount to decrement */
176 void sub(size_t index, Type n) {
177 size_t i = m_policy.offset(index);
178
179 DBUG_ASSERT(i < UT_ARRAY_SIZE(m_counter));
180
181 m_counter[i] -= n;
182 }
183
184 /* @return total value - not 100% accurate, since it is not atomic. */
185 operator Type() const {
186 Type total = 0;
187
188 for (size_t i = 0; i < N; ++i) {
189 total += m_counter[m_policy.offset(i)];
190 }
191
192 return(total);
193 }
194
195private:
196 /** Indexer into the array */
197 Indexer<Type, N>m_policy;
198
199 /** Slot 0 is unused. */
200 Type m_counter[(N + 1) * (CACHE_LINE_SIZE / sizeof(Type))];
201};
202
203#endif /* UT0COUNTER_H */
204