-
Notifications
You must be signed in to change notification settings - Fork 2
/
fd_data_map.hh
302 lines (294 loc) · 9.97 KB
/
fd_data_map.hh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
/*
* Copyright (c) 2016 Zhao DAI <[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or any
* later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see accompanying file LICENSE.txt
* or <http://www.gnu.org/licenses/>.
*/
/**
* @file
* @brief Containers for fd (file descriptor) related data.
* @author Zhao DAI
*/
#ifndef DOZERG_FD_DATA_MAP_H_20130228
#define DOZERG_FD_DATA_MAP_H_20130228
#include <vector>
#include <cassert>
#include "mutex.hh"
#include "shared_ptr.hh"
NS_SERVER_BEGIN
/**
* @brief A hash table for fd (file descriptor) related data.
* Key @em MUST be file descriptors, which are non-negative and of type @c int. In fact the file
* descriptor acts as an index to an underlying container of values. Because the number of files a
* process can open is limited, e.g. 1024 (it is modifiable), there's no chance for the underlying
* container to grow unexpectedly. So do @em NOT use CFdMap as a generic @c int to value hash table.
* @n The underlying container is @c vector by default, but you can specify it as you like.
* @tparam T Value type
* @tparam Container Container type, default to @c std::vector<T>
* @note CFdMap is @em NOT thread safe.
* @sa CFdDataMap
*/
template<class T, class Container = std::vector<T> >
struct CFdMap
{
typedef Container container_type;
typedef T value_type;
typedef T & reference;
typedef const T & const_reference;
/**
* @brief Initialize this object.
* @c capacity is used as a hint to the number of key/value pairs this object wants to hold. But
* CFdMap is free to expand as needed.
* @param capacity Initial reserved room for key/value pairs
*/
explicit CFdMap(size_t capacity = 100){map_.reserve(capacity);}
/**
* @brief Get current capacity.
* @return Current capacity
*/
size_t capacity() const{return map_.capacity();}
/**
* @brief Set current capacity.
* @param sz New capacity
*/
void capacity(size_t sz){map_.reserve(sz);}
/**
* @brief Get value for an fd.
* @param fd A file descriptor
* @return Writable reference to the value for @c fd
*/
reference operator [](int fd){
assert(fd >= 0);
if(size_t(fd) >= map_.size())
map_.resize(fd + 1);
return map_[fd];
}
/**
* @brief Get value for an fd.
* @param fd A file descriptor
* @return Readonly reference to the value for @c fd
*/
const_reference operator [](int fd) const{
//use value_type() to fix compile error for POD
static const value_type kDef = value_type();
if(fd >= 0 && size_t(fd) < map_.size())
return map_[fd];
return kDef;
}
/**
* @brief Clear all key/value pairs.
*/
void clear(){map_.clear();}
private:
container_type map_;
};
/**
* @brief Thread-safe hash table for fd (file descriptor) related data.
* Key @em MUST be file descriptors, which are non-negative and of type @c int. In fact the file
* descriptor acts as an index to an underlying @c vector of values. Because the number of files a
* process can open is limited, e.g. 1024 (it is modifiable), there's no chance for the underlying
* @c vector to grow unexpectedly. So do @em NOT use CFdDataMap as a generic @c int to value hash
* table.
* @n Value can be of any type, even non-copyable types. Usually you want to keep a session for each
* socket fd, and the session is non-copyable. CFdDataMap uses smart pointer to hold a value, there
* are several advantages. Firstly, it avoids copying issue as mentioned; secondly, it simplifies
* lifetime control for each value among multiple threads; And finally, it is efficient for the
* underlying @c vector to expand.
* @n CFdDataMap is thread safe and implemented on top of CFdMap. You can specify any lock type you
* want, as long as it cooperates with CGuard.
* @tparam T Value type
* @tparam LockT Lock type, default to CSpinLock
* @sa CFdMap
*/
template<class T, class LockT = CSpinLock>
class CFdDataMap
{
public:
typedef T value_type;
typedef CSharedPtr<value_type> pointer;
private:
typedef CFdMap<pointer> __Map;
typedef LockT lock_type;
typedef CGuard<lock_type> guard_type;
public:
/**
* @brief Initialize this object.
* @c capacity is used as a hint to the number of key/value pairs this object wants to hold. But
* CFdDataMap is free to expand as needed.
* @param capacity Initial reserved room for key/value pairs
*/
explicit CFdDataMap(size_t capacity = 100)
: map_(capacity)
, sz_(0)
{}
/**
* @brief Get number of key/value pairs.
* @return Number of key/value pairs
*/
size_t size() const{return sz_;}
/**
* @brief Get current capacity.
* @return Current capacity
*/
size_t capacity() const{
guard_type g(lock_);
return map_.capacity();
}
/**
* @brief Set current capacity.
* @param c New capacity
*/
void capacity(size_t c){
guard_type g(lock_);
map_.capacity(c);
}
/**
* @brief Set value for an fd.
* @param[in] fd A file descriptor
* @param[in] data New value for @c fd
* @param[out] old A smart pointer object to obtain old value for @c fd
*/
void setData(int fd, const pointer & data, pointer * old = NULL){
if(fd < 0)
return;
guard_type g(lock_);
setAux(fd, data, old);
}
/**
* @brief Get value for an fd.
* @param fd A file descriptor
* @return A smart pointer object hold the value for @c fd
*/
pointer getData(int fd) const{
if(fd < 0)
return pointer();
guard_type g(lock_);
return map_[fd];
}
/**
* @brief Get value for an fd.
* @param[in] fd A file descriptor
* @param[out] data A smart pointer object to obtain the value for @c fd
*/
void getData(int fd, pointer * data) const{
if(NULL == data || fd < 0)
return;
guard_type g(lock_);
*data = map_[fd];
}
/**
* @brief Get values for some file descriptors.
* This function saves many lock/unlock operations.
* @n Example code:
* @code{.cpp}
* CFdDataMap<Data> fdmap;
*
* vector<int> fds;
* vector<CFdDataMap<Data>::pointer> values(fds.size());
*
* fdmap.getData(fds.begin(), fds.end(), values.begin());
* @endcode
* @param[in] first Begin iterator for a collection of file descriptors
* @param[in] last End iterator for a collection of file descriptors
* @param[out] dstFirst Begin iterator for a collection of smart pointers to receive the values
* of file descriptors
* @note The size and validation of destination collection is guaranteed by the user.
*/
template<class ForwardIter, class OutputIter>
void getData(ForwardIter first, ForwardIter last, OutputIter dstFirst) const{
guard_type g(lock_);
for(int fd = -1;first != last;++first, ++dstFirst){
fd = *first;
if(fd >= 0)
*dstFirst = map_[fd];
}
}
/**
* @brief Remove value for an fd.
* @param[in] fd A file descriptor
* @param[out] old A smart pointer object to obtain old value for @c fd, or @c NULL if not
* interested
*/
void clearData(int fd, pointer * old = NULL){
if(fd < 0)
return;
guard_type g(lock_);
setAux(fd, pointer(), old);
}
/**
* @brief Remove values for some file descriptors.
* @param[in] first Begin iterator for a collection of file descriptors
* @param[in] last End iterator for a collection of file descriptors
*/
template<class ForwardIter>
void clearData(ForwardIter first, ForwardIter last){
guard_type g(lock_);
for(int fd = -1;first != last;++first){
fd = *first;
if(fd >= 0)
setAux(fd, pointer(), NULL);
}
}
/**
* @brief Remove values for some file descriptors.
* @param[in] first Begin iterator for a collection of file descriptors
* @param[in] last End iterator for a collection of file descriptors
* @param[out] dstFirst Begin iterator for a collection of smart pointers to receive old values
* @note The size and validation of destination collection is guaranteed by the user.
*/
template<class ForwardIter, class OutputIter>
void clearData(ForwardIter first, ForwardIter last, OutputIter dstFirst){
guard_type g(lock_);
for(int fd = -1;first != last;++first, ++dstFirst){
fd = *first;
if(fd >= 0)
setAux(fd, pointer(), &*dstFirst);
}
}
/**
* @brief Clear all key/value pairs.
*/
void clear(){
guard_type g(lock_);
map_.clear();
sz_ = 0;
}
private:
//set *old = map_[fd], then set map_[fd] = data
//Note: old and &data may be equal
void setAux(int fd, const pointer & data, pointer * old){
assert(fd >= 0);
if(fd < int(map_.capacity())){
pointer & cur = map_[fd];
sz_ += (data ? 1 : 0) - (cur ? 1 : 0);
if(NULL != old)
old->swap(cur);
if(&data != old)
cur = data;
}else{
if(data){
map_[fd] = data;
++sz_;
}
if(NULL != old)
old->reset();
}
}
//fields:
__Map map_;
lock_type lock_;
volatile size_t sz_;
};
NS_SERVER_END
#endif