1 //
2 // $Id$
3 //
4
5 //
6 // Copyright (c) 2001-2016, Andrew Aksyonoff
7 // Copyright (c) 2008-2016, Sphinx Technologies Inc
8 // All rights reserved
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License. You should have
12 // received a copy of the GPL license along with this program; if you
13 // did not, you can find it at http://www.gnu.org/
14 //
15
16 #include "sphinx.h"
17 #include "sphinxint.h"
18 #include "sphinxutils.h"
19
20 #if !USE_WINDOWS
21 #include <sys/time.h> // for gettimeofday
22
23 // define this if you want to run gprof over the threads model - to track children threads also.
24 #define USE_GPROF 0
25
26 #endif
27
28 int g_iThreadStackSize = 1024*1024;
29
30 //////////////////////////////////////////////////////////////////////////
31
32 char CSphString::EMPTY[] = "";
33
34 #if USE_WINDOWS
35 #ifndef NDEBUG
36
sphAssert(const char * sExpr,const char * sFile,int iLine)37 void sphAssert ( const char * sExpr, const char * sFile, int iLine )
38 {
39 char sBuffer [ 1024 ];
40 _snprintf ( sBuffer, sizeof(sBuffer), "%s(%d): assertion %s failed\n", sFile, iLine, sExpr );
41
42 if ( MessageBox ( NULL, sBuffer, "Assert failed! Cancel to debug.",
43 MB_OKCANCEL | MB_TOPMOST | MB_SYSTEMMODAL | MB_ICONEXCLAMATION )!=IDOK )
44 {
45 __debugbreak ();
46 } else
47 {
48 fprintf ( stdout, "%s", sBuffer );
49 exit ( 1 );
50 }
51 }
52
53 #endif // !NDEBUG
54 #endif // USE_WINDOWS
55
56 /////////////////////////////////////////////////////////////////////////////
57 // DEBUG MEMORY MANAGER
58 /////////////////////////////////////////////////////////////////////////////
59
60 #if SPH_DEBUG_LEAKS
61
62 #undef new
63 #define SPH_DEBUG_DOFREE 1 // 0 will not actually free returned blocks; helps to catch double deletes etc
64
65 const DWORD MEMORY_MAGIC_PLAIN = 0xbbbbbbbbUL;
66 const DWORD MEMORY_MAGIC_ARRAY = 0xaaaaaaaaUL;
67 const DWORD MEMORY_MAGIC_END = 0xeeeeeeeeUL;
68 const DWORD MEMORY_MAGIC_DELETED = 0xdedededeUL;
69
70
71 struct CSphMemHeader
72 {
73 DWORD m_uMagic;
74 const char * m_sFile;
75 #if SPH_DEBUG_BACKTRACES
76 const char * m_sBacktrace;
77 #endif
78 int m_iLine;
79 size_t m_iSize;
80 int m_iAllocId;
81 BYTE * m_pPointer;
82 CSphMemHeader * m_pNext;
83 CSphMemHeader * m_pPrev;
84 };
85
86 static CSphStaticMutex g_tAllocsMutex;
87
88 static int g_iCurAllocs = 0;
89 static int g_iAllocsId = 0;
90 static CSphMemHeader * g_pAllocs = NULL;
91 static int64_t g_iCurBytes = 0;
92 static int g_iTotalAllocs = 0;
93 static int g_iPeakAllocs = 0;
94 static int64_t g_iPeakBytes = 0;
95 #if SPH_ALLOC_FILL
96 static bool g_bFirstRandomAlloc = true;
97 #endif
98
sphDebugNew(size_t iSize,const char * sFile,int iLine,bool bArray)99 void * sphDebugNew ( size_t iSize, const char * sFile, int iLine, bool bArray )
100 {
101 BYTE * pBlock = (BYTE*) ::malloc ( iSize+sizeof(CSphMemHeader)+sizeof(DWORD) );
102 if ( !pBlock )
103 sphDie ( "out of memory (unable to allocate " UINT64_FMT " bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
104
105 *(DWORD*)( pBlock+iSize+sizeof(CSphMemHeader) ) = MEMORY_MAGIC_END;
106 g_tAllocsMutex.Lock();
107
108 CSphMemHeader * pHeader = (CSphMemHeader*) pBlock;
109 pHeader->m_uMagic = bArray ? MEMORY_MAGIC_ARRAY : MEMORY_MAGIC_PLAIN;
110 pHeader->m_sFile = sFile;
111 #if SPH_ALLOC_FILL
112 if ( g_bFirstRandomAlloc )
113 {
114 sphAutoSrand();
115 g_bFirstRandomAlloc = false;
116 }
117
118 BYTE * pBlockPtr = (BYTE*)(pHeader+1);
119 for ( size_t i = 0; i < iSize; i++ )
120 *pBlockPtr++ = BYTE(sphRand () & 0xFF);
121 #endif
122 #if SPH_DEBUG_BACKTRACES
123 const char * sTrace = DoBacktrace ( 0, 3 );
124 if ( sTrace )
125 {
126 char * pTrace = (char*) ::malloc ( strlen(sTrace) + 1 );
127 strcpy ( pTrace, sTrace ); //NOLINT
128 pHeader->m_sBacktrace = pTrace;
129 } else
130 pHeader->m_sBacktrace = NULL;
131 #endif
132 pHeader->m_iLine = iLine;
133 pHeader->m_iSize = iSize;
134 pHeader->m_iAllocId = ++g_iAllocsId;
135 pHeader->m_pPointer = pBlock;
136 pHeader->m_pNext = g_pAllocs;
137 pHeader->m_pPrev = NULL;
138 if ( g_pAllocs )
139 {
140 assert ( !g_pAllocs->m_pPrev );
141 g_pAllocs->m_pPrev = pHeader;
142 }
143 g_pAllocs = pHeader;
144
145 g_iCurAllocs++;
146 g_iCurBytes += iSize;
147
148 g_iTotalAllocs++;
149 g_iPeakAllocs = Max ( g_iPeakAllocs, g_iCurAllocs );
150 g_iPeakBytes = Max ( g_iPeakBytes, g_iCurBytes );
151
152 g_tAllocsMutex.Unlock();
153 return pHeader+1;
154 }
155
156
sphDebugDelete(void * pPtr,bool bArray)157 void sphDebugDelete ( void * pPtr, bool bArray )
158 {
159 if ( !pPtr )
160 return;
161 g_tAllocsMutex.Lock();
162
163 CSphMemHeader * pHeader = ((CSphMemHeader*)pPtr)-1;
164 switch ( pHeader->m_uMagic )
165 {
166 case MEMORY_MAGIC_ARRAY:
167 if ( !bArray )
168 sphDie ( "delete [] on non-array block %d allocated at %s(%d)",
169 pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
170 break;
171
172 case MEMORY_MAGIC_PLAIN:
173 if ( bArray )
174 sphDie ( "delete on array block %d allocated at %s(%d)",
175 pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
176 break;
177
178 case MEMORY_MAGIC_DELETED:
179 sphDie ( "double delete on block %d allocated at %s(%d)",
180 pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
181 break;
182
183 default:
184 sphDie ( "delete on unmanaged block at 0x%08x", pPtr );
185 return;
186 }
187
188 BYTE * pBlock = (BYTE*) pHeader;
189 if ( *(DWORD*)( pBlock+pHeader->m_iSize+sizeof(CSphMemHeader) )!=MEMORY_MAGIC_END )
190 sphDie ( "out-of-bounds write beyond block %d allocated at %s(%d)",
191 pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
192
193 // unchain
194 if ( pHeader==g_pAllocs )
195 g_pAllocs = g_pAllocs->m_pNext;
196
197 if ( pHeader->m_pPrev )
198 {
199 assert ( pHeader->m_pPrev->m_uMagic==MEMORY_MAGIC_PLAIN || pHeader->m_pPrev->m_uMagic==MEMORY_MAGIC_ARRAY );
200 pHeader->m_pPrev->m_pNext = pHeader->m_pNext;
201 }
202 if ( pHeader->m_pNext )
203 {
204 assert ( pHeader->m_pNext->m_uMagic==MEMORY_MAGIC_PLAIN || pHeader->m_pNext->m_uMagic==MEMORY_MAGIC_ARRAY );
205 pHeader->m_pNext->m_pPrev = pHeader->m_pPrev;
206 }
207
208 pHeader->m_pPrev = NULL;
209 pHeader->m_pNext = NULL;
210
211 // mark and delete
212 pHeader->m_uMagic = MEMORY_MAGIC_DELETED;
213
214 g_iCurAllocs--;
215 g_iCurBytes -= pHeader->m_iSize;
216
217 #if SPH_DEBUG_BACKTRACES
218 if ( pHeader->m_sBacktrace )
219 ::free ( (void*) pHeader->m_sBacktrace );
220 #endif
221
222 #if SPH_DEBUG_DOFREE
223 ::free ( pHeader );
224 #endif
225
226 g_tAllocsMutex.Unlock();
227 }
228
229
sphAllocBytes()230 int64_t sphAllocBytes ()
231 {
232 return g_iCurBytes;
233 }
234
235
sphAllocsCount()236 int sphAllocsCount ()
237 {
238 return g_iCurAllocs;
239 }
240
241
sphAllocsLastID()242 int sphAllocsLastID ()
243 {
244 return g_iAllocsId;
245 }
246
247
sphAllocsDump(int iFile,int iSinceID)248 void sphAllocsDump ( int iFile, int iSinceID )
249 {
250 g_tAllocsMutex.Lock();
251
252 sphSafeInfo ( iFile, "--- dumping allocs since %d ---\n", iSinceID );
253
254 uint64_t iTotalBytes = 0;
255 int iTotal = 0;
256
257 for ( CSphMemHeader * pHeader = g_pAllocs;
258 pHeader && pHeader->m_iAllocId > iSinceID;
259 pHeader = pHeader->m_pNext )
260 {
261 sphSafeInfo ( iFile, "alloc %d at %s(%d): 0x%0p %d bytes\n", pHeader->m_iAllocId,
262 pHeader->m_sFile, pHeader->m_iLine, pHeader->m_pPointer, (int)pHeader->m_iSize );
263
264 #if SPH_DEBUG_BACKTRACES
265 sphSafeInfo ( iFile, "Backtrace:\n%s\n", pHeader->m_sBacktrace );
266 #endif
267
268 iTotalBytes += pHeader->m_iSize;
269 iTotal++;
270 }
271
272 sphSafeInfo ( iFile, "total allocs %d: %d.%03d bytes", iTotal, (int)(iTotalBytes/1024), (int)(iTotalBytes%1000) );
273 sphSafeInfo ( iFile, "--- end of dump ---\n" );
274
275 g_tAllocsMutex.Unlock();
276 }
277
278
sphAllocsStats()279 void sphAllocsStats ()
280 {
281 fprintf ( stdout, "--- total-allocs=%d, peak-allocs=%d, peak-bytes=" INT64_FMT "\n",
282 g_iTotalAllocs, g_iPeakAllocs, g_iPeakBytes );
283 }
284
285
sphAllocsCheck()286 void sphAllocsCheck ()
287 {
288 g_tAllocsMutex.Lock();
289 for ( CSphMemHeader * pHeader=g_pAllocs; pHeader; pHeader=pHeader->m_pNext )
290 {
291 BYTE * pBlock = (BYTE*) pHeader;
292
293 if (!( pHeader->m_uMagic==MEMORY_MAGIC_ARRAY || pHeader->m_uMagic==MEMORY_MAGIC_PLAIN ))
294 sphDie ( "corrupted header in block %d allocated at %s(%d)",
295 pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
296
297 if ( *(DWORD*)( pBlock+pHeader->m_iSize+sizeof(CSphMemHeader) )!=MEMORY_MAGIC_END )
298 sphDie ( "out-of-bounds write beyond block %d allocated at %s(%d)",
299 pHeader->m_iAllocId, pHeader->m_sFile, pHeader->m_iLine );
300 }
301 g_tAllocsMutex.Unlock();
302 }
303
sphMemStatInit()304 void sphMemStatInit () {}
sphMemStatDone()305 void sphMemStatDone () {}
sphMemStatDump(int)306 void sphMemStatDump ( int ) {}
307
308 //////////////////////////////////////////////////////////////////////////
309
operator new(size_t iSize,const char * sFile,int iLine)310 void * operator new ( size_t iSize, const char * sFile, int iLine )
311 {
312 return sphDebugNew ( iSize, sFile, iLine, false );
313 }
314
315
operator new[](size_t iSize,const char * sFile,int iLine)316 void * operator new [] ( size_t iSize, const char * sFile, int iLine )
317 {
318 return sphDebugNew ( iSize, sFile, iLine, true );
319 }
320
321
operator delete(void * pPtr)322 void operator delete ( void * pPtr )
323 {
324 sphDebugDelete ( pPtr, false );
325 }
326
327
operator delete[](void * pPtr)328 void operator delete [] ( void * pPtr )
329 {
330 sphDebugDelete ( pPtr, true );
331 }
332
333 //////////////////////////////////////////////////////////////////////////////
334 // ALLOCACTIONS COUNT/SIZE PROFILER
335 //////////////////////////////////////////////////////////////////////////////
336
337 #else
338 #if SPH_ALLOCS_PROFILER
339
340 #undef new
341
342 static CSphStaticMutex g_tAllocsMutex;
343 static int g_iAllocsId = 0;
344 static int g_iCurAllocs = 0;
345 static int64_t g_iCurBytes = 0;
346 static int g_iTotalAllocs = 0;
347 static int g_iPeakAllocs = 0;
348 static int64_t g_iPeakBytes = 0;
349
350 // statictic's per memory category
351 struct MemCategorized_t
352 {
353 int64_t m_iSize;
354 int m_iCount;
355
MemCategorized_tMemCategorized_t356 MemCategorized_t()
357 : m_iSize ( 0 )
358 , m_iCount ( 0 )
359 {
360 }
361 };
362
363 static MemCategory_e sphMemStatGet ();
364
365 // memory categories storage
366 static MemCategorized_t g_dMemCategoryStat [ MEM_TOTAL ];
367
368 //////////////////////////////////////////////////////////////////////////
369 // ALLOCATIONS COUNT/SIZE PROFILER
370 //////////////////////////////////////////////////////////////////////////
371
sphDebugNew(size_t iSize)372 void * sphDebugNew ( size_t iSize )
373 {
374 BYTE * pBlock = (BYTE*) ::malloc ( iSize+sizeof(size_t)*2 );
375 if ( !pBlock )
376 sphDie ( "out of memory (unable to allocate %"PRIu64" bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
377
378 const int iMemType = sphMemStatGet();
379 assert ( iMemType>=0 && iMemType<MEM_TOTAL );
380
381 g_tAllocsMutex.Lock ();
382
383 g_iAllocsId++;
384 g_iCurAllocs++;
385 g_iCurBytes += iSize;
386 g_iTotalAllocs++;
387 g_iPeakAllocs = Max ( g_iCurAllocs, g_iPeakAllocs );
388 g_iPeakBytes = Max ( g_iCurBytes, g_iPeakBytes );
389
390 g_dMemCategoryStat[iMemType].m_iSize += iSize;
391 g_dMemCategoryStat[iMemType].m_iCount++;
392
393 g_tAllocsMutex.Unlock ();
394
395 size_t * pData = (size_t *)pBlock;
396 pData[0] = iSize;
397 pData[1] = iMemType;
398
399 return pBlock + sizeof(size_t)*2;
400 }
401
sphDebugDelete(void * pPtr)402 void sphDebugDelete ( void * pPtr )
403 {
404 if ( !pPtr )
405 return;
406
407 size_t * pBlock = (size_t*) pPtr;
408 pBlock -= 2;
409
410 const int iSize = pBlock[0];
411 const int iMemType = pBlock[1];
412 assert ( iMemType>=0 && iMemType<MEM_TOTAL );
413
414 g_tAllocsMutex.Lock ();
415
416 g_iCurAllocs--;
417 g_iCurBytes -= iSize;
418
419 g_dMemCategoryStat[iMemType].m_iSize -= iSize;
420 g_dMemCategoryStat[iMemType].m_iCount--;
421
422 g_tAllocsMutex.Unlock ();
423
424 ::free ( pBlock );
425 }
426
sphAllocsStats()427 void sphAllocsStats ()
428 {
429 g_tAllocsMutex.Lock ();
430 fprintf ( stdout, "--- total-allocs=%d, peak-allocs=%d, peak-bytes=" INT64_FMT "\n",
431 g_iTotalAllocs, g_iPeakAllocs, g_iPeakBytes );
432 g_tAllocsMutex.Unlock ();
433 }
434
sphAllocBytes()435 int64_t sphAllocBytes () { return g_iCurBytes; }
sphAllocsCount()436 int sphAllocsCount () { return g_iCurAllocs; }
sphAllocsLastID()437 int sphAllocsLastID () { return g_iAllocsId; }
sphAllocsDump(int,int)438 void sphAllocsDump ( int, int ) {}
sphAllocsCheck()439 void sphAllocsCheck () {}
440
operator new(size_t iSize,const char *,int)441 void * operator new ( size_t iSize, const char *, int ) { return sphDebugNew ( iSize ); }
operator new[](size_t iSize,const char *,int)442 void * operator new [] ( size_t iSize, const char *, int ) { return sphDebugNew ( iSize ); }
operator delete(void * pPtr)443 void operator delete ( void * pPtr ) { sphDebugDelete ( pPtr ); }
operator delete[](void * pPtr)444 void operator delete [] ( void * pPtr ) { sphDebugDelete ( pPtr ); }
445
446
447 //////////////////////////////////////////////////////////////////////////////
448 // MEMORY STATISTICS
449 //////////////////////////////////////////////////////////////////////////////
450
451 /// TLS key of memory category stack
452 SphThreadKey_t g_tTLSMemCategory;
453
454 STATIC_ASSERT ( MEM_TOTAL<255, TOO_MANY_MEMORY_CATEGORIES );
455
456 // stack of memory categories as we move deeper and deeper
457 class MemCategoryStack_t // NOLINT
458 {
459 #define MEM_STACK_MAX 1024
460 BYTE m_dStack[MEM_STACK_MAX];
461 int m_iDepth;
462
463 public:
464
465 // ctor ( cross platform )
Reset()466 void Reset ()
467 {
468 m_iDepth = 0;
469 m_dStack[0] = MEM_CORE;
470 }
471
Push(MemCategory_e eCategory)472 void Push ( MemCategory_e eCategory )
473 {
474 assert ( eCategory>=0 && eCategory<MEM_TOTAL );
475 assert ( m_iDepth+1<MEM_STACK_MAX );
476 m_dStack[++m_iDepth] = (BYTE)eCategory;
477 }
478
479 #ifndef NDEBUG
Pop(MemCategory_e eCategory)480 void Pop ( MemCategory_e eCategory )
481 {
482 assert ( eCategory>=0 && eCategory<MEM_TOTAL );
483 #else
484 void Pop ( MemCategory_e )
485 {
486 #endif
487
488 assert ( m_iDepth-1>=0 );
489 assert ( m_dStack[m_iDepth]==eCategory );
490 m_iDepth--;
491 }
492
493 MemCategory_e Top () const
494 {
495 assert ( m_iDepth>= 0 && m_iDepth<MEM_STACK_MAX );
496 assert ( m_dStack[m_iDepth]>=0 && m_dStack[m_iDepth]<MEM_TOTAL );
497 return MemCategory_e ( m_dStack[m_iDepth] );
498 }
499 };
500
501 static MemCategoryStack_t * g_pMainTLS = NULL; // category stack of main thread
502
503 // memory statistic's per thread factory
sphMemStatThdInit()504 static MemCategoryStack_t * sphMemStatThdInit ()
505 {
506 MemCategoryStack_t * pTLS = (MemCategoryStack_t *)sphDebugNew ( sizeof ( MemCategoryStack_t ) );
507 pTLS->Reset();
508
509 Verify ( sphThreadSet ( g_tTLSMemCategory, pTLS ) );
510 return pTLS;
511 }
512
513 // per thread cleanup of memory statistic's
sphMemStatThdCleanup(MemCategoryStack_t * pTLS)514 static void sphMemStatThdCleanup ( MemCategoryStack_t * pTLS )
515 {
516 sphDebugDelete ( pTLS );
517 }
518
519 // init of memory statistic's data
sphMemStatInit()520 static void sphMemStatInit ()
521 {
522 Verify ( sphThreadKeyCreate ( &g_tTLSMemCategory ) );
523
524 // main thread statistic's creation
525 assert ( g_pMainTLS==NULL );
526 g_pMainTLS = sphMemStatThdInit();
527 assert ( g_pMainTLS!=NULL );
528 }
529
530 // cleanup of memory statistic's data
sphMemStatDone()531 static void sphMemStatDone ()
532 {
533 assert ( g_pMainTLS!=NULL );
534 sphMemStatThdCleanup ( g_pMainTLS );
535
536 sphThreadKeyDelete ( g_tTLSMemCategory );
537 }
538
539 // direct access for special category
sphMemStatMMapAdd(int64_t iSize)540 void sphMemStatMMapAdd ( int64_t iSize )
541 {
542 g_tAllocsMutex.Lock ();
543
544 g_iCurAllocs++;
545 g_iCurBytes += iSize;
546 g_iTotalAllocs++;
547 g_iPeakAllocs = Max ( g_iCurAllocs, g_iPeakAllocs );
548 g_iPeakBytes = Max ( g_iCurBytes, g_iPeakBytes );
549
550 g_dMemCategoryStat[MEM_MMAPED].m_iSize += iSize;
551 g_dMemCategoryStat[MEM_MMAPED].m_iCount++;
552
553 g_tAllocsMutex.Unlock ();
554 }
555
sphMemStatMMapDel(int64_t iSize)556 void sphMemStatMMapDel ( int64_t iSize )
557 {
558 g_tAllocsMutex.Lock ();
559
560 g_iCurAllocs--;
561 g_iCurBytes -= iSize;
562
563 g_dMemCategoryStat[MEM_MMAPED].m_iSize -= iSize;
564 g_dMemCategoryStat[MEM_MMAPED].m_iCount--;
565
566 g_tAllocsMutex.Unlock ();
567 }
568
569 // push new category on arrival
sphMemStatPush(MemCategory_e eCategory)570 void sphMemStatPush ( MemCategory_e eCategory )
571 {
572 MemCategoryStack_t * pTLS = (MemCategoryStack_t*) sphThreadGet ( g_tTLSMemCategory );
573 if ( pTLS )
574 pTLS->Push ( eCategory );
575 };
576
577 // restore last category
sphMemStatPop(MemCategory_e eCategory)578 void sphMemStatPop ( MemCategory_e eCategory )
579 {
580 MemCategoryStack_t * pTLS = (MemCategoryStack_t*) sphThreadGet ( g_tTLSMemCategory );
581 if ( pTLS )
582 pTLS->Pop ( eCategory );
583 };
584
585 // get current category
sphMemStatGet()586 static MemCategory_e sphMemStatGet ()
587 {
588 MemCategoryStack_t * pTLS = (MemCategoryStack_t*) sphThreadGet ( g_tTLSMemCategory );
589 return pTLS ? pTLS->Top() : MEM_CORE;
590 }
591
592
593 // human readable category names
594 #define MEM_CATEGORY(_arg) #_arg
595 static const char* g_dMemCategoryName[] = { MEM_CATEGORIES };
596 #undef MEM_CATEGORY
597
598
sphMemStatDump(int iFD)599 void sphMemStatDump ( int iFD )
600 {
601 int64_t iSize = 0;
602 int iCount = 0;
603 for ( int i=0; i<MEM_TOTAL; i++ )
604 {
605 iSize += (int64_t) g_dMemCategoryStat[i].m_iSize;
606 iCount += g_dMemCategoryStat[i].m_iCount;
607 }
608
609 sphSafeInfo ( iFD, "%-24s allocs-count=%d, mem-total=%d.%d Mb", "(total)", iCount,
610 (int)(iSize/1048576), (int)( (iSize*10/1048576)%10 ) );
611
612 for ( int i=0; i<MEM_TOTAL; i++ )
613 if ( g_dMemCategoryStat[i].m_iCount>0 )
614 {
615 iSize = (int64_t) g_dMemCategoryStat[i].m_iSize;
616 sphSafeInfo ( iFD, "%-24s allocs-count=%d, mem-total=%d.%d Mb",
617 g_dMemCategoryName[i], g_dMemCategoryStat[i].m_iCount,
618 (int)(iSize/1048576), (int)( (iSize*10/1048576)%10 ) );
619 }
620 }
621
622 //////////////////////////////////////////////////////////////////////////////
623 // PRODUCTION MEMORY MANAGER
624 //////////////////////////////////////////////////////////////////////////////
625
626 #else
627 #ifndef SPH_DONT_OVERRIDE_MEMROUTINES
628
operator new(size_t iSize)629 void * operator new ( size_t iSize )
630 {
631 void * pResult = ::malloc ( iSize );
632 if ( !pResult )
633 sphDieRestart ( "out of memory (unable to allocate " UINT64_FMT " bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
634 return pResult;
635 }
636
637
operator new[](size_t iSize)638 void * operator new [] ( size_t iSize )
639 {
640 void * pResult = ::malloc ( iSize );
641 if ( !pResult )
642 sphDieRestart ( "out of memory (unable to allocate " UINT64_FMT " bytes)", (uint64_t)iSize ); // FIXME! this may fail with malloc error too
643 return pResult;
644 }
645
646 #if USE_RE2
operator delete(void * pPtr)647 void operator delete ( void * pPtr ) throw ()
648 #else
649 void operator delete ( void * pPtr )
650 #endif
651 {
652 if ( pPtr )
653 ::free ( pPtr );
654 }
655
656
657 #if USE_RE2
operator delete[](void * pPtr)658 void operator delete [] ( void * pPtr ) throw ()
659 #else
660 void operator delete [] ( void * pPtr )
661 #endif
662 {
663 if ( pPtr )
664 ::free ( pPtr );
665 }
666
667 #endif // SPH_DONT_OVERRIDE_MEMROUTINES
668 #endif // SPH_ALLOCS_PROFILER
669 #endif // SPH_DEBUG_LEAKS
670
671 //////////////////////////////////////////////////////////////////////////
672
673 // now let the rest of sphinxstd use proper new
674 #if SPH_DEBUG_LEAKS || SPH_ALLOCS_PROFILER
675 #undef new
676 #define new new(__FILE__,__LINE__)
677 #endif
678
679 /////////////////////////////////////////////////////////////////////////////
680 // HELPERS
681 /////////////////////////////////////////////////////////////////////////////
682
683 static SphDieCallback_t g_pfDieCallback = NULL;
684
685
sphSetDieCallback(SphDieCallback_t pfDieCallback)686 void sphSetDieCallback ( SphDieCallback_t pfDieCallback )
687 {
688 g_pfDieCallback = pfDieCallback;
689 }
690
691
sphDie(const char * sTemplate,...)692 void sphDie ( const char * sTemplate, ... )
693 {
694 char sBuf[1024];
695
696 va_list ap;
697 va_start ( ap, sTemplate );
698 vsnprintf ( sBuf, sizeof(sBuf), sTemplate, ap );
699 va_end ( ap );
700
701 // if there's no callback,
702 // or if callback returns true,
703 // log to stdout
704 if ( !g_pfDieCallback || g_pfDieCallback ( sBuf ) )
705 fprintf ( stdout, "FATAL: %s\n", sBuf );
706
707 exit ( 1 );
708 }
709
710
sphDieRestart(const char * sTemplate,...)711 void sphDieRestart ( const char * sTemplate, ... )
712 {
713 char sBuf[1024];
714
715 va_list ap;
716 va_start ( ap, sTemplate );
717 vsnprintf ( sBuf, sizeof(sBuf), sTemplate, ap );
718 va_end ( ap );
719
720 // if there's no callback,
721 // or if callback returns true,
722 // log to stdout
723 if ( !g_pfDieCallback || g_pfDieCallback ( sBuf ) )
724 fprintf ( stdout, "FATAL: %s\n", sBuf );
725
726 exit ( 2 ); // almost CRASH_EXIT
727 }
728
729 //////////////////////////////////////////////////////////////////////////
730 // RANDOM NUMBERS GENERATOR
731 //////////////////////////////////////////////////////////////////////////
732
733 /// MWC (Multiply-With-Carry) RNG, invented by George Marsaglia
734 static DWORD g_dRngState[5] = { 0x95d3474bUL, 0x035cf1f7UL, 0xfd43995fUL, 0x5dfc55fbUL, 0x334a9229UL };
735
736
737 /// seed
sphSrand(DWORD uSeed)738 void sphSrand ( DWORD uSeed )
739 {
740 for ( int i=0; i<5; i++ )
741 {
742 uSeed = uSeed*29943829 - 1;
743 g_dRngState[i] = uSeed;
744 }
745 for ( int i=0; i<19; i++ )
746 sphRand();
747 }
748
749
750 /// auto-seed RNG based on time and PID
sphAutoSrand()751 void sphAutoSrand ()
752 {
753 // get timestamp
754 #if !USE_WINDOWS
755 struct timeval tv;
756 gettimeofday ( &tv, NULL );
757 #else
758 #define getpid() GetCurrentProcessId()
759
760 struct
761 {
762 time_t tv_sec;
763 DWORD tv_usec;
764 } tv;
765
766 FILETIME ft;
767 GetSystemTimeAsFileTime ( &ft );
768
769 uint64_t ts = ( uint64_t(ft.dwHighDateTime)<<32 ) + uint64_t(ft.dwLowDateTime) - 116444736000000000ULL; // Jan 1, 1970 magic
770 ts /= 10; // to microseconds
771 tv.tv_sec = (DWORD)(ts/1000000);
772 tv.tv_usec = (DWORD)(ts%1000000);
773 #endif
774
775 // twist and shout
776 sphSrand ( sphRand() ^ DWORD(tv.tv_sec) ^ (DWORD(tv.tv_usec) + DWORD(getpid())) );
777 }
778
779
780 /// generate another dword
sphRand()781 DWORD sphRand ()
782 {
783 uint64_t uSum;
784 uSum =
785 (uint64_t)g_dRngState[0] * (uint64_t)5115 +
786 (uint64_t)g_dRngState[1] * (uint64_t)1776 +
787 (uint64_t)g_dRngState[2] * (uint64_t)1492 +
788 (uint64_t)g_dRngState[3] * (uint64_t)2111111111UL +
789 (uint64_t)g_dRngState[4];
790 g_dRngState[3] = g_dRngState[2];
791 g_dRngState[2] = g_dRngState[1];
792 g_dRngState[1] = g_dRngState[0];
793 g_dRngState[4] = (DWORD)( uSum>>32 );
794 g_dRngState[0] = (DWORD)uSum;
795 return g_dRngState[0];
796 }
797
798 //////////////////////////////////////////////////////////////////////////
799
800 #if !USE_WINDOWS
CSphProcessSharedMutex(int iExtraSize)801 CSphProcessSharedMutex::CSphProcessSharedMutex ( int iExtraSize )
802 {
803 m_pMutex = NULL;
804
805 #ifdef __FreeBSD__
806 CSphString sError, sWarning;
807 if ( !m_pStorage.Alloc ( sizeof(sem_t) + iExtraSize, sError, sWarning ) )
808 {
809 m_sError.SetSprintf ( "storage.alloc, error='%s', warning='%s'", sError.cstr(), sWarning.cstr() );
810 return;
811 }
812
813 m_pMutex = (sem_t*) m_pStorage.GetWritePtr ();
814 int iRes = sem_init ( m_pMutex, 1, 1 );
815 if ( iRes )
816 {
817 m_sError.SetSprintf ( "sem_init, errno=%d ", iRes );
818 m_pMutex = NULL;
819 m_pStorage.Reset ();
820 return;
821 }
822 #else
823 pthread_mutexattr_t tAttr;
824 int iRes = pthread_mutexattr_init ( &tAttr );
825 if ( iRes )
826 {
827 m_sError.SetSprintf ( "pthread_mutexattr_init, errno=%d", iRes );
828 return;
829 }
830 iRes = pthread_mutexattr_setpshared ( &tAttr, PTHREAD_PROCESS_SHARED );
831 if ( iRes )
832 {
833 m_sError.SetSprintf ( "pthread_mutexattr_setpshared, errno = %d", iRes );
834 pthread_mutexattr_destroy ( &tAttr );
835 return;
836 }
837
838 CSphString sError, sWarning;
839 if ( !m_pStorage.Alloc ( sizeof(pthread_mutex_t) + iExtraSize, sError, sWarning ) )
840 {
841 m_sError.SetSprintf ( "storage.alloc, error='%s', warning='%s'", sError.cstr(), sWarning.cstr() );
842 pthread_mutexattr_destroy ( &tAttr );
843 return;
844 }
845
846 m_pMutex = (pthread_mutex_t*) m_pStorage.GetWritePtr ();
847 iRes = pthread_mutex_init ( m_pMutex, &tAttr );
848
849 if ( iRes )
850 {
851 m_sError.SetSprintf ( "pthread_mutex_init, errno=%d ", iRes );
852 pthread_mutexattr_destroy ( &tAttr );
853 m_pMutex = NULL;
854 m_pStorage.Reset ();
855 return;
856 }
857
858 iRes = pthread_mutexattr_destroy ( &tAttr );
859 if ( iRes )
860 {
861 m_sError.SetSprintf ( "pthread_mutexattr_destroy, errno = %d", iRes );
862 return;
863 }
864 #endif // __FreeBSD__
865 }
866
~CSphProcessSharedMutex()867 CSphProcessSharedMutex::~CSphProcessSharedMutex()
868 {
869 if ( m_pMutex )
870 {
871 #ifdef __FreeBSD__
872 sem_destroy ( m_pMutex );
873 #else
874 pthread_mutex_destroy ( m_pMutex );
875 #endif
876 m_pMutex = NULL;
877 }
878 }
879 #else
CSphProcessSharedMutex(int)880 CSphProcessSharedMutex::CSphProcessSharedMutex ( int )
881 {
882 m_tLock.Init();
883 }
884
~CSphProcessSharedMutex()885 CSphProcessSharedMutex::~CSphProcessSharedMutex()
886 {
887 m_tLock.Done();
888 }
889 #endif
890
891
Lock()892 void CSphProcessSharedMutex::Lock ()
893 {
894 #if !USE_WINDOWS
895 #ifdef __FreeBSD__
896 if ( m_pMutex )
897 sem_wait ( m_pMutex );
898 #else
899 if ( m_pMutex )
900 pthread_mutex_lock ( m_pMutex );
901 #endif
902 #else
903 m_tLock.Lock();
904 #endif
905 }
906
907
Unlock()908 void CSphProcessSharedMutex::Unlock ()
909 {
910 #if !USE_WINDOWS
911 #ifdef __FreeBSD__
912 if ( m_pMutex )
913 sem_post ( m_pMutex );
914 #else
915 if ( m_pMutex )
916 pthread_mutex_unlock ( m_pMutex );
917 #endif
918 #else
919 m_tLock.Unlock();
920 #endif
921 }
922
923
924 #if USE_WINDOWS
TimedLock(int) const925 bool CSphProcessSharedMutex::TimedLock ( int ) const
926 {
927 return false;
928 #else
929 bool CSphProcessSharedMutex::TimedLock ( int tmSpin ) const
930 {
931 if ( !m_pMutex )
932 return false;
933
934 #ifdef __FreeBSD__
935 struct timespec tp;
936 clock_gettime ( CLOCK_REALTIME, &tp );
937
938 tp.tv_nsec += tmSpin * 1000;
939 if ( tp.tv_nsec > 1000000 )
940 {
941 int iDelta = (int)( tp.tv_nsec / 1000000 );
942 tp.tv_sec += iDelta * 1000000;
943 tp.tv_nsec -= iDelta * 1000000;
944 }
945
946 return ( sem_timedwait ( m_pMutex, &tp )==0 );
947 #else
948 #if defined(HAVE_PTHREAD_MUTEX_TIMEDLOCK) && defined(HAVE_CLOCK_GETTIME)
949 struct timespec tp;
950 clock_gettime ( CLOCK_REALTIME, &tp );
951
952 tp.tv_nsec += tmSpin * 1000;
953 if ( tp.tv_nsec > 1000000 )
954 {
955 int iDelta = (int)( tp.tv_nsec / 1000000 );
956 tp.tv_sec += iDelta * 1000000;
957 tp.tv_nsec -= iDelta * 1000000;
958 }
959
960 return ( pthread_mutex_timedlock ( m_pMutex, &tp )==0 );
961 #else
962 int iRes = EBUSY;
963 int64_t tmTill = sphMicroTimer() + tmSpin;
964 do
965 {
966 iRes = pthread_mutex_trylock ( m_pMutex );
967 if ( iRes==EBUSY )
968 sphSleepMsec ( 0 );
969 } while ( iRes==EBUSY && sphMicroTimer()<tmTill );
970
971 if ( iRes==EBUSY )
972 iRes = pthread_mutex_trylock ( m_pMutex );
973
974 return iRes==0;
975 #endif // HAVE_PTHREAD_MUTEX_TIMEDLOCK && HAVE_CLOCK_GETTIME
976 #endif // __FreeBSD__
977 #endif // USE_WINDOWS
978 }
979
980
981 BYTE * CSphProcessSharedMutex::GetSharedData() const
982 {
983 #if !USE_WINDOWS
984 #ifdef __FreeBSD__
985 return m_pStorage.GetWritePtr () + sizeof ( sem_t );
986 #else
987 return m_pStorage.GetWritePtr () + sizeof ( pthread_mutex_t );
988 #endif
989 #else
990 return NULL;
991 #endif
992 }
993
994
995 const char * CSphProcessSharedMutex::GetError() const
996 {
997 const char * sError = NULL;
998 #if !USE_WINDOWS
999 sError = m_sError.cstr();
1000 #endif
1001 return sError;
1002 }
1003
1004
1005 //////////////////////////////////////////////////////////////////////////
1006 // THREADING FUNCTIONS
1007 //////////////////////////////////////////////////////////////////////////
1008
1009 // This is a working context for a thread wrapper. It wraps every thread to
1010 // store information about it's stack size, cleanup threads and something else.
1011 // This struct always should be allocated in the heap, cause wrapper need
1012 // to see it all the time and it frees it out of the heap by itself. Wrapper thread function
1013 // receives as an argument a pointer to ThreadCall_t with one function pointer to
1014 // a main thread function. Afterwards, thread can set up one or more cleanup functions
1015 // which will be executed by a wrapper in the linked list order after it dies.
1016 struct ThreadCall_t
1017 {
1018 void ( *m_pCall )( void * pArg );
1019 void * m_pArg;
1020 #if USE_GPROF
1021 pthread_mutex_t m_dlock;
1022 pthread_cond_t m_dwait;
1023 itimerval m_ditimer;
1024 #endif
1025 ThreadCall_t * m_pNext;
1026 };
1027 static SphThreadKey_t g_tThreadCleanupKey;
1028 static SphThreadKey_t g_tMyThreadStack;
1029
1030
1031 #if USE_WINDOWS
1032 #define SPH_THDFUNC DWORD __stdcall
1033 #else
1034 #define SPH_THDFUNC void *
1035 #endif
1036
1037 SPH_THDFUNC sphThreadProcWrapper ( void * pArg )
1038 {
1039 // This is the first local variable in the new thread. So, its address is the top of the stack.
1040 // We need to know thread stack size for both expression and query evaluating engines.
1041 // We store expressions as a linked tree of structs and execution is a calls of mutually
1042 // recursive methods. Before executing we compute tree height and multiply it by a constant
1043 // with experimentally measured value to check whether we have enough stack to execute current query.
1044 // The check is not ideal and do not work for all compilers and compiler settings.
1045 char cTopOfMyStack;
1046 assert ( sphThreadGet ( g_tThreadCleanupKey )==NULL );
1047 assert ( sphThreadGet ( g_tMyThreadStack )==NULL );
1048
1049 #if SPH_ALLOCS_PROFILER
1050 MemCategoryStack_t * pTLS = sphMemStatThdInit();
1051 #endif
1052
1053 #if USE_GPROF
1054 // Set the profile timer value
1055 setitimer ( ITIMER_PROF, &( (ThreadCall_t*) pArg )->m_ditimer, NULL );
1056
1057 // Tell the calling thread that we don't need its data anymore
1058 pthread_mutex_lock ( &( (ThreadCall_t*) pArg)->m_dlock );
1059 pthread_cond_signal ( &( (ThreadCall_t*) pArg)->m_dwait );
1060 pthread_mutex_unlock ( &( (ThreadCall_t*) pArg)->m_dlock );
1061 #endif
1062
1063 ThreadCall_t * pCall = (ThreadCall_t*) pArg;
1064 MemorizeStack ( & cTopOfMyStack );
1065 pCall->m_pCall ( pCall->m_pArg );
1066 SafeDelete ( pCall );
1067
1068 ThreadCall_t * pCleanup = (ThreadCall_t*) sphThreadGet ( g_tThreadCleanupKey );
1069 while ( pCleanup )
1070 {
1071 pCall = pCleanup;
1072 pCall->m_pCall ( pCall->m_pArg );
1073 pCleanup = pCall->m_pNext;
1074 SafeDelete ( pCall );
1075 }
1076
1077 #if SPH_ALLOCS_PROFILER
1078 sphMemStatThdCleanup ( pTLS );
1079 #endif
1080
1081 return 0;
1082 }
1083
1084 #if !USE_WINDOWS
1085 void * sphThreadInit ( bool bDetached )
1086 #else
1087 void * sphThreadInit ( bool )
1088 #endif
1089 {
1090 static bool bInit = false;
1091 #if !USE_WINDOWS
1092 static pthread_attr_t tJoinableAttr;
1093 static pthread_attr_t tDetachedAttr;
1094 #endif
1095
1096 if ( !bInit )
1097 {
1098 #if SPH_DEBUG_LEAKS || SPH_ALLOCS_PROFILER
1099 sphMemStatInit();
1100 #endif
1101
1102 // we're single-threaded yet, right?!
1103 if ( !sphThreadKeyCreate ( &g_tThreadCleanupKey ) )
1104 sphDie ( "FATAL: sphThreadKeyCreate() failed" );
1105
1106 if ( !sphThreadKeyCreate ( &g_tMyThreadStack ) )
1107 sphDie ( "FATAL: sphThreadKeyCreate() failed" );
1108
1109 #if !USE_WINDOWS
1110 if ( pthread_attr_init ( &tJoinableAttr ) )
1111 sphDie ( "FATAL: pthread_attr_init( joinable ) failed" );
1112
1113 if ( pthread_attr_init ( &tDetachedAttr ) )
1114 sphDie ( "FATAL: pthread_attr_init( detached ) failed" );
1115
1116 if ( pthread_attr_setdetachstate ( &tDetachedAttr, PTHREAD_CREATE_DETACHED ) )
1117 sphDie ( "FATAL: pthread_attr_setdetachstate( detached ) failed" );
1118 #endif
1119 bInit = true;
1120 }
1121 #if !USE_WINDOWS
1122 if ( pthread_attr_setstacksize ( &tJoinableAttr, g_iThreadStackSize + PTHREAD_STACK_MIN ) )
1123 sphDie ( "FATAL: pthread_attr_setstacksize( joinable ) failed" );
1124
1125 if ( pthread_attr_setstacksize ( &tDetachedAttr, g_iThreadStackSize + PTHREAD_STACK_MIN ) )
1126 sphDie ( "FATAL: pthread_attr_setstacksize( detached ) failed" );
1127
1128 return bDetached ? &tDetachedAttr : &tJoinableAttr;
1129 #else
1130 return NULL;
1131 #endif
1132 }
1133
1134
1135 #if SPH_DEBUG_LEAKS || SPH_ALLOCS_PROFILER
1136 void sphThreadDone ( int iFD )
1137 {
1138 sphMemStatDump ( iFD );
1139 sphMemStatDone();
1140 }
1141 #else
1142 void sphThreadDone ( int )
1143 {
1144 }
1145 #endif
1146
1147
1148 bool sphThreadCreate ( SphThread_t * pThread, void (*fnThread)(void*), void * pArg, bool bDetached )
1149 {
1150 // we can not put this on current stack because wrapper need to see
1151 // it all the time and it will destroy this data from heap by itself
1152 ThreadCall_t * pCall = new ThreadCall_t;
1153 pCall->m_pCall = fnThread;
1154 pCall->m_pArg = pArg;
1155 pCall->m_pNext = NULL;
1156
1157 // create thread
1158 #if USE_WINDOWS
1159 sphThreadInit ( bDetached );
1160 *pThread = CreateThread ( NULL, g_iThreadStackSize, sphThreadProcWrapper, pCall, 0, NULL );
1161 if ( *pThread )
1162 return true;
1163 #else
1164
1165 #if USE_GPROF
1166 getitimer ( ITIMER_PROF, &pCall->m_ditimer );
1167 pthread_cond_init ( &pCall->m_dwait, NULL );
1168 pthread_mutex_init ( &pCall->m_dlock, NULL );
1169 pthread_mutex_lock ( &pCall->m_dlock );
1170 #endif
1171
1172 void * pAttr = sphThreadInit ( bDetached );
1173 errno = pthread_create ( pThread, (pthread_attr_t*) pAttr, sphThreadProcWrapper, pCall );
1174
1175 #if USE_GPROF
1176 if ( !errno )
1177 pthread_cond_wait ( &pCall->m_dwait, &pCall->m_dlock );
1178
1179 pthread_mutex_unlock ( &pCall->m_dlock );
1180 pthread_mutex_destroy ( &pCall->m_dlock );
1181 pthread_cond_destroy ( &pCall->m_dwait );
1182 #endif
1183
1184 if ( !errno )
1185 return true;
1186
1187 #endif
1188
1189 // thread creation failed so we need to cleanup ourselves
1190 SafeDelete ( pCall );
1191 return false;
1192 }
1193
1194
1195 bool sphThreadJoin ( SphThread_t * pThread )
1196 {
1197 #if USE_WINDOWS
1198 DWORD uWait = WaitForSingleObject ( *pThread, INFINITE );
1199 CloseHandle ( *pThread );
1200 *pThread = NULL;
1201 return ( uWait==WAIT_OBJECT_0 || uWait==WAIT_ABANDONED );
1202 #else
1203 return pthread_join ( *pThread, NULL )==0;
1204 #endif
1205 }
1206
1207 // Adds a function call (a new task for a wrapper) to a linked list
1208 // of thread contexts. They will be executed one by one right after
1209 // the main thread ends its execution. This is a way for a wrapper
1210 // to free local resources allocated by its main thread.
1211 void sphThreadOnExit ( void (*fnCleanup)(void*), void * pArg )
1212 {
1213 ThreadCall_t * pCleanup = new ThreadCall_t;
1214 pCleanup->m_pCall = fnCleanup;
1215 pCleanup->m_pArg = pArg;
1216 pCleanup->m_pNext = (ThreadCall_t*) sphThreadGet ( g_tThreadCleanupKey );
1217 sphThreadSet ( g_tThreadCleanupKey, pCleanup );
1218 }
1219
1220
1221 bool sphThreadKeyCreate ( SphThreadKey_t * pKey )
1222 {
1223 #if USE_WINDOWS
1224 *pKey = TlsAlloc();
1225 return *pKey!=TLS_OUT_OF_INDEXES;
1226 #else
1227 return pthread_key_create ( pKey, NULL )==0;
1228 #endif
1229 }
1230
1231
1232 void sphThreadKeyDelete ( SphThreadKey_t tKey )
1233 {
1234 #if USE_WINDOWS
1235 TlsFree ( tKey );
1236 #else
1237 pthread_key_delete ( tKey );
1238 #endif
1239 }
1240
1241
1242 void * sphThreadGet ( SphThreadKey_t tKey )
1243 {
1244 #if USE_WINDOWS
1245 return TlsGetValue ( tKey );
1246 #else
1247 return pthread_getspecific ( tKey );
1248 #endif
1249 }
1250
1251 void * sphMyStack ()
1252 {
1253 return sphThreadGet ( g_tMyThreadStack );
1254 }
1255
1256
1257 int64_t sphGetStackUsed()
1258 {
1259 BYTE cStack;
1260 BYTE * pStackTop = (BYTE*)sphMyStack();
1261 if ( !pStackTop )
1262 return 0;
1263 int64_t iHeight = pStackTop - &cStack;
1264 return ( iHeight>=0 ) ? iHeight : -iHeight;
1265 }
1266
1267 void sphSetMyStackSize ( int iStackSize )
1268 {
1269 g_iThreadStackSize = iStackSize;
1270 sphThreadInit ( false );
1271 }
1272
1273
1274 void MemorizeStack ( void* PStack )
1275 {
1276 sphThreadSet ( g_tMyThreadStack, PStack );
1277 }
1278
1279
1280 bool sphThreadSet ( SphThreadKey_t tKey, void * pValue )
1281 {
1282 #if USE_WINDOWS
1283 return TlsSetValue ( tKey, pValue )!=FALSE;
1284 #else
1285 return pthread_setspecific ( tKey, pValue )==0;
1286 #endif
1287 }
1288
1289 #if !USE_WINDOWS
1290 bool sphIsLtLib()
1291 {
1292 #ifndef _CS_GNU_LIBPTHREAD_VERSION
1293 return false;
1294 #else
1295 char buff[64];
1296 confstr ( _CS_GNU_LIBPTHREAD_VERSION, buff, 64 );
1297
1298 if ( !strncasecmp ( buff, "linuxthreads", 12 ) )
1299 return true;
1300 return false;
1301 #endif
1302 }
1303 #endif
1304
1305 //////////////////////////////////////////////////////////////////////////
1306 // MUTEX and EVENT
1307 //////////////////////////////////////////////////////////////////////////
1308
1309 #if USE_WINDOWS
1310
1311 // Windows mutex implementation
1312
1313 bool CSphMutex::Init ()
1314 {
1315 assert ( !m_bInitialized );
1316 m_hMutex = CreateMutex ( NULL, FALSE, NULL );
1317 m_bInitialized = ( m_hMutex!=NULL );
1318 return m_bInitialized;
1319 }
1320
1321 bool CSphMutex::Done ()
1322 {
1323 if ( !m_bInitialized )
1324 return true;
1325
1326 m_bInitialized = false;
1327 return CloseHandle ( m_hMutex )==TRUE;
1328 }
1329
1330 bool CSphMutex::Lock ()
1331 {
1332 assert ( m_bInitialized );
1333 DWORD uWait = WaitForSingleObject ( m_hMutex, INFINITE );
1334 return ( uWait!=WAIT_FAILED && uWait!=WAIT_TIMEOUT );
1335 }
1336
1337 bool CSphMutex::Unlock ()
1338 {
1339 assert ( m_bInitialized );
1340 return ReleaseMutex ( m_hMutex )==TRUE;
1341 }
1342
1343 bool CSphAutoEvent::Init ( CSphMutex * )
1344 {
1345 m_bSent = false;
1346 m_hEvent = CreateEvent ( NULL, FALSE, FALSE, NULL );
1347 m_bInitialized = ( m_hEvent!=0 );
1348 return m_bInitialized;
1349 }
1350
1351 bool CSphAutoEvent::Done()
1352 {
1353 if ( !m_bInitialized )
1354 return true;
1355
1356 m_bInitialized = false;
1357 return CloseHandle ( m_hEvent )==TRUE;
1358 }
1359
1360 void CSphAutoEvent::SetEvent()
1361 {
1362 ::SetEvent ( m_hEvent );
1363 m_bSent = true;
1364 }
1365
1366 bool CSphAutoEvent::WaitEvent()
1367 {
1368 if ( m_bSent )
1369 {
1370 m_bSent = false;
1371 return true;
1372 }
1373 DWORD uWait = WaitForSingleObject ( m_hEvent, INFINITE );
1374 return !( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT );
1375 }
1376
1377 #else
1378
1379 // UNIX mutex implementation
1380
1381 bool CSphMutex::Init ()
1382 {
1383 assert ( !m_bInitialized );
1384 m_bInitialized = ( pthread_mutex_init ( &m_tMutex, NULL )==0 );
1385 return m_bInitialized;
1386 }
1387
1388 bool CSphMutex::Done ()
1389 {
1390 if ( !m_bInitialized )
1391 return true;
1392
1393 m_bInitialized = false;
1394 return pthread_mutex_destroy ( &m_tMutex )==0;
1395 }
1396
1397 bool CSphMutex::Lock ()
1398 {
1399 assert ( m_bInitialized );
1400 return ( pthread_mutex_lock ( &m_tMutex )==0 );
1401 }
1402
1403 bool CSphMutex::Unlock ()
1404 {
1405 assert ( m_bInitialized );
1406 return ( pthread_mutex_unlock ( &m_tMutex )==0 );
1407 }
1408
1409 bool CSphAutoEvent::Init ( CSphMutex * pMutex )
1410 {
1411 m_bSent = false;
1412 assert ( pMutex );
1413 if ( !pMutex )
1414 return false;
1415 m_pMutex = pMutex->GetInternalMutex();
1416 m_bInitialized = ( pthread_cond_init ( &m_tCond, NULL )==0 );
1417 return m_bInitialized;
1418 }
1419
1420 bool CSphAutoEvent::Done ()
1421 {
1422 if ( !m_bInitialized )
1423 return true;
1424
1425 m_bInitialized = false;
1426 return ( pthread_cond_destroy ( &m_tCond ) )==0;
1427 }
1428
1429 void CSphAutoEvent::SetEvent ()
1430 {
1431 if ( !m_bInitialized )
1432 return;
1433
1434 pthread_cond_signal ( &m_tCond ); // locking is done from outside
1435 m_bSent = true;
1436 }
1437
1438 bool CSphAutoEvent::WaitEvent ()
1439 {
1440 if ( !m_bInitialized )
1441 return true;
1442 pthread_mutex_lock ( m_pMutex );
1443 if ( !m_bSent )
1444 pthread_cond_wait ( &m_tCond, m_pMutex );
1445 m_bSent = false;
1446 pthread_mutex_unlock ( m_pMutex );
1447 return true;
1448 }
1449
1450 #endif
1451
1452 //////////////////////////////////////////////////////////////////////////
1453 // RWLOCK
1454 //////////////////////////////////////////////////////////////////////////
1455
1456 #if USE_WINDOWS
1457
1458 // Windows rwlock implementation
1459
1460 CSphRwlock::CSphRwlock ()
1461 : m_bInitialized ( false )
1462 , m_hWriteMutex ( NULL )
1463 , m_hReadEvent ( NULL )
1464 , m_iReaders ( 0 )
1465 {}
1466
1467
1468 bool CSphRwlock::Init ( bool )
1469 {
1470 assert ( !m_bInitialized );
1471 assert ( !m_hWriteMutex && !m_hReadEvent && !m_iReaders );
1472
1473 m_hReadEvent = CreateEvent ( NULL, TRUE, FALSE, NULL );
1474 if ( !m_hReadEvent )
1475 return false;
1476
1477 m_hWriteMutex = CreateMutex ( NULL, FALSE, NULL );
1478 if ( !m_hWriteMutex )
1479 {
1480 CloseHandle ( m_hReadEvent );
1481 m_hReadEvent = NULL;
1482 return false;
1483 }
1484 m_bInitialized = true;
1485 return true;
1486 }
1487
1488
1489 bool CSphRwlock::Done ()
1490 {
1491 if ( !m_bInitialized )
1492 return true;
1493
1494 if ( !CloseHandle ( m_hReadEvent ) )
1495 return false;
1496 m_hReadEvent = NULL;
1497
1498 if ( !CloseHandle ( m_hWriteMutex ) )
1499 return false;
1500 m_hWriteMutex = NULL;
1501
1502 m_iReaders = 0;
1503 m_bInitialized = false;
1504 return true;
1505 }
1506
1507
1508 const char * CSphRwlock::GetError () const
1509 {
1510 return m_sError.cstr();
1511 }
1512
1513
1514 bool CSphRwlock::ReadLock ()
1515 {
1516 assert ( m_bInitialized );
1517
1518 DWORD uWait = WaitForSingleObject ( m_hWriteMutex, INFINITE );
1519 if ( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT )
1520 return false;
1521
1522 // got the writer mutex, can't be locked for write
1523 // so it's OK to add the reader lock, then free the writer mutex
1524 // writer mutex also protects readers counter
1525 InterlockedIncrement ( &m_iReaders );
1526
1527 // reset writer lock event, we just got ourselves a reader
1528 if ( !ResetEvent ( m_hReadEvent ) )
1529 return false;
1530
1531 // release writer lock
1532 return ReleaseMutex ( m_hWriteMutex )==TRUE;
1533 }
1534
1535
1536 bool CSphRwlock::WriteLock ()
1537 {
1538 assert ( m_bInitialized );
1539
1540 // try to acquire writer mutex
1541 DWORD uWait = WaitForSingleObject ( m_hWriteMutex, INFINITE );
1542 if ( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT )
1543 return false;
1544
1545 // got the writer mutex, no pending readers, rock'n'roll
1546 if ( !m_iReaders )
1547 return true;
1548
1549 // got the writer mutex, but still have to wait for all readers to complete
1550 uWait = WaitForSingleObject ( m_hReadEvent, INFINITE );
1551 if ( uWait==WAIT_FAILED || uWait==WAIT_TIMEOUT )
1552 {
1553 // wait failed, well then, release writer mutex
1554 ReleaseMutex ( m_hWriteMutex );
1555 return false;
1556 }
1557 return true;
1558 }
1559
1560
1561 bool CSphRwlock::Unlock ()
1562 {
1563 assert ( m_bInitialized );
1564
1565 // are we unlocking a writer?
1566 if ( ReleaseMutex ( m_hWriteMutex ) )
1567 return true; // yes we are
1568
1569 if ( GetLastError()!=ERROR_NOT_OWNER )
1570 return false; // some unexpected error
1571
1572 // writer mutex wasn't mine; we must have a read lock
1573 if ( !m_iReaders )
1574 return true; // could this ever happen?
1575
1576 // atomically decrement reader counter
1577 if ( InterlockedDecrement ( &m_iReaders ) )
1578 return true; // there still are pending readers
1579
1580 // no pending readers, fire the event for write lock
1581 return SetEvent ( m_hReadEvent )==TRUE;
1582 }
1583
1584 #else
1585
1586 // UNIX rwlock implementation (pthreads wrapper)
1587
1588 CSphRwlock::CSphRwlock ()
1589 : m_bInitialized ( false )
1590 {}
1591
1592 bool CSphRwlock::Init ( bool bProcessShared )
1593 {
1594 assert ( !m_bInitialized );
1595
1596 #ifdef __FreeBSD__
1597 if ( bProcessShared )
1598 {
1599 m_sError = "process shared rwlock is not supported by FreeBSD";
1600 return false;
1601 }
1602 #endif
1603
1604 pthread_rwlockattr_t tAttr;
1605 pthread_rwlockattr_t * pAttrUsed = NULL;
1606 int iRes;
1607
1608 if ( bProcessShared )
1609 {
1610 iRes = pthread_rwlockattr_init ( &tAttr );
1611 if ( iRes )
1612 {
1613 m_sError.SetSprintf ( "pthread_rwlockattr_init, errno=%d", iRes );
1614 return false;
1615 }
1616 iRes = pthread_rwlockattr_setpshared ( &tAttr, PTHREAD_PROCESS_SHARED );
1617 if ( iRes )
1618 {
1619 m_sError.SetSprintf ( "pthread_rwlockattr_setpshared, errno = %d", iRes );
1620 pthread_rwlockattr_destroy ( &tAttr );
1621 return false;
1622 }
1623
1624 pAttrUsed = &tAttr;
1625 }
1626
1627 iRes = pthread_rwlock_init ( &m_tLock, pAttrUsed );
1628 if ( iRes )
1629 {
1630 m_sError.SetSprintf ( "pthread_rwlock_init, errno = %d", iRes );
1631 if ( pAttrUsed )
1632 pthread_rwlockattr_destroy ( pAttrUsed );
1633 return false;
1634 }
1635
1636 if ( pAttrUsed )
1637 {
1638 iRes = pthread_rwlockattr_destroy ( pAttrUsed );
1639 if ( iRes )
1640 {
1641 m_sError.SetSprintf ( "pthread_rwlockattr_destroy, errno = %d", iRes );
1642 return false;
1643 }
1644 }
1645
1646 m_bInitialized = true;
1647
1648 return true;
1649 }
1650
1651 bool CSphRwlock::Done ()
1652 {
1653 if ( !m_bInitialized )
1654 return true;
1655
1656 m_bInitialized = !( pthread_rwlock_destroy ( &m_tLock )==0 );
1657 return !m_bInitialized;
1658 }
1659
1660 const char * CSphRwlock::GetError () const
1661 {
1662 return m_sError.cstr();
1663 }
1664
1665 bool CSphRwlock::ReadLock ()
1666 {
1667 assert ( m_bInitialized );
1668
1669 return pthread_rwlock_rdlock ( &m_tLock )==0;
1670 }
1671
1672 bool CSphRwlock::WriteLock ()
1673 {
1674 assert ( m_bInitialized );
1675
1676 return pthread_rwlock_wrlock ( &m_tLock )==0;
1677 }
1678
1679 bool CSphRwlock::Unlock ()
1680 {
1681 assert ( m_bInitialized );
1682
1683 return pthread_rwlock_unlock ( &m_tLock )==0;
1684 }
1685
1686 #endif
1687
1688 //////////////////////////////////////////////////////////////////////////
1689
1690 /// microsecond precision timestamp
1691 int64_t sphMicroTimer()
1692 {
1693 #if USE_WINDOWS
1694 // Windows time query
1695 static int64_t iBase = 0;
1696 static int64_t iStart = 0;
1697 static int64_t iFreq = 0;
1698
1699 LARGE_INTEGER iLarge;
1700 if ( !iBase )
1701 {
1702 // get start QPC value
1703 QueryPerformanceFrequency ( &iLarge ); iFreq = iLarge.QuadPart;
1704 QueryPerformanceCounter ( &iLarge ); iStart = iLarge.QuadPart;
1705
1706 // get start UTC timestamp
1707 // assuming it's still approximately the same moment as iStart, give or take a msec or three
1708 FILETIME ft;
1709 GetSystemTimeAsFileTime ( &ft );
1710
1711 iBase = ( int64_t(ft.dwHighDateTime)<<32 ) + int64_t(ft.dwLowDateTime);
1712 iBase = ( iBase - 116444736000000000ULL ) / 10; // rebase from 01 Jan 1601 to 01 Jan 1970, and rescale to 1 usec from 100 ns
1713 }
1714
1715 // we can't easily drag iBase into parens because iBase*iFreq/1000000 overflows 64bit int!
1716 QueryPerformanceCounter ( &iLarge );
1717 return iBase + ( iLarge.QuadPart - iStart )*1000000/iFreq;
1718
1719 #else
1720 // UNIX time query
1721 struct timeval tv;
1722 gettimeofday ( &tv, NULL );
1723 return int64_t(tv.tv_sec)*int64_t(1000000) + int64_t(tv.tv_usec);
1724 #endif // USE_WINDOWS
1725 }
1726
1727 //////////////////////////////////////////////////////////////////////////
1728
1729 int CSphStrHashFunc::Hash ( const CSphString & sKey )
1730 {
1731 return sKey.IsEmpty() ? 0 : sphCRC32 ( sKey.cstr() );
1732 }
1733
1734 //////////////////////////////////////////////////////////////////////////
1735
1736 DWORD g_dSphinxCRC32 [ 256 ] =
1737 {
1738 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
1739 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
1740 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
1741 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
1742 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
1743 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
1744 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
1745 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
1746 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
1747 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
1748 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
1749 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
1750 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
1751 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
1752 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
1753 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
1754 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
1755 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
1756 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
1757 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
1758 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
1759 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
1760 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
1761 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
1762 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
1763 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
1764 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
1765 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
1766 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
1767 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
1768 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
1769 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
1770 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
1771 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
1772 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
1773 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
1774 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
1775 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
1776 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
1777 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
1778 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
1779 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
1780 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
1781 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
1782 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
1783 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
1784 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
1785 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
1786 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
1787 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
1788 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
1789 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
1790 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
1791 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
1792 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
1793 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
1794 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
1795 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
1796 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
1797 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
1798 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
1799 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
1800 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
1801 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
1802 };
1803
1804
1805 DWORD sphCRC32 ( const void * s )
1806 {
1807 // calc CRC
1808 DWORD crc = ~((DWORD)0);
1809 for ( const BYTE * p=(const BYTE*)s; *p; p++ )
1810 crc = (crc >> 8) ^ g_dSphinxCRC32 [ (crc ^ (*p)) & 0xff ];
1811 return ~crc;
1812 }
1813
1814 DWORD sphCRC32 ( const void * s, int iLen )
1815 {
1816 // calc CRC
1817 DWORD crc = ~((DWORD)0);
1818 const BYTE * p = (const BYTE*)s;
1819 const BYTE * pMax = p + iLen;
1820 while ( p<pMax )
1821 crc = (crc >> 8) ^ g_dSphinxCRC32 [ (crc ^ *p++) & 0xff ];
1822 return ~crc;
1823 }
1824
1825 DWORD sphCRC32 ( const void * s, int iLen, DWORD uPrevCRC )
1826 {
1827 // calc CRC
1828 DWORD crc = ~((DWORD)uPrevCRC);
1829 const BYTE * p = (const BYTE*)s;
1830 const BYTE * pMax = p + iLen;
1831 while ( p<pMax )
1832 crc = (crc >> 8) ^ g_dSphinxCRC32 [ (crc ^ *p++) & 0xff ];
1833 return ~crc;
1834 }
1835
1836 #if USE_WINDOWS
1837 template<>
1838 CSphAtomic<long>::operator long()
1839 {
1840 return InterlockedExchangeAdd ( &m_iValue, 0 );
1841 }
1842 template<>
1843 long CSphAtomic<long>::Inc()
1844 {
1845 return InterlockedIncrement ( &m_iValue )-1;
1846 }
1847 template<>
1848 long CSphAtomic<long>::Dec()
1849 {
1850 return InterlockedDecrement ( &m_iValue )+1;
1851 }
1852 #endif
1853
1854 // fast check if we are built with right endianess settings
1855 const char* sphCheckEndian()
1856 {
1857 const char* sErrorMsg = "Oops! It seems that sphinx was built with wrong endianess (cross-compiling?)\n"
1858 #if USE_LITTLE_ENDIAN
1859 "either reconfigure and rebuild, defining ac_cv_c_bigendian=yes in the environment of ./configure script,\n"
1860 "either ensure that '#define USE_LITTLE_ENDIAN = 0' in config/config.h\n";
1861 #else
1862 "either reconfigure and rebuild, defining ac_cv_c_bigendian=no in the environment of ./configure script,\n"
1863 "either ensure that '#define USE_LITTLE_ENDIAN = 1' in config/config.h\n";
1864 #endif
1865
1866 char sMagic[] = "\x01\x02\x03\x04\x05\x06\x07\x08";
1867 unsigned long *pMagic;
1868 unsigned long uResult;
1869 pMagic = (unsigned long*)sMagic;
1870 uResult = 0xFFFFFFFF & (*pMagic);
1871 #if USE_LITTLE_ENDIAN
1872 if ( uResult==0x01020304 || uResult==0x05060708 )
1873 #else
1874 if ( uResult==0x08070605 || uResult==0x04030201 )
1875 #endif
1876 return sErrorMsg;
1877 return NULL;
1878 }
1879
1880 //
1881 // $Id$
1882 //
1883