Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 653 lines (599 sloc) 23.398 kB
ac2936d @gstrauss prep for public release
authored
1 /*
2 * mcdb - fast, reliable, simple code to create and read constant databases
3 *
4 * Copyright (c) 2010, Glue Logic LLC. All rights reserved. code()gluelogic.com
5 *
6 * This file is part of mcdb.
7 *
8 * mcdb is free software: you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * mcdb is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with mcdb. If not, see <http://www.gnu.org/licenses/>.
20 *
21 *
22 * mcdb is originally based upon the Public Domain cdb-0.75 by Dan Bernstein
23 */
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
24
216b28a @gstrauss add #ifndef guard before feature macros
authored
25 #ifndef _XOPEN_SOURCE /* POSIX_MADV_RANDOM */
26 #define _XOPEN_SOURCE 600
27 #endif
28 #ifndef _ATFILE_SOURCE /* fstatat(), openat() */
29 #define _ATFILE_SOURCE
30 #endif
e428dc2 @gstrauss minor fixes
authored
31 #ifndef _GNU_SOURCE /* enable O_CLOEXEC on GNU systems */
32 #define _GNU_SOURCE 1
33 #endif
d778aaa @gstrauss need largefile support for open() and stat(),fstat() in mcdb to operate
authored
34 /* large file support needed for stat(),fstat() input file > 2 GB */
35 #if defined(_AIX)
36 #ifndef _LARGE_FILES
37 #define _LARGE_FILES
38 #endif
503c34d @gstrauss convert preprocessor token __linux -> __linux__
authored
39 #else /*#elif defined(__linux__) || defined(__sun) || defined(__hpux)*/
d778aaa @gstrauss need largefile support for open() and stat(),fstat() in mcdb to operate
authored
40 #ifndef _FILE_OFFSET_BITS
41 #define _FILE_OFFSET_BITS 64
42 #endif
43 #ifndef _LARGEFILE_SOURCE
44 #define _LARGEFILE_SOURCE 1
45 #endif
46 #ifndef _LARGEFILE64_SOURCE
47 #define _LARGEFILE64_SOURCE 1
48 #endif
49 #endif
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
50
e7ab2bf @gstrauss mcdb.c - add comment about why no largefile support in 32-bit
authored
51 /* Note: mcdb client does not use largefile defines when compiled 32-bit
52 * since the implementation mmap()s the entire file into the address space,
53 * meaning only up to 4 GB (minus memory used by program and other libraries)
54 * is supported in 32-bit. djb's original implementation using lseek() and
55 * read() would support > 4 GB with the > 4 GB support added in mcdb, but
56 * mcdb uses mmap() for performance instead of lseek() and read(). In current
57 * mcdb implementation, client must compile and run 64-bit for > 4 GB mcdb. */
58
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
59 #include "mcdb.h"
9beafd6 @gstrauss nointr.[ch]: simple routines that retry if interrupted (errno==EINTR)
authored
60 #include "nointr.h"
dc8f706 @gstrauss - rename mcdb_uint32.h and mcdb_attribute.h for more generic usage
authored
61 #include "uint32.h"
62 #include "code_attributes.h"
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
63
64 #include <sys/types.h>
65 #include <sys/stat.h>
66 #include <fcntl.h>
3ee3625 @gstrauss set FD_CLOEXEC on directory file descriptor
authored
67 #include <unistd.h>
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
68 #include <sys/mman.h>
69 #include <errno.h>
70 #include <limits.h>
71 #include <string.h>
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
72 #include <stdint.h> /* SIZE_MAX */
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
73
74 #ifdef _THREAD_SAFE
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
75 #include <pthread.h> /* pthread_mutex_t, pthread_mutex_{lock,unlock}() */
76 static pthread_mutex_t mcdb_global_mutex = PTHREAD_MUTEX_INITIALIZER;
77 #else
78 #define pthread_mutex_lock(mutexp) 0
79 #define pthread_mutex_unlock(mutexp) (void)0
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
80 #endif
81
216b28a @gstrauss add #ifndef guard before feature macros
authored
82 #ifndef O_CLOEXEC /* O_CLOEXEC available since Linux 2.6.23 */
3ee3625 @gstrauss set FD_CLOEXEC on directory file descriptor
authored
83 #define O_CLOEXEC 0
84 #endif
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
85
74d5670 @gstrauss porting from GNU Linux/glibc to other platforms
authored
86 /*(posix_madvise, defines not provided in Solaris 10, even w/ __EXTENSIONS__)*/
87 #if (defined(__sun) || defined(__hpux)) && !defined(POSIX_MADV_NORMAL)
88 extern int madvise(caddr_t, size_t, int);
89 #define posix_madvise(addr,len,advice) madvise((caddr_t)(addr),(len),(advice))
90 #define POSIX_MADV_NORMAL 0
91 #define POSIX_MADV_RANDOM 1
92 #define POSIX_MADV_SEQUENTIAL 2
93 #define POSIX_MADV_WILLNEED 3
94 #define POSIX_MADV_DONTNEED 4
95 #endif
0020922 @gstrauss mcdb_hash() into C99 inlined function in header mcdb.h
authored
96
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
97 /* Note: tagc of 0 ('\0') is reserved to indicate no tag */
98
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
99 bool
1efff6b @gstrauss create mcdb_findtagstart() and mcdb_findtagnext()
authored
100 mcdb_findtagstart(struct mcdb * const restrict m,
101 const char * const restrict key, const size_t klen,
102 const unsigned char tagc)
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
103 {
dc8f706 @gstrauss - rename mcdb_uint32.h and mcdb_attribute.h for more generic usage
authored
104 const uint32_t khash_init = /* init hash value; hash tagc if tagc not 0 */
105 (tagc != 0)
106 ? uint32_hash_djb_uchar(UINT32_HASH_DJB_INIT, tagc)
107 : UINT32_HASH_DJB_INIT;
108 const uint32_t khash = uint32_hash_djb(khash_init, key, klen);
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
109 const unsigned char * restrict ptr;
110
116e0d2 @gstrauss - rework thread-safe interfaces
authored
111 (void) mcdb_thread_refresh_self(m);
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
112 /* (ignore rc; continue with previous map in case of failure) */
113
4af1ec1 @gstrauss change structures to support cdb > 4 GB file size with 64-bit mcdb cl…
authored
114 /* (size of data in lvl1 hash table element is 16-bytes (shift 4 bits)) */
743da9c @gstrauss mcdb.h - add gcc __attribute__((hot)) to mcdb_findtagstart mcdb_findt…
authored
115 ptr = m->map->ptr + ((khash & MCDB_SLOT_MASK) << 4);
116 m->hpos = uint64_strunpack_bigendian_aligned_macro(ptr);
117 m->hslots= uint32_strunpack_bigendian_aligned_macro(ptr+8);
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
118 m->loop = 0;
57fb8dd @gstrauss fix bug in definition of MCDB_SLOTS which resulted in excess work bei…
authored
119 if (__builtin_expect((!m->hslots), 0))
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
120 return false;
4af1ec1 @gstrauss change structures to support cdb > 4 GB file size with 64-bit mcdb cl…
authored
121 /* (size of data in lvl2 hash table element is 16-bytes (shift 4 bits)) */
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
122 m->kpos = m->hpos
123 +(((uintptr_t)((khash>>MCDB_SLOT_BITS) % m->hslots)) << m->map->b);
c3c6ccc @gstrauss minor: remove unnecessary cast in mcdb.c
authored
124 ptr = m->map->ptr + m->kpos;
6a09f2d @gstrauss code_attributes.h - add link to gcc __builtin_prefetch() doc
authored
125 __builtin_prefetch(ptr,0,2); /*prefetch for mcdb_findtagnext()*/
126 __builtin_prefetch(ptr+64,0,2); /*prefetch for mcdb_findtagnext()*/
127 uint32_strpack_bigendian_aligned_macro(&m->khash, khash);/*store bigendian*/
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
128 return true;
129 }
130
131 bool
1efff6b @gstrauss create mcdb_findtagstart() and mcdb_findtagnext()
authored
132 mcdb_findtagnext(struct mcdb * const restrict m,
133 const char * const restrict key, const size_t klen,
134 const unsigned char tagc)
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
135 {
136 const unsigned char * ptr;
137 const unsigned char * const restrict mptr = m->map->ptr;
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
138 const uintptr_t hslots_end= m->hpos + (((uintptr_t)m->hslots) << m->map->b);
5ca93e2 @gstrauss fix 64-bit bug in mcdb.c:mcdb_findtagnext() where vpos not a 64-bit q…
authored
139 uintptr_t vpos;
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
140 uint32_t khash;
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
141
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
142 if (m->map->b == 3) {
143 while (m->loop < m->hslots) {
144 ptr = mptr + m->kpos;
145 m->kpos += 8;
146 if (__builtin_expect((m->kpos == hslots_end), 0))
147 m->kpos = m->hpos;
148 khash= *(uint32_t *)ptr; /* m->khash stored bigendian */
149 vpos = uint32_strunpack_bigendian_aligned_macro(ptr+4);
150 ptr = mptr + vpos;
151 __builtin_prefetch((char *)ptr, 0, 1);
152 if (__builtin_expect((!vpos), 0))
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
153 break;
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
154 ++m->loop;
155 if (khash == m->khash) {
156 ptr = mptr + vpos + 8;
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
157 m->klen = uint32_strunpack_bigendian_macro(ptr-8);
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
158 m->dlen = uint32_strunpack_bigendian_macro(ptr-4);
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
159 m->dpos = vpos + 8 + m->klen;
160 if (m->klen == klen+(tagc!=0)
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
161 && (tagc == 0 || tagc == *ptr++) && memcmp(key,ptr,klen)==0)
162 return true;
163 }
164 }
165 }
166 else {
167 while (m->loop < m->hslots) {
168 ptr = mptr + m->kpos;
169 m->kpos += 16;
170 if (__builtin_expect((m->kpos == hslots_end), 0))
171 m->kpos = m->hpos;
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
172 khash = *(uint32_t *)ptr; /* m->khash stored bigendian */
173 m->klen = uint32_strunpack_bigendian_aligned_macro(ptr+4);
174 vpos = uint64_strunpack_bigendian_aligned_macro(ptr+8);
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
175 __builtin_prefetch((char *)mptr+vpos+4, 0, 1);
176 if (__builtin_expect((!vpos), 0))
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
177 break;
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
178 ++m->loop;
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
179 if (khash == m->khash && m->klen == klen+(tagc!=0)) {
180 m->dpos = vpos + 8 + m->klen;
3c2a836 @gstrauss performance improvement and smaller mcdb when < 4 GB data
authored
181 ptr = mptr + vpos + 8;
182 m->dlen = uint32_strunpack_bigendian_macro(ptr-4);
183 if ((tagc == 0 || tagc == *ptr++) && memcmp(key,ptr,klen) == 0)
184 return true;
185 }
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
186 }
187 }
2655d18 @gstrauss mcdb.h - modify struct mcdb to add klen element
authored
188 return (m->loop = false);
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
189 }
190
191 /* read value from mmap const db into buffer and return pointer to buffer
192 * (return NULL if position (offset) or length to read will be out-of-bounds)
5c6817f @gstrauss optimization: align hash tables to 8-byte boundary, store numbers in
authored
193 * Note: caller must terminate with '\0' if desired, i.e. buf[len] = '\0';
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
194 */
195 void *
bb7553f @gstrauss minor: add some more 'const' qualifiers
authored
196 mcdb_read(const struct mcdb * const restrict m, const uintptr_t pos,
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
197 const uint32_t len, void * const restrict buf)
198 {
5ca93e2 @gstrauss fix 64-bit bug in mcdb.c:mcdb_findtagnext() where vpos not a 64-bit q…
authored
199 const uintptr_t mapsz = m->map->size;
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
200 return (pos <= mapsz && mapsz - pos >= len) /* bounds check */
201 ? memcpy(buf, m->map->ptr + pos, len)
202 : NULL;
203 }
204
53eef89 @gstrauss provide mcdb_validate_slots() to validate mcdb
authored
205 uint32_t
206 mcdb_numrecs(struct mcdb * const restrict m)
207 {
208 struct mcdb_mmap * const restrict map = m->map;
209 if (map->n == ~0) {
210 const unsigned char * const restrict ptr = map->ptr;
211 uint32_t u = 0;
212 for (unsigned int i = 8; i < MCDB_HEADER_SZ; i += 16)
213 u += uint32_strunpack_bigendian_aligned_macro(ptr+i);
214 map->n = u >> 1; /* (hslots / 2) */
215 }
216 return map->n; /* mcdb_make currently limits n to INT_MAX (~2 billion) */
217 }
218
219 bool
220 mcdb_validate_slots(struct mcdb * const restrict m)
221 {
222 const unsigned char * const restrict ptr = m->map->ptr;
223 uint32_t u = 0;
224 const uint32_t bits = m->map->b;
225 uint64_t hpos;
226 uint64_t hpos_next;
227 uint32_t hslots;
228 uint32_t numrecs = 0;
229 if (MCDB_HEADER_SZ > m->map->size)
230 return false;
231 hpos_next = uint64_strunpack_bigendian_aligned_macro(ptr);
232 do {
233 hpos = uint64_strunpack_bigendian_aligned_macro(ptr+u);
234 numrecs += (hslots = uint32_strunpack_bigendian_aligned_macro(ptr+u+8));
235 if (/* __builtin_expect( (*(uint32_t *)(ptr+u+12)) == 0, 1) && */
236 __builtin_expect( (hpos == hpos_next), 1)) /*(skip padding == 0)*/
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
237 hpos_next += ((uintptr_t)hslots << bits);
53eef89 @gstrauss provide mcdb_validate_slots() to validate mcdb
authored
238 else
239 return false;
240 } while ((u += 16) < MCDB_HEADER_SZ);
241 m->map->n = numrecs >> 1; /* (hslots / 2) */
242 return (hpos_next == m->map->size);
243 }
244
1c3a9e7 @gstrauss mods for use by future plugins to Perl,Python,etc
authored
245 bool
65ded13 @gstrauss mcdb iterator interface
authored
246 mcdb_iter(struct mcdb_iter * const restrict iter)
1c3a9e7 @gstrauss mods for use by future plugins to Perl,Python,etc
authored
247 {
65ded13 @gstrauss mcdb iterator interface
authored
248 if (iter->ptr < iter->eod) {
249 iter->klen = uint32_strunpack_bigendian_macro(iter->ptr);
250 iter->dlen = uint32_strunpack_bigendian_macro(iter->ptr+4);
251 iter->ptr += 8 + iter->klen + iter->dlen;
252 if (iter->klen != ~0) { /* (klen == ~0 padding at end of data) */
253 /* klen <= INT_MAX-8 (see mcdb_make.c), so no need to also check
254 * (iter->ptr >= iter->eod-(MCDB_PAD_MASK-7))
255 * (using original iter->ptr value before update above) */
256 __builtin_prefetch(iter->ptr, 0, 3);
257 return true;
258 }
259 iter->ptr = iter->eod; /* (reached end of data; return false below) */
1c3a9e7 @gstrauss mods for use by future plugins to Perl,Python,etc
authored
260 }
261 return false;
262 }
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
263
65ded13 @gstrauss mcdb iterator interface
authored
264 void
265 mcdb_iter_init(struct mcdb_iter * const restrict iter,
266 struct mcdb * const restrict m)
267 {
268 /* About eod: Last data record must begin before end-of-data (iter->eod)
269 * End-of-data is beginning of open hash tables minus up to MCDB_PAD_MASK
270 * MCDB_PAD_MASK is 1-filled (~0), so iter->klen == ~0 is in padding
271 * MCDB_PAD_MASK might be larger than min record size, so a valid
272 * record might reside in MCDB_PAD_MASK bytes before open hash tables
273 * Minimum rec size is 8 bytes for klen, dlen; 7 or fewer bytes are padding
274 */
275 unsigned char * const ptr = m->map->ptr;
276 iter->ptr = ptr + MCDB_HEADER_SZ;
277 iter->eod = ptr + uint64_strunpack_bigendian_aligned_macro(ptr) - 7;
278 __builtin_prefetch(iter->ptr,0,3); /*(must be non-faulting load if 0 recs)*/
279 iter->klen = 0;
280 iter->dlen = 0;
281 iter->map = m->map;
282 /* Note: callers that intend to iterate through entire mcdb might call
283 * posix_madvise() on the mcdb as long as mcdb fits into physical memory,
d2c858e @gstrauss workaround Solaris madvise() crashing bug
authored
284 * e.g. posix_madvise(iter->map, (size_t)(iter->eod - iter->map),
65ded13 @gstrauss mcdb iterator interface
authored
285 * POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED);
d2c858e @gstrauss workaround Solaris madvise() crashing bug
authored
286 * (if iter->ptr instead of iter->map, round down for page alignment)
65ded13 @gstrauss mcdb iterator interface
authored
287 */
288 }
289
1efff6b @gstrauss create mcdb_findtagstart() and mcdb_findtagnext()
authored
290
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
291 /* Note: __attribute_noinline__ is used to mark less frequent code paths
292 * to prevent inlining of seldoms used paths, hopefully improving instruction
293 * cache hits.
294 */
1efff6b @gstrauss create mcdb_findtagstart() and mcdb_findtagnext()
authored
295
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
296
116e0d2 @gstrauss - rework thread-safe interfaces
authored
297 static void inline
298 mcdb_mmap_unmap(struct mcdb_mmap * const restrict map)
299 __attribute_nonnull__;
300
301 static void inline
302 mcdb_mmap_unmap(struct mcdb_mmap * const restrict map)
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
303 {
116e0d2 @gstrauss - rework thread-safe interfaces
authored
304 if (map->ptr)
305 munmap(map->ptr, map->size);
6336e94 @gstrauss some addtl sanity checks detect incorrect API use
authored
306 map->ptr = NULL;
307 map->size = 0; /* map->size initialization required for mcdb_read() */
116e0d2 @gstrauss - rework thread-safe interfaces
authored
308 }
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
309
116e0d2 @gstrauss - rework thread-safe interfaces
authored
310 bool __attribute_noinline__
311 mcdb_mmap_init(struct mcdb_mmap * const restrict map, int fd)
312 {
313 struct stat st;
314 void * restrict x;
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
315
116e0d2 @gstrauss - rework thread-safe interfaces
authored
316 mcdb_mmap_unmap(map);
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
317
5ebfa73 @gstrauss mcdb.c - use POSIX_MADV_RANDOM only for larger mcdb. Smaller mcdb have
authored
318 if (fstat(fd, &st) != 0) return false;
8a9d853 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
319 #if !defined(_LP64) && !defined(__LP64__)
320 if (st.st_size > (off_t)SIZE_MAX) return (errno = EFBIG, false);
321 #endif
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
322 x = mmap(0, (size_t)st.st_size, PROT_READ, MAP_SHARED, fd, 0);
116e0d2 @gstrauss - rework thread-safe interfaces
authored
323 if (x == MAP_FAILED) return false;
1c3dd7e @gstrauss add __builtin_prefetch() to code_attributes
authored
324 __builtin_prefetch((char *)x+960, 0, 3); /*(touch mem page w/ mcdb header)*/
4a0601c @gstrauss minor: reorder some lines for sequential reads from memory
authored
325 #if 0 /* disable; does not appear to improve performance */
326 /*(peformance hit when hitting an uncached mcdb on my 32-bit Pentium-M)*/
2bfb490 @gstrauss add comment about how 4 MB size is arbitrarily chosen as cutoff
authored
327 if (st.st_size > 4194304) /*(skip syscall overhead if < 4 MB (arbitrary))*/
5ef58b4 @gstrauss mcdb.c - address given to posix_madvise() must be page-aligned (_SC_P…
authored
328 posix_madvise(((char *)x), st.st_size, POSIX_MADV_RANDOM);
329 /*(addr (x) must be aligned on _SC_PAGESIZE for madvise portability)*/
4a0601c @gstrauss minor: reorder some lines for sequential reads from memory
authored
330 #endif
116e0d2 @gstrauss - rework thread-safe interfaces
authored
331 map->ptr = (unsigned char *)x;
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
332 map->size = (uintptr_t)st.st_size;
333 map->b = st.st_size < UINT_MAX || *(uint32_t *)x == 0 ? 3u : 4u;
53eef89 @gstrauss provide mcdb_validate_slots() to validate mcdb
authored
334 map->n = ~0;
116e0d2 @gstrauss - rework thread-safe interfaces
authored
335 map->mtime = st.st_mtime;
336 map->next = NULL;
337 map->refcnt= 0;
338 return true;
339 }
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
340
116e0d2 @gstrauss - rework thread-safe interfaces
authored
341 void __attribute_noinline__
bb7553f @gstrauss minor: add some more 'const' qualifiers
authored
342 mcdb_mmap_prefault(const struct mcdb_mmap * const restrict map)
e988a5f @gstrauss add new routine: mcdb_mmap_prefault(m->map)
authored
343 {
344 /* improves performance on uncached mcdb (if about to make *many* queries)
345 * by asking operating system to prefault pages into memory from disk
346 * (call only if mcdb fits into filesystem cache in physical memory) */
f049194 @gstrauss add to posix_madvise() advice in a couple places in the code
authored
347 posix_madvise(((char *)map->ptr), map->size,
348 POSIX_MADV_WILLNEED | POSIX_MADV_RANDOM);
e988a5f @gstrauss add new routine: mcdb_mmap_prefault(m->map)
authored
349 }
350
351 void __attribute_noinline__
116e0d2 @gstrauss - rework thread-safe interfaces
authored
352 mcdb_mmap_free(struct mcdb_mmap * const restrict map)
353 {
6336e94 @gstrauss some addtl sanity checks detect incorrect API use
authored
354 if (map == NULL) return;
116e0d2 @gstrauss - rework thread-safe interfaces
authored
355 mcdb_mmap_unmap(map);
356 if (map->fn_free) {
a17d095 @gstrauss nss_mcdb - register atexit() to cleanup open nss mcdb if -D_FORTIFY_S…
authored
357 if (map->fname != NULL && map->fname != map->fnamebuf) {
116e0d2 @gstrauss - rework thread-safe interfaces
authored
358 map->fn_free(map->fname);
a17d095 @gstrauss nss_mcdb - register atexit() to cleanup open nss mcdb if -D_FORTIFY_S…
authored
359 map->fname = NULL;
360 }
116e0d2 @gstrauss - rework thread-safe interfaces
authored
361 map->fn_free(map);
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
362 }
363 }
364
116e0d2 @gstrauss - rework thread-safe interfaces
authored
365 void __attribute_noinline__
366 mcdb_mmap_destroy(struct mcdb_mmap * const restrict map)
367 {
6336e94 @gstrauss some addtl sanity checks detect incorrect API use
authored
368 if (map == NULL) return;
369 if (map->dfd != -1) {
116e0d2 @gstrauss - rework thread-safe interfaces
authored
370 (void) nointr_close(map->dfd);
6336e94 @gstrauss some addtl sanity checks detect incorrect API use
authored
371 map->dfd = -1;
372 }
116e0d2 @gstrauss - rework thread-safe interfaces
authored
373 mcdb_mmap_free(map);
374 }
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
375
116e0d2 @gstrauss - rework thread-safe interfaces
authored
376 bool __attribute_noinline__
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
377 mcdb_mmap_reopen(struct mcdb_mmap * const restrict map)
378 {
379 int fd;
380 bool rc;
381
36794f3 @gstrauss add O_CLOEXEC to more *open() calls so that it is used where available
authored
382 const int oflags = O_RDONLY | O_NONBLOCK | O_CLOEXEC;
05eea2b @gstrauss platform portability tweaks for Solaris,AIX,HP-UX
authored
383 #ifdef AT_FDCWD
116e0d2 @gstrauss - rework thread-safe interfaces
authored
384 if (map->dfd != -1) {
385 if ((fd = nointr_openat(map->dfd, map->fname, oflags, 0)) == -1)
386 return false;
387 }
388 else
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
389 #endif
116e0d2 @gstrauss - rework thread-safe interfaces
authored
390 if ((fd = nointr_open(map->fname, oflags, 0)) == -1)
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
391 return false;
392
393 rc = mcdb_mmap_init(map, fd);
394
9beafd6 @gstrauss nointr.[ch]: simple routines that retry if interrupted (errno==EINTR)
authored
395 (void) nointr_close(fd); /* close fd once it has been mmap'ed */
ccd70f6 @gstrauss repurpose thread-safe code for any multiple use of same persistent mcdb
authored
396
397 return rc;
398 }
399
116e0d2 @gstrauss - rework thread-safe interfaces
authored
400 bool
401 mcdb_mmap_refresh_check(const struct mcdb_mmap * const restrict map)
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
402 {
403 struct stat st;
116e0d2 @gstrauss - rework thread-safe interfaces
authored
404 return (map->ptr == NULL
405 || ( (
05eea2b @gstrauss platform portability tweaks for Solaris,AIX,HP-UX
authored
406 #ifdef AT_FDCWD
116e0d2 @gstrauss - rework thread-safe interfaces
authored
407 map->dfd != -1
408 ? fstatat(map->dfd, map->fname, &st, 0) == 0
409 :
410 #endif
411 stat(map->fname, &st) == 0 )
412 && map->mtime != st.st_mtime ) );
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
413 }
414
116e0d2 @gstrauss - rework thread-safe interfaces
authored
415 /*
416 * Example use of mcdb_mmap_create() and mcdb_mmap_destroy():
417 * mcdb_mmap_create()
418 * repeat:
419 * (optional) mcdb_mmap_refresh())
420 * (lookups)
421 * mcdb_mmap_destroy();
422 *
423 * Example use in threaded program where multiple threads access mcdb_mmap:
424 * maintenance thread: mcdb_mmap_create(...)
425 *
426 * maintenance thread: mcdb_mmap_refresh_threadsafe(&map) (periodic/triggered)
427 * querying threads: mcdb_thread_register(m)
428 * querying threads: mcdb_find(m,key,klen) (repeat for may lookups)
429 * querying threads: mcdb_thread_unregister(m)
430 *
431 * maintenance thread: mcdb_mmap_destroy(map)
432 *
433 * Querying threads can register once and then may do many lookups at will.
434 * (no need to register, query, unregister each time)
435 * Each query (mcdb_findtagstart()) checks for updated mmap by calling
436 * mcdb_thread_refresh_self(). If a thread might go for a long period of time
437 * without querying, and mcdb_mmap has been updated, then thread can optionally
438 * (periodically) call mcdb_thread_refresh_self() to release the outdated mmap
439 * before the thread gets around (some time in the future) to its next query.
440 *
441 * Note: using map->dfd means that if a directory replaces existing directory,
442 * mcdb_mmap will not notice. Pass dname == NULL to skip using map->dfd.
443 *
444 * Note: use return value from mcdb_mmap_create(). If an error occurs during
445 * mcdb_mmap_create(), fn_free(map) is called, whether or not map or NULL was
446 * passed as first argument to mcdb_mmap_create().
447 */
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
448 __attribute_noinline__
449 struct mcdb_mmap *
116e0d2 @gstrauss - rework thread-safe interfaces
authored
450 mcdb_mmap_create(struct mcdb_mmap * restrict map,
451 const char * const dname __attribute_unused__,
8ca6c05 @gstrauss minor fixes and code portability
authored
452 const char * const fname,
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
453 void * (*fn_malloc)(size_t), void (*fn_free)(void *))
454 {
116e0d2 @gstrauss - rework thread-safe interfaces
authored
455 char *fbuf;
55d66fd @gstrauss netdb services - <netinet/in.h> defines in_port_t as uint16_t
authored
456 size_t flen;
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
457
116e0d2 @gstrauss - rework thread-safe interfaces
authored
458 if (fn_malloc == NULL)
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
459 return NULL;
116e0d2 @gstrauss - rework thread-safe interfaces
authored
460 if (map == NULL && (map = fn_malloc(sizeof(struct mcdb_mmap))) == NULL)
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
461 return NULL;
116e0d2 @gstrauss - rework thread-safe interfaces
authored
462 /* initialize */
463 memset(map, '\0', sizeof(struct mcdb_mmap));
464 map->fn_malloc = fn_malloc;
465 map->fn_free = fn_free;
466 map->dfd = -1;
55d66fd @gstrauss netdb services - <netinet/in.h> defines in_port_t as uint16_t
authored
467 flen = strlen(fname);
116e0d2 @gstrauss - rework thread-safe interfaces
authored
468
503c34d @gstrauss convert preprocessor token __linux -> __linux__
authored
469 #if defined(__linux__) || defined(__sun)
116e0d2 @gstrauss - rework thread-safe interfaces
authored
470 if (dname != NULL) {
471 /* caller must have open STDIN, STDOUT, STDERR */
472 map->dfd = nointr_open(dname, O_RDONLY|O_CLOEXEC, 0);
473 if (map->dfd > STDERR_FILENO) {
474 if (O_CLOEXEC == 0)
475 (void) fcntl(map->dfd, F_SETFD, FD_CLOEXEC);
476 }
477 else {
a7f6ba9 @gstrauss add symbol aliases with "hidden" DSO visibility
authored
478 mcdb_mmap_destroy_h(map);
116e0d2 @gstrauss - rework thread-safe interfaces
authored
479 return NULL;
480 }
3ee3625 @gstrauss set FD_CLOEXEC on directory file descriptor
authored
481 }
116e0d2 @gstrauss - rework thread-safe interfaces
authored
482 #else
483 if (dname != NULL) {
484 const size_t dlen = strlen(dname);
485 if (sizeof(map->fnamebuf) >= dlen+flen+2)
486 fbuf = map->fnamebuf;
74d5670 @gstrauss porting from GNU Linux/glibc to other platforms
authored
487 else if ((fbuf = fn_malloc(dlen+flen+2)) == NULL) {
a7f6ba9 @gstrauss add symbol aliases with "hidden" DSO visibility
authored
488 mcdb_mmap_destroy_h(map);
116e0d2 @gstrauss - rework thread-safe interfaces
authored
489 return NULL;
490 }
491 memcpy(fbuf, dname, dlen);
492 fbuf[dlen] = '/';
493 memcpy(fbuf+dlen+1, fname, flen+1);
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
494 }
116e0d2 @gstrauss - rework thread-safe interfaces
authored
495 else
d3acba9 @gstrauss begin code for implementing the small cdb utilities
authored
496 #endif
116e0d2 @gstrauss - rework thread-safe interfaces
authored
497 {
498 if (sizeof(map->fnamebuf) > flen)
499 fbuf = map->fnamebuf;
500 else if ((fbuf = fn_malloc(flen+1)) == NULL) {
a7f6ba9 @gstrauss add symbol aliases with "hidden" DSO visibility
authored
501 mcdb_mmap_destroy_h(map);
116e0d2 @gstrauss - rework thread-safe interfaces
authored
502 return NULL;
503 }
504 memcpy(fbuf, fname, flen+1);
505 }
506 map->fname = fbuf;
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
507
508 if (mcdb_mmap_reopen(map)) {
509 ++map->refcnt;
510 return map;
511 }
512 else {
a7f6ba9 @gstrauss add symbol aliases with "hidden" DSO visibility
authored
513 mcdb_mmap_destroy_h(map);
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
514 return NULL;
515 }
516 }
517
1efff6b @gstrauss create mcdb_findtagstart() and mcdb_findtagnext()
authored
518 bool __attribute_noinline__
116e0d2 @gstrauss - rework thread-safe interfaces
authored
519 mcdb_mmap_thread_registration(struct mcdb_mmap ** const restrict mapptr,
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
520 const int flags)
2c4c6c3 @gstrauss more code addition, code restructure;
authored
521 {
116e0d2 @gstrauss - rework thread-safe interfaces
authored
522 struct mcdb_mmap *map;
523 struct mcdb_mmap *next = NULL;
524 const bool register_use_incr = ((flags & MCDB_REGISTER_USE_INCR) != 0);
525 #define register_use_decr (!register_use_incr)
2c4c6c3 @gstrauss more code addition, code restructure;
authored
526
116e0d2 @gstrauss - rework thread-safe interfaces
authored
527 if (!(flags & MCDB_REGISTER_MUTEX_UNLOCK_HOLD)
528 && pthread_mutex_lock(&mcdb_global_mutex) != 0)
529 return false;
530
531 /*map = *mapptr;*/ /*(ought to be preceded by StoreLoad memory barrier)*/
532 map = *(struct mcdb_mmap * volatile * restrict)mapptr;
533 if (map == NULL || (map->ptr == NULL && register_use_incr)) {
534 if (!(flags & MCDB_REGISTER_MUTEX_LOCK_HOLD))
535 pthread_mutex_unlock(&mcdb_global_mutex);
536 return !register_use_incr; /* succeed if unregister; fail if register */
537 /* If registering, possibly detected race condition in which another
538 * thread released final reference and mcdb was munmap()'d while current
539 * thread waited for lock. It is now invalid to attempt to register use
540 * of a resource that has been released. Caller can detect and reopen*/
541 }
542
543 if (register_use_incr) {
544 if ((next = map->next) == NULL)
545 ++map->refcnt;
546 else {
547 while (next->next != NULL)
548 next = next->next;
549 ++next->refcnt;
550 *mapptr = next;
551 }
552 }
553
554 if ((register_use_decr || next != NULL) && --map->refcnt == 0) {
555 /* (above handles refcnt decremented to zero and refcnt already zero) */
556 while ((next = map->next) != NULL && next->refcnt == 0) {
557 map->next = next->next;
558 next->fname = NULL; /* do not free(next->fname) yet */
559 mcdb_mmap_free(next);
560 }
561 if (!(flags & MCDB_REGISTER_MUNMAP_SKIP)) {
562 map->fname = NULL; /* do not free(map->fname) yet */
563 mcdb_mmap_free(map);
564 if (register_use_decr)
565 *mapptr = NULL;
566 }
567 }
568 #undef register_use_decr
569
570 if (!(flags & MCDB_REGISTER_MUTEX_LOCK_HOLD))
571 pthread_mutex_unlock(&mcdb_global_mutex);
2c4c6c3 @gstrauss more code addition, code restructure;
authored
572
573 return true;
574 }
575
4af1ec1 @gstrauss change structures to support cdb > 4 GB file size with 64-bit mcdb cl…
authored
576 /* theaded programs (while multiple threads are using same struct mcdb_mmap)
116e0d2 @gstrauss - rework thread-safe interfaces
authored
577 * must reopen and register (update refcnt on previous and new mcdb_mmap) while
578 * holding a lock, or else there are race conditions with refcnt'ing. */
579 bool __attribute_noinline__
580 mcdb_mmap_reopen_threadsafe(struct mcdb_mmap ** const restrict mapptr)
581 {
582 bool rc = true;
ca11100 @gstrauss mmap constant db: initial cut from Dan Bernstein cdb-0.75
authored
583
116e0d2 @gstrauss - rework thread-safe interfaces
authored
584 if (pthread_mutex_lock(&mcdb_global_mutex) != 0)
585 return false;
586
587 if ((*mapptr)->next == NULL) {
588 const struct mcdb_mmap * const map = *mapptr;
589 struct mcdb_mmap *next;
590 if (map->fn_malloc != NULL /*(else caller misconfigured mcdb_mmap)*/
591 && (next = map->fn_malloc(sizeof(struct mcdb_mmap))) != NULL) {
592 memcpy(next, map, sizeof(struct mcdb_mmap));
593 next->ptr = NULL; /*(skip munmap() in mcdb_mmap_reopen())*/
594 if (map->fname == map->fnamebuf)
595 next->fname = next->fnamebuf;
596 if ((rc = mcdb_mmap_reopen(next)))
597 (*mapptr)->next = next;
598 else
599 map->fn_free(next);
600 }
601 else
602 rc = false; /* map->fn_malloc failed */
603 }
604 /* else rc = true; (map->next already updated e.g. while obtaining lock) */
605
606 if (rc) {
cc41e53 @gstrauss vendor compiler portability to Solaris, AIX, HP-UX
authored
607 const int mcdb_flags_hold_lock =
116e0d2 @gstrauss - rework thread-safe interfaces
authored
608 MCDB_REGISTER_USE_INCR
609 | MCDB_REGISTER_MUTEX_UNLOCK_HOLD
610 | MCDB_REGISTER_MUTEX_LOCK_HOLD;
a7f6ba9 @gstrauss add symbol aliases with "hidden" DSO visibility
authored
611 rc = mcdb_mmap_thread_registration_h(mapptr, mcdb_flags_hold_lock);
116e0d2 @gstrauss - rework thread-safe interfaces
authored
612 }
613
614 pthread_mutex_unlock(&mcdb_global_mutex);
615 return rc;
616 }
a7f6ba9 @gstrauss add symbol aliases with "hidden" DSO visibility
authored
617
618
619 /* alias symbols with hidden visibility for use in DSO linking static mcdb.o
620 * (Reference: "How to Write Shared Libraries", by Ulrich Drepper)
621 * (optimization)
622 * The aliases below are not a complete set of mcdb symbols,
623 * but instead are the most common used in libnss_mcdb.so.2 */
a3baf01 @gstrauss clang supports most gcc __attribute__, __builtin_*
authored
624 #if __GNUC_PREREQ(4,0) || __has_attribute(alias)
a7f6ba9 @gstrauss add symbol aliases with "hidden" DSO visibility
authored
625 HIDDEN extern __typeof (mcdb_findtagstart)
626 mcdb_findtagstart_h
627 __attribute__((alias ("mcdb_findtagstart")));
628 HIDDEN extern __typeof (mcdb_findtagnext)
629 mcdb_findtagnext_h
630 __attribute__((alias ("mcdb_findtagnext")));
631 HIDDEN extern __typeof (mcdb_iter)
632 mcdb_iter_h
633 __attribute__((alias ("mcdb_iter")));
634 HIDDEN extern __typeof (mcdb_iter_init)
635 mcdb_iter_init_h
636 __attribute__((alias ("mcdb_iter_init")));
637 HIDDEN extern __typeof (mcdb_mmap_create)
638 mcdb_mmap_create_h
639 __attribute__((alias ("mcdb_mmap_create")));
640 HIDDEN extern __typeof (mcdb_mmap_destroy)
641 mcdb_mmap_destroy_h
642 __attribute__((alias ("mcdb_mmap_destroy")));
643 HIDDEN extern __typeof (mcdb_mmap_refresh_check)
644 mcdb_mmap_refresh_check_h
645 __attribute__((alias ("mcdb_mmap_refresh_check")));
646 HIDDEN extern __typeof (mcdb_mmap_thread_registration)
647 mcdb_mmap_thread_registration_h
648 __attribute__((alias ("mcdb_mmap_thread_registration")));
649 HIDDEN extern __typeof (mcdb_mmap_reopen_threadsafe)
650 mcdb_mmap_reopen_threadsafe_h
651 __attribute__((alias ("mcdb_mmap_reopen_threadsafe")));
652 #endif
Something went wrong with that request. Please try again.