mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-29 16:47:28 +01:00
716 lines
18 KiB
C++
716 lines
18 KiB
C++
// goodies.h
|
|
// miscellaneous junk
|
|
|
|
/* Copyright 2009 10gen Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "../bson/util/misc.h"
|
|
|
|
namespace mongo {
|
|
|
|
#if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !defined(__sun__)
|
|
|
|
} // namespace mongo
|
|
|
|
#include <pthread.h>
|
|
#include <execinfo.h>
|
|
|
|
namespace mongo {
|
|
|
|
inline pthread_t GetCurrentThreadId() {
|
|
return pthread_self();
|
|
}
|
|
|
|
/* use "addr2line -CFe <exe>" to parse. */
|
|
inline void printStackTrace( ostream &o = cout ) {
|
|
void *b[20];
|
|
size_t size;
|
|
char **strings;
|
|
size_t i;
|
|
|
|
size = backtrace(b, 20);
|
|
strings = backtrace_symbols(b, size);
|
|
|
|
for (i = 0; i < size; i++)
|
|
o << hex << b[i] << dec << ' ';
|
|
o << '\n';
|
|
for (i = 0; i < size; i++)
|
|
o << ' ' << strings[i] << '\n';
|
|
|
|
free (strings);
|
|
}
|
|
#else
|
|
inline void printStackTrace( ostream &o = cout ) { }
|
|
#endif
|
|
|
|
/* set to TRUE if we are exiting */
|
|
extern bool goingAway;
|
|
|
|
/* find the multimap member which matches a particular key and value.
|
|
|
|
note this can be slow if there are a lot with the same key.
|
|
*/
|
|
template<class C,class K,class V> inline typename C::iterator kv_find(C& c, const K& k,const V& v) {
|
|
pair<typename C::iterator,typename C::iterator> p = c.equal_range(k);
|
|
|
|
for ( typename C::iterator it=p.first; it!=p.second; ++it)
|
|
if ( it->second == v )
|
|
return it;
|
|
|
|
return c.end();
|
|
}
|
|
|
|
bool isPrime(int n);
|
|
int nextPrime(int n);
|
|
|
|
inline void dumpmemory(const char *data, int len) {
|
|
if ( len > 1024 )
|
|
len = 1024;
|
|
try {
|
|
const char *q = data;
|
|
const char *p = q;
|
|
while ( len > 0 ) {
|
|
for ( int i = 0; i < 16; i++ ) {
|
|
if ( *p >= 32 && *p <= 126 )
|
|
cout << *p;
|
|
else
|
|
cout << '.';
|
|
p++;
|
|
}
|
|
cout << " ";
|
|
p -= 16;
|
|
for ( int i = 0; i < 16; i++ )
|
|
cout << (unsigned) ((unsigned char)*p++) << ' ';
|
|
cout << endl;
|
|
len -= 16;
|
|
}
|
|
} catch (...) {
|
|
}
|
|
}
|
|
|
|
// PRINT(2+2); prints "2+2: 4"
|
|
#define MONGO_PRINT(x) cout << #x ": " << (x) << endl
|
|
#define PRINT MONGO_PRINT
|
|
// PRINTFL; prints file:line
|
|
#define MONGO_PRINTFL cout << __FILE__ ":" << __LINE__ << endl
|
|
#define PRINTFL MONGO_PRINTFL
|
|
|
|
#undef assert
|
|
#define assert MONGO_assert
|
|
|
|
struct WrappingInt {
|
|
WrappingInt() {
|
|
x = 0;
|
|
}
|
|
WrappingInt(unsigned z) : x(z) { }
|
|
unsigned x;
|
|
operator unsigned() const {
|
|
return x;
|
|
}
|
|
|
|
|
|
static int diff(unsigned a, unsigned b) {
|
|
return a-b;
|
|
}
|
|
bool operator<=(WrappingInt r) {
|
|
// platform dependent
|
|
int df = (r.x - x);
|
|
return df >= 0;
|
|
}
|
|
bool operator>(WrappingInt r) {
|
|
return !(r<=*this);
|
|
}
|
|
};
|
|
|
|
} // namespace mongo
|
|
|
|
#include <ctime>
|
|
|
|
namespace mongo {
|
|
|
|
inline void time_t_to_Struct(time_t t, struct tm * buf , bool local = false ) {
|
|
#if defined(_WIN32)
|
|
if ( local )
|
|
localtime_s( buf , &t );
|
|
else
|
|
gmtime_s(buf, &t);
|
|
#else
|
|
if ( local )
|
|
localtime_r(&t, buf);
|
|
else
|
|
gmtime_r(&t, buf);
|
|
#endif
|
|
}
|
|
|
|
inline string terseCurrentTime(){
|
|
struct tm t;
|
|
time_t_to_Struct( time(0) , &t );
|
|
stringstream ss;
|
|
ss << ( 1900 + t.tm_year ) << "-"
|
|
<< t.tm_mon << "-"
|
|
<< t.tm_mday << "-"
|
|
<< t.tm_hour << "-"
|
|
<< t.tm_min;
|
|
return ss.str();
|
|
}
|
|
|
|
#define MONGO_asctime _asctime_not_threadsafe_
|
|
#define asctime MONGO_asctime
|
|
#define MONGO_gmtime _gmtime_not_threadsafe_
|
|
#define gmtime MONGO_gmtime
|
|
#define MONGO_localtime _localtime_not_threadsafe_
|
|
#define localtime MONGO_localtime
|
|
#define MONGO_ctime _ctime_is_not_threadsafe_
|
|
#define ctime MONGO_ctime
|
|
|
|
#if defined(_WIN32) || defined(__sunos__)
|
|
inline void sleepsecs(int s) {
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
xt.sec += s;
|
|
boost::thread::sleep(xt);
|
|
}
|
|
inline void sleepmillis(int s) {
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
xt.sec += ( s / 1000 );
|
|
xt.nsec += ( s % 1000 ) * 1000000;
|
|
if ( xt.nsec >= 1000000000 ) {
|
|
xt.nsec -= 1000000000;
|
|
xt.sec++;
|
|
}
|
|
boost::thread::sleep(xt);
|
|
}
|
|
inline void sleepmicros(int s) {
|
|
if ( s <= 0 )
|
|
return;
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
xt.sec += ( s / 1000000 );
|
|
xt.nsec += ( s % 1000000 ) * 1000;
|
|
if ( xt.nsec >= 1000000000 ) {
|
|
xt.nsec -= 1000000000;
|
|
xt.sec++;
|
|
}
|
|
boost::thread::sleep(xt);
|
|
}
|
|
#else
|
|
inline void sleepsecs(int s) {
|
|
struct timespec t;
|
|
t.tv_sec = s;
|
|
t.tv_nsec = 0;
|
|
if ( nanosleep( &t , 0 ) ){
|
|
cout << "nanosleep failed" << endl;
|
|
}
|
|
}
|
|
inline void sleepmicros(int s) {
|
|
if ( s <= 0 )
|
|
return;
|
|
struct timespec t;
|
|
t.tv_sec = (int)(s / 1000000);
|
|
t.tv_nsec = 1000 * ( s % 1000000 );
|
|
struct timespec out;
|
|
if ( nanosleep( &t , &out ) ){
|
|
cout << "nanosleep failed" << endl;
|
|
}
|
|
}
|
|
inline void sleepmillis(int s) {
|
|
sleepmicros( s * 1000 );
|
|
}
|
|
#endif
|
|
|
|
// note this wraps
|
|
inline int tdiff(unsigned told, unsigned tnew) {
|
|
return WrappingInt::diff(tnew, told);
|
|
}
|
|
inline unsigned curTimeMillis() {
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
unsigned t = xt.nsec / 1000000;
|
|
return (xt.sec & 0xfffff) * 1000 + t;
|
|
}
|
|
|
|
inline Date_t jsTime() {
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
unsigned long long t = xt.nsec / 1000000;
|
|
return ((unsigned long long) xt.sec * 1000) + t;
|
|
}
|
|
|
|
inline unsigned long long curTimeMicros64() {
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
unsigned long long t = xt.nsec / 1000;
|
|
return (((unsigned long long) xt.sec) * 1000000) + t;
|
|
}
|
|
|
|
// measures up to 1024 seconds. or, 512 seconds with tdiff that is...
|
|
inline unsigned curTimeMicros() {
|
|
boost::xtime xt;
|
|
boost::xtime_get(&xt, boost::TIME_UTC);
|
|
unsigned t = xt.nsec / 1000;
|
|
unsigned secs = xt.sec % 1024;
|
|
return secs*1000000 + t;
|
|
}
|
|
using namespace boost;
|
|
|
|
extern bool __destroyingStatics;
|
|
|
|
// If you create a local static instance of this class, that instance will be destroyed
|
|
// before all global static objects are destroyed, so __destroyingStatics will be set
|
|
// to true before the global static variables are destroyed.
|
|
class StaticObserver : boost::noncopyable {
|
|
public:
|
|
~StaticObserver() { __destroyingStatics = true; }
|
|
};
|
|
|
|
// On pthread systems, it is an error to destroy a mutex while held. Static global
|
|
// mutexes may be held upon shutdown in our implementation, and this way we avoid
|
|
// destroying them.
|
|
class mutex : boost::noncopyable {
|
|
public:
|
|
mutex() { _m = new boost::mutex(); }
|
|
~mutex() {
|
|
if( !__destroyingStatics ) {
|
|
delete _m;
|
|
}
|
|
}
|
|
class scoped_lock : boost::noncopyable {
|
|
public:
|
|
scoped_lock( mongo::mutex &m ) : _l( m.boost() ) {}
|
|
boost::mutex::scoped_lock &boost() { return _l; }
|
|
private:
|
|
boost::mutex::scoped_lock _l;
|
|
};
|
|
private:
|
|
boost::mutex &boost() { return *_m; }
|
|
boost::mutex *_m;
|
|
};
|
|
|
|
typedef mongo::mutex::scoped_lock scoped_lock;
|
|
typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock;
|
|
|
|
// simple scoped timer
|
|
class Timer {
|
|
public:
|
|
Timer() {
|
|
reset();
|
|
}
|
|
Timer( unsigned long long start ) {
|
|
old = start;
|
|
}
|
|
int seconds(){
|
|
return (int)(micros() / 1000000);
|
|
}
|
|
int millis() {
|
|
return (long)(micros() / 1000);
|
|
}
|
|
unsigned long long micros() {
|
|
unsigned long long n = curTimeMicros64();
|
|
return n - old;
|
|
}
|
|
unsigned long long micros(unsigned long long & n) { // returns cur time in addition to timer result
|
|
n = curTimeMicros64();
|
|
return n - old;
|
|
}
|
|
unsigned long long startTime(){
|
|
return old;
|
|
}
|
|
void reset() {
|
|
old = curTimeMicros64();
|
|
}
|
|
private:
|
|
unsigned long long old;
|
|
};
|
|
|
|
/*
|
|
|
|
class DebugMutex : boost::noncopyable {
|
|
friend class lock;
|
|
mongo::mutex m;
|
|
int locked;
|
|
public:
|
|
DebugMutex() : locked(0); { }
|
|
bool isLocked() { return locked; }
|
|
};
|
|
|
|
*/
|
|
|
|
//typedef scoped_lock lock;
|
|
|
|
inline bool startsWith(const char *str, const char *prefix) {
|
|
size_t l = strlen(prefix);
|
|
if ( strlen(str) < l ) return false;
|
|
return strncmp(str, prefix, l) == 0;
|
|
}
|
|
inline bool startsWith(string s, string p) { return startsWith(s.c_str(), p.c_str()); }
|
|
|
|
inline bool endsWith(const char *p, const char *suffix) {
|
|
size_t a = strlen(p);
|
|
size_t b = strlen(suffix);
|
|
if ( b > a ) return false;
|
|
return strcmp(p + a - b, suffix) == 0;
|
|
}
|
|
|
|
} // namespace mongo
|
|
|
|
#include "boost/detail/endian.hpp"
|
|
|
|
namespace mongo {
|
|
|
|
inline unsigned long swapEndian(unsigned long x) {
|
|
return
|
|
((x & 0xff) << 24) |
|
|
((x & 0xff00) << 8) |
|
|
((x & 0xff0000) >> 8) |
|
|
((x & 0xff000000) >> 24);
|
|
}
|
|
|
|
#if defined(BOOST_LITTLE_ENDIAN)
|
|
inline unsigned long fixEndian(unsigned long x) {
|
|
return x;
|
|
}
|
|
#else
|
|
inline unsigned long fixEndian(unsigned long x) {
|
|
return swapEndian(x);
|
|
}
|
|
#endif
|
|
|
|
// Like strlen, but only scans up to n bytes.
|
|
// Returns -1 if no '0' found.
|
|
inline int strnlen( const char *s, int n ) {
|
|
for( int i = 0; i < n; ++i )
|
|
if ( !s[ i ] )
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
#if !defined(_WIN32)
|
|
typedef int HANDLE;
|
|
inline void strcpy_s(char *dst, unsigned len, const char *src) {
|
|
strcpy(dst, src);
|
|
}
|
|
#else
|
|
typedef void *HANDLE;
|
|
#endif
|
|
|
|
/* thread local "value" rather than a pointer
|
|
good for things which have copy constructors (and the copy constructor is fast enough)
|
|
e.g.
|
|
ThreadLocalValue<int> myint;
|
|
*/
|
|
template<class T>
|
|
class ThreadLocalValue {
|
|
public:
|
|
ThreadLocalValue( T def = 0 ) : _default( def ) { }
|
|
|
|
T get() {
|
|
T * val = _val.get();
|
|
if ( val )
|
|
return *val;
|
|
return _default;
|
|
}
|
|
|
|
void set( const T& i ) {
|
|
T *v = _val.get();
|
|
if( v ) {
|
|
*v = i;
|
|
return;
|
|
}
|
|
v = new T(i);
|
|
_val.reset( v );
|
|
}
|
|
|
|
private:
|
|
T _default;
|
|
boost::thread_specific_ptr<T> _val;
|
|
};
|
|
|
|
class ProgressMeter {
|
|
public:
|
|
ProgressMeter( long long total , int secondsBetween = 3 , int checkInterval = 100 ){
|
|
reset( total , secondsBetween , checkInterval );
|
|
}
|
|
|
|
ProgressMeter(){
|
|
_active = 0;
|
|
}
|
|
|
|
void reset( long long total , int secondsBetween = 3 , int checkInterval = 100 ){
|
|
_total = total;
|
|
_secondsBetween = secondsBetween;
|
|
_checkInterval = checkInterval;
|
|
|
|
_done = 0;
|
|
_hits = 0;
|
|
_lastTime = (int)time(0);
|
|
|
|
_active = 1;
|
|
}
|
|
|
|
void finished(){
|
|
_active = 0;
|
|
}
|
|
|
|
bool isActive(){
|
|
return _active;
|
|
}
|
|
|
|
bool hit( int n = 1 ){
|
|
if ( ! _active ){
|
|
cout << "warning: hit on in-active ProgressMeter" << endl;
|
|
}
|
|
|
|
_done += n;
|
|
_hits++;
|
|
if ( _hits % _checkInterval )
|
|
return false;
|
|
|
|
int t = (int) time(0);
|
|
if ( t - _lastTime < _secondsBetween )
|
|
return false;
|
|
|
|
if ( _total > 0 ){
|
|
int per = (int)( ( (double)_done * 100.0 ) / (double)_total );
|
|
cout << "\t\t" << _done << "/" << _total << "\t" << per << "%" << endl;
|
|
}
|
|
_lastTime = t;
|
|
return true;
|
|
}
|
|
|
|
long long done(){
|
|
return _done;
|
|
}
|
|
|
|
long long hits(){
|
|
return _hits;
|
|
}
|
|
|
|
string toString() const {
|
|
if ( ! _active )
|
|
return "";
|
|
stringstream buf;
|
|
buf << _done << "/" << _total << " " << (_done*100)/_total << "%";
|
|
return buf.str();
|
|
}
|
|
private:
|
|
|
|
bool _active;
|
|
|
|
long long _total;
|
|
int _secondsBetween;
|
|
int _checkInterval;
|
|
|
|
long long _done;
|
|
long long _hits;
|
|
int _lastTime;
|
|
};
|
|
|
|
class TicketHolder {
|
|
public:
|
|
TicketHolder( int num ){
|
|
_outof = num;
|
|
_num = num;
|
|
}
|
|
|
|
bool tryAcquire(){
|
|
scoped_lock lk( _mutex );
|
|
if ( _num <= 0 ){
|
|
if ( _num < 0 ){
|
|
cerr << "DISASTER! in TicketHolder" << endl;
|
|
}
|
|
return false;
|
|
}
|
|
_num--;
|
|
return true;
|
|
}
|
|
|
|
void release(){
|
|
scoped_lock lk( _mutex );
|
|
_num++;
|
|
}
|
|
|
|
void resize( int newSize ){
|
|
scoped_lock lk( _mutex );
|
|
int used = _outof - _num;
|
|
if ( used > newSize ){
|
|
cout << "ERROR: can't resize since we're using (" << used << ") more than newSize(" << newSize << ")" << endl;
|
|
return;
|
|
}
|
|
|
|
_outof = newSize;
|
|
_num = _outof - used;
|
|
}
|
|
|
|
int available(){
|
|
return _num;
|
|
}
|
|
|
|
int used(){
|
|
return _outof - _num;
|
|
}
|
|
|
|
private:
|
|
int _outof;
|
|
int _num;
|
|
mongo::mutex _mutex;
|
|
};
|
|
|
|
class TicketHolderReleaser {
|
|
public:
|
|
TicketHolderReleaser( TicketHolder * holder ){
|
|
_holder = holder;
|
|
}
|
|
|
|
~TicketHolderReleaser(){
|
|
_holder->release();
|
|
}
|
|
private:
|
|
TicketHolder * _holder;
|
|
};
|
|
|
|
|
|
/**
|
|
* this is a thread safe string
|
|
* you will never get a bad pointer, though data may be mungedd
|
|
*/
|
|
class ThreadSafeString {
|
|
public:
|
|
ThreadSafeString( size_t size=256 )
|
|
: _size( 256 ) , _buf( new char[256] ){
|
|
memset( _buf , 0 , _size );
|
|
}
|
|
|
|
ThreadSafeString( const ThreadSafeString& other )
|
|
: _size( other._size ) , _buf( new char[_size] ){
|
|
strncpy( _buf , other._buf , _size );
|
|
}
|
|
|
|
~ThreadSafeString(){
|
|
delete[] _buf;
|
|
_buf = 0;
|
|
}
|
|
|
|
operator string() const {
|
|
string s = _buf;
|
|
return s;
|
|
}
|
|
|
|
ThreadSafeString& operator=( const char * str ){
|
|
size_t s = strlen(str);
|
|
if ( s >= _size - 2 )
|
|
s = _size - 2;
|
|
strncpy( _buf , str , s );
|
|
_buf[s] = 0;
|
|
return *this;
|
|
}
|
|
|
|
bool operator==( const ThreadSafeString& other ) const {
|
|
return strcmp( _buf , other._buf ) == 0;
|
|
}
|
|
|
|
bool operator==( const char * str ) const {
|
|
return strcmp( _buf , str ) == 0;
|
|
}
|
|
|
|
bool operator!=( const char * str ) const {
|
|
return strcmp( _buf , str );
|
|
}
|
|
|
|
bool empty() const {
|
|
return _buf[0] == 0;
|
|
}
|
|
|
|
private:
|
|
size_t _size;
|
|
char * _buf;
|
|
};
|
|
|
|
ostream& operator<<( ostream &s, const ThreadSafeString &o );
|
|
|
|
inline bool isNumber( char c ) {
|
|
return c >= '0' && c <= '9';
|
|
}
|
|
|
|
inline unsigned stringToNum(const char *str) {
|
|
unsigned x = 0;
|
|
const char *p = str;
|
|
while( 1 ) {
|
|
if( !isNumber(*p) ) {
|
|
if( *p == 0 && p != str )
|
|
break;
|
|
throw 0;
|
|
}
|
|
x = x * 10 + *p++ - '0';
|
|
}
|
|
return x;
|
|
}
|
|
|
|
// for convenience, '{' is greater than anything and stops number parsing
|
|
inline int lexNumCmp( const char *s1, const char *s2 ) {
|
|
while( *s1 && *s2 ) {
|
|
|
|
bool p1 = ( *s1 == '{' );
|
|
bool p2 = ( *s2 == '{' );
|
|
if ( p1 && !p2 )
|
|
return 1;
|
|
if ( p2 && !p1 )
|
|
return -1;
|
|
|
|
bool n1 = isNumber( *s1 );
|
|
bool n2 = isNumber( *s2 );
|
|
|
|
if ( n1 && n2 ) {
|
|
char * e1;
|
|
char * e2;
|
|
long l1 = strtol( s1 , &e1 , 10 );
|
|
long l2 = strtol( s2 , &e2 , 10 );
|
|
|
|
if ( l1 > l2 )
|
|
return 1;
|
|
else if ( l1 < l2 )
|
|
return -1;
|
|
|
|
s1 = e1;
|
|
s2 = e2;
|
|
continue;
|
|
}
|
|
|
|
if ( n1 )
|
|
return 1;
|
|
|
|
if ( n2 )
|
|
return -1;
|
|
|
|
if ( *s1 > *s2 )
|
|
return 1;
|
|
|
|
if ( *s2 > *s1 )
|
|
return -1;
|
|
|
|
s1++; s2++;
|
|
}
|
|
|
|
if ( *s1 )
|
|
return 1;
|
|
if ( *s2 )
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
} // namespace mongo
|