1 | /* |
2 | Copyright (c) 2012, Oracle and/or its affiliates. All Rights Reserved. |
3 | This program is free software; you can redistribute it and/or modify it under |
4 | the terms of the GNU General Public License as published by the Free Software |
5 | Foundation; version 2 of the License. |
6 | This program is distributed in the hope that it will be useful, but WITHOUT |
7 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
8 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
9 | You should have received a copy of the GNU General Public License along with |
10 | this program; if not, write to the Free Software Foundation, Inc., |
11 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
12 | *****************************************************************************/ |
13 | |
14 | /**************************************************//** |
15 | @file include/ut0counter.h |
16 | Counter utility class |
17 | Created 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. */ |
40 | template <typename Type, int N> |
41 | struct 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 |
54 | use the thread id. */ |
55 | template <typename Type, int N> |
56 | struct 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. */ |
73 | template <typename Type, int N> |
74 | struct 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 */ |
86 | template <typename Type, int N=1> |
87 | struct 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 |
104 | mutex and the results are not guaranteed to be 100% accurate but close |
105 | enough. Creates an array of counters and separates each element by the |
106 | CACHE_LINE_SIZE bytes */ |
107 | template < |
108 | typename Type, |
109 | int N = IB_N_SLOTS, |
110 | template<typename, int> class Indexer = thread_id_indexer_t> |
111 | class ib_counter_t { |
112 | public: |
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 | |
195 | private: |
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 | |