summaryrefslogtreecommitdiff
path: root/src/mongo/util
diff options
context:
space:
mode:
authorEliot Horowitz <eliot@10gen.com>2011-12-24 15:33:26 -0500
committerEliot Horowitz <eliot@10gen.com>2011-12-24 15:33:45 -0500
commitae1ecd9c786911f9f1f0242f0f7d702b3e5dfeba (patch)
tree92f8e1649e6f080b251ff5f1763679a72eb59b34 /src/mongo/util
parentdfa4cd7e2cf109b072440155fabc08a93c8045a0 (diff)
downloadmongo-ae1ecd9c786911f9f1f0242f0f7d702b3e5dfeba.tar.gz
bulk move of code to src/ SERVER-4551
Diffstat (limited to 'src/mongo/util')
-rw-r--r--src/mongo/util/admin_access.h52
-rw-r--r--src/mongo/util/alignedbuilder.cpp141
-rw-r--r--src/mongo/util/alignedbuilder.h125
-rw-r--r--src/mongo/util/allocator.h39
-rw-r--r--src/mongo/util/array.h127
-rw-r--r--src/mongo/util/assert_util.cpp213
-rw-r--r--src/mongo/util/assert_util.h275
-rw-r--r--src/mongo/util/background.cpp190
-rw-r--r--src/mongo/util/background.h155
-rw-r--r--src/mongo/util/base64.cpp109
-rw-r--r--src/mongo/util/base64.h68
-rw-r--r--src/mongo/util/bson_util.h42
-rw-r--r--src/mongo/util/bufreader.h100
-rw-r--r--src/mongo/util/checksum.h37
-rw-r--r--src/mongo/util/compress.cpp31
-rw-r--r--src/mongo/util/compress.h21
-rw-r--r--src/mongo/util/concurrency/README39
-rw-r--r--src/mongo/util/concurrency/list.h99
-rw-r--r--src/mongo/util/concurrency/msg.h61
-rw-r--r--src/mongo/util/concurrency/mutex.h228
-rw-r--r--src/mongo/util/concurrency/mutexdebugger.h117
-rw-r--r--src/mongo/util/concurrency/mvar.h118
-rw-r--r--src/mongo/util/concurrency/race.h77
-rw-r--r--src/mongo/util/concurrency/rwlock.h271
-rw-r--r--src/mongo/util/concurrency/rwlockimpl.h170
-rw-r--r--src/mongo/util/concurrency/shared_mutex_win.hpp594
-rw-r--r--src/mongo/util/concurrency/spin_lock.cpp107
-rw-r--r--src/mongo/util/concurrency/spin_lock.h77
-rw-r--r--src/mongo/util/concurrency/synchronization.cpp81
-rw-r--r--src/mongo/util/concurrency/synchronization.h86
-rw-r--r--src/mongo/util/concurrency/task.cpp181
-rw-r--r--src/mongo/util/concurrency/task.h72
-rw-r--r--src/mongo/util/concurrency/thread_pool.cpp141
-rw-r--r--src/mongo/util/concurrency/thread_pool.h82
-rw-r--r--src/mongo/util/concurrency/threadlocal.h126
-rw-r--r--src/mongo/util/concurrency/value.h139
-rw-r--r--src/mongo/util/concurrency/vars.cpp56
-rw-r--r--src/mongo/util/debug_util.cpp60
-rw-r--r--src/mongo/util/debug_util.h106
-rw-r--r--src/mongo/util/embedded_builder.h92
-rw-r--r--src/mongo/util/file.h230
-rw-r--r--src/mongo/util/file_allocator.cpp329
-rw-r--r--src/mongo/util/file_allocator.h91
-rw-r--r--src/mongo/util/goodies.h475
-rw-r--r--src/mongo/util/hashtab.h179
-rw-r--r--src/mongo/util/heapcheck.h33
-rw-r--r--src/mongo/util/hex.h67
-rw-r--r--src/mongo/util/histogram.cpp131
-rw-r--r--src/mongo/util/histogram.h128
-rwxr-xr-xsrc/mongo/util/intrusive_counter.cpp30
-rwxr-xr-xsrc/mongo/util/intrusive_counter.h79
-rw-r--r--src/mongo/util/log.cpp197
-rw-r--r--src/mongo/util/log.h581
-rw-r--r--src/mongo/util/logfile.cpp253
-rw-r--r--src/mongo/util/logfile.h58
-rw-r--r--src/mongo/util/lruishmap.h78
-rw-r--r--src/mongo/util/md5.c381
-rw-r--r--src/mongo/util/md5.h91
-rw-r--r--src/mongo/util/md5.hpp58
-rw-r--r--src/mongo/util/md5main.cpp142
-rwxr-xr-xsrc/mongo/util/mmap.cpp211
-rw-r--r--src/mongo/util/mmap.h305
-rw-r--r--src/mongo/util/mmap_mm.cpp52
-rw-r--r--src/mongo/util/mmap_posix.cpp214
-rw-r--r--src/mongo/util/mmap_win.cpp202
-rwxr-xr-xsrc/mongo/util/mongoutils/README15
-rw-r--r--src/mongo/util/mongoutils/checksum.h32
-rw-r--r--src/mongo/util/mongoutils/hash.h41
-rw-r--r--src/mongo/util/mongoutils/html.h158
-rwxr-xr-xsrc/mongo/util/mongoutils/mongoutils.vcxproj75
-rwxr-xr-xsrc/mongo/util/mongoutils/mongoutils.vcxproj.filters10
-rw-r--r--src/mongo/util/mongoutils/str.h216
-rw-r--r--src/mongo/util/mongoutils/test.cpp45
-rw-r--r--src/mongo/util/moveablebuffer.h51
-rw-r--r--src/mongo/util/net/hostandport.h239
-rw-r--r--src/mongo/util/net/httpclient.cpp177
-rw-r--r--src/mongo/util/net/httpclient.h78
-rw-r--r--src/mongo/util/net/listen.cpp394
-rw-r--r--src/mongo/util/net/listen.h190
-rw-r--r--src/mongo/util/net/message.cpp64
-rw-r--r--src/mongo/util/net/message.h312
-rw-r--r--src/mongo/util/net/message_port.cpp303
-rw-r--r--src/mongo/util/net/message_port.h108
-rw-r--r--src/mongo/util/net/message_server.h66
-rw-r--r--src/mongo/util/net/message_server_asio.cpp261
-rw-r--r--src/mongo/util/net/message_server_port.cpp204
-rw-r--r--src/mongo/util/net/miniwebserver.cpp212
-rw-r--r--src/mongo/util/net/miniwebserver.h60
-rw-r--r--src/mongo/util/net/sock.cpp763
-rw-r--r--src/mongo/util/net/sock.h261
-rw-r--r--src/mongo/util/ntservice.cpp408
-rw-r--r--src/mongo/util/ntservice.h49
-rw-r--r--src/mongo/util/optime.h170
-rw-r--r--src/mongo/util/password.cpp91
-rw-r--r--src/mongo/util/password.h61
-rw-r--r--src/mongo/util/paths.h124
-rw-r--r--src/mongo/util/processinfo.cpp48
-rw-r--r--src/mongo/util/processinfo.h67
-rw-r--r--src/mongo/util/processinfo_darwin.cpp116
-rw-r--r--src/mongo/util/processinfo_linux2.cpp244
-rw-r--r--src/mongo/util/processinfo_none.cpp55
-rw-r--r--src/mongo/util/processinfo_win32.cpp102
-rw-r--r--src/mongo/util/queue.h106
-rw-r--r--src/mongo/util/ramlog.cpp190
-rw-r--r--src/mongo/util/ramlog.h65
-rw-r--r--src/mongo/util/scopeguard.h427
-rw-r--r--src/mongo/util/signal_handlers.cpp122
-rw-r--r--src/mongo/util/signal_handlers.h34
-rwxr-xr-xsrc/mongo/util/string_writer.h28
-rw-r--r--src/mongo/util/stringutils.cpp44
-rw-r--r--src/mongo/util/stringutils.h139
-rwxr-xr-xsrc/mongo/util/systeminfo.h41
-rwxr-xr-xsrc/mongo/util/systeminfo_linux2.cpp47
-rwxr-xr-xsrc/mongo/util/systeminfo_none.cpp26
-rwxr-xr-xsrc/mongo/util/systeminfo_win32.cpp48
-rw-r--r--src/mongo/util/text.cpp115
-rw-r--r--src/mongo/util/text.h148
-rw-r--r--src/mongo/util/time_support.h255
-rw-r--r--src/mongo/util/timer.h115
-rw-r--r--src/mongo/util/unittest.h62
-rw-r--r--src/mongo/util/util.cpp220
-rw-r--r--src/mongo/util/version.cpp288
-rw-r--r--src/mongo/util/version.h27
-rw-r--r--src/mongo/util/winutil.h44
124 files changed, 17961 insertions, 0 deletions
diff --git a/src/mongo/util/admin_access.h b/src/mongo/util/admin_access.h
new file mode 100644
index 00000000000..bb882b2b4c5
--- /dev/null
+++ b/src/mongo/util/admin_access.h
@@ -0,0 +1,52 @@
+/** @file admin_access.h
+ */
+
+/**
+* Copyright (C) 2010 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+namespace mongo {
+
+ /*
+ * An AdminAccess is an interface class used to determine if certain users have
+ * priviledges to a given resource.
+ *
+ */
+ class AdminAccess {
+ public:
+ virtual ~AdminAccess() { }
+
+ /** @return if there are any priviledge users. This should not
+ * block for long and throw if can't get a lock if needed.
+ */
+ virtual bool haveAdminUsers() const = 0;
+
+ /** @return priviledged user with this name. This should not block
+ * for long and throw if can't get a lock if needed
+ */
+ virtual BSONObj getAdminUser( const string& username ) const = 0;
+ };
+
+ class NoAdminAccess : public AdminAccess {
+ public:
+ virtual ~NoAdminAccess() { }
+
+ virtual bool haveAdminUsers() const { return false; }
+ virtual BSONObj getAdminUser( const string& username ) const { return BSONObj(); }
+ };
+
+} // namespace mongo
diff --git a/src/mongo/util/alignedbuilder.cpp b/src/mongo/util/alignedbuilder.cpp
new file mode 100644
index 00000000000..b2e0461b733
--- /dev/null
+++ b/src/mongo/util/alignedbuilder.cpp
@@ -0,0 +1,141 @@
+// @file alignedbuilder.cpp
+
+/**
+* Copyright (C) 2009 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+#include "alignedbuilder.h"
+
+namespace mongo {
+
+ AlignedBuilder::AlignedBuilder(unsigned initSize) {
+ _len = 0;
+ _malloc(initSize);
+ uassert(13584, "out of memory AlignedBuilder", _p._allocationAddress);
+ }
+
+ BOOST_STATIC_ASSERT(sizeof(void*) == sizeof(size_t));
+
+ /** reset for a re-use. shrinks if > 128MB */
+ void AlignedBuilder::reset() {
+ _len = 0;
+ RARELY {
+ const unsigned sizeCap = 128*1024*1024;
+ if (_p._size > sizeCap)
+ _realloc(sizeCap, _len);
+ }
+ }
+
+ /** reset with a hint as to the upcoming needed size specified */
+ void AlignedBuilder::reset(unsigned sz) {
+ _len = 0;
+ unsigned Q = 32 * 1024 * 1024 - 1;
+ unsigned want = (sz+Q) & (~Q);
+ if( _p._size == want ) {
+ return;
+ }
+ if( _p._size > want ) {
+ if( _p._size <= 64 * 1024 * 1024 )
+ return;
+ bool downsize = false;
+ RARELY { downsize = true; }
+ if( !downsize )
+ return;
+ }
+ _realloc(want, _len);
+ }
+
+ void AlignedBuilder::mallocSelfAligned(unsigned sz) {
+ assert( sz == _p._size );
+ void *p = malloc(sz + Alignment - 1);
+ _p._allocationAddress = p;
+ size_t s = (size_t) p;
+ size_t sold = s;
+ s += Alignment - 1;
+ s = (s/Alignment)*Alignment;
+ assert( s >= sold ); // begining
+ assert( (s + sz) <= (sold + sz + Alignment - 1) ); //end
+ _p._data = (char *) s;
+ }
+
+ /* "slow"/infrequent portion of 'grow()' */
+ void NOINLINE_DECL AlignedBuilder::growReallocate(unsigned oldLen) {
+ dassert( _len > _p._size );
+ unsigned a = _p._size;
+ assert( a );
+ while( 1 ) {
+ if( a < 128 * 1024 * 1024 )
+ a *= 2;
+ else if( sizeof(int*) == 4 )
+ a += 32 * 1024 * 1024;
+ else
+ a += 64 * 1024 * 1024;
+ DEV if( a > 256*1024*1024 ) {
+ log() << "dur AlignedBuilder too big, aborting in _DEBUG build" << endl;
+ abort();
+ }
+ wassert( a <= 256*1024*1024 );
+ assert( a <= 512*1024*1024 );
+ if( _len < a )
+ break;
+ }
+ _realloc(a, oldLen);
+ }
+
+ void AlignedBuilder::_malloc(unsigned sz) {
+ _p._size = sz;
+#if defined(_WIN32)
+ void *p = VirtualAlloc(0, sz, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ _p._allocationAddress = p;
+ _p._data = (char *) p;
+#elif defined(__linux__)
+ // in theory #ifdef _POSIX_VERSION should work, but it doesn't on OS X 10.4, and needs to be testeed on solaris.
+ // so for now, linux only for this.
+ void *p = 0;
+ int res = posix_memalign(&p, Alignment, sz);
+ massert(13524, "out of memory AlignedBuilder", res == 0);
+ _p._allocationAddress = p;
+ _p._data = (char *) p;
+#else
+ mallocSelfAligned(sz);
+ assert( ((size_t) _p._data) % Alignment == 0 );
+#endif
+ }
+
+ void AlignedBuilder::_realloc(unsigned newSize, unsigned oldLen) {
+ // posix_memalign alignment is not maintained on reallocs, so we can't use realloc().
+ AllocationInfo old = _p;
+ _malloc(newSize);
+ assert( oldLen <= _len );
+ memcpy(_p._data, old._data, oldLen);
+ _free(old._allocationAddress);
+ }
+
+ void AlignedBuilder::_free(void *p) {
+#if defined(_WIN32)
+ VirtualFree(p, 0, MEM_RELEASE);
+#else
+ free(p);
+#endif
+ }
+
+ void AlignedBuilder::kill() {
+ _free(_p._allocationAddress);
+ _p._allocationAddress = 0;
+ _p._data = 0;
+ }
+
+}
diff --git a/src/mongo/util/alignedbuilder.h b/src/mongo/util/alignedbuilder.h
new file mode 100644
index 00000000000..1d246a9d78e
--- /dev/null
+++ b/src/mongo/util/alignedbuilder.h
@@ -0,0 +1,125 @@
+// @file alignedbuilder.h
+
+/**
+* Copyright (C) 2009 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "../bson/stringdata.h"
+
+namespace mongo {
+
+ /** a page-aligned BufBuilder. */
+ class AlignedBuilder {
+ public:
+ AlignedBuilder(unsigned init_size);
+ ~AlignedBuilder() { kill(); }
+
+ /** reset with a hint as to the upcoming needed size specified */
+ void reset(unsigned sz);
+
+ /** reset for a re-use. shrinks if > 128MB */
+ void reset();
+
+ /** note this may be deallocated (realloced) if you keep writing or reset(). */
+ const char* buf() const { return _p._data; }
+
+ /** leave room for some stuff later
+ @return offset in the buffer that was our current position
+ */
+ size_t skip(unsigned n) {
+ unsigned l = len();
+ grow(n);
+ return l;
+ }
+
+ /** if buffer grows pointer no longer valid */
+ char* atOfs(unsigned ofs) { return _p._data + ofs; }
+
+ /** if buffer grows pointer no longer valid */
+ char* cur() { return _p._data + _len; }
+
+ void appendChar(char j) {
+ *((char*)grow(sizeof(char))) = j;
+ }
+ void appendNum(char j) {
+ *((char*)grow(sizeof(char))) = j;
+ }
+ void appendNum(short j) {
+ *((short*)grow(sizeof(short))) = j;
+ }
+ void appendNum(int j) {
+ *((int*)grow(sizeof(int))) = j;
+ }
+ void appendNum(unsigned j) {
+ *((unsigned*)grow(sizeof(unsigned))) = j;
+ }
+ void appendNum(bool j) {
+ *((bool*)grow(sizeof(bool))) = j;
+ }
+ void appendNum(double j) {
+ *((double*)grow(sizeof(double))) = j;
+ }
+ void appendNum(long long j) {
+ *((long long*)grow(sizeof(long long))) = j;
+ }
+ void appendNum(unsigned long long j) {
+ *((unsigned long long*)grow(sizeof(unsigned long long))) = j;
+ }
+
+ void appendBuf(const void *src, size_t len) { memcpy(grow((unsigned) len), src, len); }
+
+ template<class T>
+ void appendStruct(const T& s) { appendBuf(&s, sizeof(T)); }
+
+ void appendStr(const StringData &str , bool includeEOO = true ) {
+ const unsigned len = str.size() + ( includeEOO ? 1 : 0 );
+ assert( len < (unsigned) BSONObjMaxUserSize );
+ memcpy(grow(len), str.data(), len);
+ }
+
+ /** @return the in-use length */
+ unsigned len() const { return _len; }
+
+ private:
+ static const unsigned Alignment = 8192;
+
+ /** returns the pre-grow write position */
+ inline char* grow(unsigned by) {
+ unsigned oldlen = _len;
+ _len += by;
+ if (MONGO_unlikely( _len > _p._size )) {
+ growReallocate(oldlen);
+ }
+ return _p._data + oldlen;
+ }
+
+ void growReallocate(unsigned oldLenInUse);
+ void kill();
+ void mallocSelfAligned(unsigned sz);
+ void _malloc(unsigned sz);
+ void _realloc(unsigned newSize, unsigned oldLenInUse);
+ void _free(void*);
+
+ struct AllocationInfo {
+ char *_data;
+ void *_allocationAddress;
+ unsigned _size;
+ } _p;
+ unsigned _len; // bytes in use
+ };
+
+}
diff --git a/src/mongo/util/allocator.h b/src/mongo/util/allocator.h
new file mode 100644
index 00000000000..a642e7cab56
--- /dev/null
+++ b/src/mongo/util/allocator.h
@@ -0,0 +1,39 @@
+// allocator.h
+
+/* 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
+
+namespace mongo {
+
+ inline void * ourmalloc(size_t size) {
+ void *x = malloc(size);
+ if ( x == 0 ) dbexit( EXIT_OOM_MALLOC , "malloc fails");
+ return x;
+ }
+
+ inline void * ourrealloc(void *ptr, size_t size) {
+ void *x = realloc(ptr, size);
+ if ( x == 0 ) dbexit( EXIT_OOM_REALLOC , "realloc fails");
+ return x;
+ }
+
+#define MONGO_malloc mongo::ourmalloc
+#define malloc MONGO_malloc
+#define MONGO_realloc mongo::ourrealloc
+#define realloc MONGO_realloc
+
+} // namespace mongo
diff --git a/src/mongo/util/array.h b/src/mongo/util/array.h
new file mode 100644
index 00000000000..12822252fd7
--- /dev/null
+++ b/src/mongo/util/array.h
@@ -0,0 +1,127 @@
+// array.h
+
+/*
+ * Copyright 2010 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.
+ */
+
+namespace mongo {
+
+ /*
+ * simple array class that does no allocations
+ * same api as vector
+ * fixed buffer, so once capacity is exceeded, will assert
+ * meant to be-reused with clear()
+ */
+ template<typename T>
+ class FastArray {
+ public:
+ FastArray( int capacity=10000 )
+ : _capacity( capacity ) , _size(0) , _end(this,capacity) {
+ _data = new T[capacity];
+ }
+
+ ~FastArray() {
+ delete[] _data;
+ }
+
+ void clear() {
+ _size = 0;
+ }
+
+ T& operator[]( int x ) {
+ assert( x >= 0 && x < _capacity );
+ return _data[x];
+ }
+
+ T& getNext() {
+ return _data[_size++];
+ }
+
+ void push_back( const T& t ) {
+ assert( _size < _capacity );
+ _data[_size++] = t;
+ }
+
+ void sort( int (*comp)(const void *, const void *) ) {
+ qsort( _data , _size , sizeof(T) , comp );
+ }
+
+ int size() {
+ return _size;
+ }
+
+ bool hasSpace() {
+ return _size < _capacity;
+ }
+ class iterator {
+ public:
+ iterator() {
+ _it = 0;
+ _pos = 0;
+ }
+
+ iterator( FastArray * it , int pos=0 ) {
+ _it = it;
+ _pos = pos;
+ }
+
+ bool operator==(const iterator& other ) const {
+ return _pos == other._pos;
+ }
+
+ bool operator!=(const iterator& other ) const {
+ return _pos != other._pos;
+ }
+
+ void operator++() {
+ _pos++;
+ }
+
+ T& operator*() {
+ return _it->_data[_pos];
+ }
+
+ string toString() const {
+ stringstream ss;
+ ss << _pos;
+ return ss.str();
+ }
+ private:
+ FastArray * _it;
+ int _pos;
+
+ friend class FastArray;
+ };
+
+
+ iterator begin() {
+ return iterator(this);
+ }
+
+ iterator end() {
+ _end._pos = _size;
+ return _end;
+ }
+
+
+ private:
+ int _capacity;
+ int _size;
+
+ iterator _end;
+
+ T * _data;
+ };
+}
diff --git a/src/mongo/util/assert_util.cpp b/src/mongo/util/assert_util.cpp
new file mode 100644
index 00000000000..2199cb1ce11
--- /dev/null
+++ b/src/mongo/util/assert_util.cpp
@@ -0,0 +1,213 @@
+// assert_util.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "assert_util.h"
+#include "assert.h"
+//#include "file.h"
+#include <cmath>
+using namespace std;
+
+#ifndef _WIN32
+#include <cxxabi.h>
+#include <sys/file.h>
+#endif
+
+//#include "../bson/bson.h"
+#include "../db/jsobj.h"
+
+namespace mongo {
+
+ AssertionCount assertionCount;
+
+ AssertionCount::AssertionCount()
+ : regular(0),warning(0),msg(0),user(0),rollovers(0) {
+ }
+
+ void AssertionCount::rollover() {
+ rollovers++;
+ regular = 0;
+ warning = 0;
+ msg = 0;
+ user = 0;
+ }
+
+ void AssertionCount::condrollover( int newvalue ) {
+ static int max = (int)pow( 2.0 , 30 );
+ if ( newvalue >= max )
+ rollover();
+ }
+
+ bool DBException::traceExceptions = false;
+
+ void ExceptionInfo::append( BSONObjBuilder& b , const char * m , const char * c ) const {
+ if ( msg.empty() )
+ b.append( m , "unknown assertion" );
+ else
+ b.append( m , msg );
+
+ if ( code )
+ b.append( c , code );
+ }
+
+ string getDbContext();
+
+ /* "warning" assert -- safe to continue, so we don't throw exception. */
+ NOINLINE_DECL void wasserted(const char *msg, const char *file, unsigned line) {
+ static bool rateLimited;
+ static time_t lastWhen;
+ static unsigned lastLine;
+ if( lastLine == line && time(0)-lastWhen < 5 ) {
+ if( rateLimited++ == 0 ) {
+ log() << "rate limiting wassert" << endl;
+ }
+ return;
+ }
+ lastWhen = time(0);
+ lastLine = line;
+
+ problem() << "warning assertion failure " << msg << ' ' << file << ' ' << dec << line << endl;
+ sayDbContext();
+ raiseError(0,msg && *msg ? msg : "wassertion failure");
+ assertionCount.condrollover( ++assertionCount.warning );
+#if defined(_DEBUG) || defined(_DURABLEDEFAULTON) || defined(_DURABLEDEFAULTOFF)
+ // this is so we notice in buildbot
+ log() << "\n\n***aborting after wassert() failure in a debug/test build\n\n" << endl;
+ abort();
+#endif
+ }
+
+ NOINLINE_DECL void asserted(const char *msg, const char *file, unsigned line) {
+ assertionCount.condrollover( ++assertionCount.regular );
+ problem() << "Assertion failure " << msg << ' ' << file << ' ' << dec << line << endl;
+ sayDbContext();
+ raiseError(0,msg && *msg ? msg : "assertion failure");
+ stringstream temp;
+ temp << "assertion " << file << ":" << line;
+ AssertionException e(temp.str(),0);
+ breakpoint();
+#if defined(_DEBUG) || defined(_DURABLEDEFAULTON) || defined(_DURABLEDEFAULTOFF)
+ // this is so we notice in buildbot
+ log() << "\n\n***aborting after assert() failure as this is a debug/test build\n\n" << endl;
+ abort();
+#endif
+ throw e;
+ }
+
+ NOINLINE_DECL void verifyFailed( int msgid ) {
+ assertionCount.condrollover( ++assertionCount.regular );
+ problem() << "Assertion failure " << msgid << endl;
+ sayDbContext();
+ raiseError(0,"assertion failure");
+ stringstream temp;
+ temp << msgid;
+ AssertionException e(temp.str(),0);
+ breakpoint();
+#if defined(_DEBUG) || defined(_DURABLEDEFAULTON) || defined(_DURABLEDEFAULTOFF)
+ // this is so we notice in buildbot
+ log() << "\n\n***aborting after verify() failure in a debug/test build\n\n" << endl;
+ abort();
+#endif
+ throw e;
+ }
+
+ void uassert_nothrow(const char *msg) {
+ raiseError(0,msg);
+ }
+
+ void uasserted(int msgid , const string &msg) {
+ uasserted(msgid, msg.c_str());
+ }
+
+ NOINLINE_DECL void uasserted(int msgid, const char *msg) {
+ assertionCount.condrollover( ++assertionCount.user );
+ LOG(1) << "User Assertion: " << msgid << ":" << msg << endl;
+ raiseError(msgid,msg);
+ throw UserException(msgid, msg);
+ }
+
+ void msgasserted(int msgid, const string &msg) {
+ msgasserted(msgid, msg.c_str());
+ }
+
+ NOINLINE_DECL void msgasserted(int msgid, const char *msg) {
+ assertionCount.condrollover( ++assertionCount.warning );
+ tlog() << "Assertion: " << msgid << ":" << msg << endl;
+ raiseError(msgid,msg && *msg ? msg : "massert failure");
+ breakpoint();
+ printStackTrace();
+ throw MsgAssertionException(msgid, msg);
+ }
+
+ NOINLINE_DECL void msgassertedNoTrace(int msgid, const char *msg) {
+ assertionCount.condrollover( ++assertionCount.warning );
+ log() << "Assertion: " << msgid << ":" << msg << endl;
+ raiseError(msgid,msg && *msg ? msg : "massert failure");
+ throw MsgAssertionException(msgid, msg);
+ }
+
+ NOINLINE_DECL void streamNotGood( int code , string msg , std::ios& myios ) {
+ stringstream ss;
+ // errno might not work on all systems for streams
+ // if it doesn't for a system should deal with here
+ ss << msg << " stream invalid: " << errnoWithDescription();
+ throw UserException( code , ss.str() );
+ }
+
+ string errnoWithPrefix( const char * prefix ) {
+ stringstream ss;
+ if ( prefix )
+ ss << prefix << ": ";
+ ss << errnoWithDescription();
+ return ss.str();
+ }
+
+ string demangleName( const type_info& typeinfo ) {
+#ifdef _WIN32
+ return typeinfo.name();
+#else
+ int status;
+
+ char * niceName = abi::__cxa_demangle(typeinfo.name(), 0, 0, &status);
+ if ( ! niceName )
+ return typeinfo.name();
+
+ string s = niceName;
+ free(niceName);
+ return s;
+#endif
+ }
+
+ NOINLINE_DECL ErrorMsg::ErrorMsg(const char *msg, char ch) {
+ int l = strlen(msg);
+ assert( l < 128);
+ memcpy(buf, msg, l);
+ char *p = buf + l;
+ p[0] = ch;
+ p[1] = 0;
+ }
+
+ NOINLINE_DECL ErrorMsg::ErrorMsg(const char *msg, unsigned val) {
+ int l = strlen(msg);
+ assert( l < 128);
+ memcpy(buf, msg, l);
+ char *p = buf + l;
+ sprintf(p, "%u", val);
+ }
+
+}
+
diff --git a/src/mongo/util/assert_util.h b/src/mongo/util/assert_util.h
new file mode 100644
index 00000000000..2e6b2a9732a
--- /dev/null
+++ b/src/mongo/util/assert_util.h
@@ -0,0 +1,275 @@
+// assert_util.h
+
+/* 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 "../db/lasterror.h"
+
+// MONGO_NORETURN undefed at end of file
+#ifdef __GNUC__
+# define MONGO_NORETURN __attribute__((__noreturn__))
+#else
+# define MONGO_NORETURN
+#endif
+
+namespace mongo {
+
+ enum CommonErrorCodes {
+ DatabaseDifferCaseCode = 13297 ,
+ SendStaleConfigCode = 13388 ,
+ RecvStaleConfigCode = 9996
+ };
+
+ class AssertionCount {
+ public:
+ AssertionCount();
+ void rollover();
+ void condrollover( int newValue );
+
+ int regular;
+ int warning;
+ int msg;
+ int user;
+ int rollovers;
+ };
+
+ extern AssertionCount assertionCount;
+
+ struct ExceptionInfo {
+ ExceptionInfo() : msg(""),code(-1) {}
+ ExceptionInfo( const char * m , int c )
+ : msg( m ) , code( c ) {
+ }
+ ExceptionInfo( const string& m , int c )
+ : msg( m ) , code( c ) {
+ }
+ void append( BSONObjBuilder& b , const char * m = "$err" , const char * c = "code" ) const ;
+ string toString() const { stringstream ss; ss << "exception: " << code << " " << msg; return ss.str(); }
+ bool empty() const { return msg.empty(); }
+
+ void reset(){ msg = ""; code=-1; }
+
+ string msg;
+ int code;
+ };
+
+ /** helper class that builds error strings. lighter weight than a StringBuilder, albeit less flexible.
+ NOINLINE_DECL used in the constructor implementations as we are assuming this is a cold code path when used.
+
+ example:
+ throw UserException(123, ErrorMsg("blah", num_val));
+ */
+ class ErrorMsg {
+ public:
+ ErrorMsg(const char *msg, char ch);
+ ErrorMsg(const char *msg, unsigned val);
+ operator string() const { return buf; }
+ private:
+ char buf[256];
+ };
+
+ class DBException;
+ string causedBy( const DBException& e );
+ string causedBy( const string& e );
+
+ class DBException : public std::exception {
+ public:
+ DBException( const ExceptionInfo& ei ) : _ei(ei) { traceIfNeeded(*this); }
+ DBException( const char * msg , int code ) : _ei(msg,code) { traceIfNeeded(*this); }
+ DBException( const string& msg , int code ) : _ei(msg,code) { traceIfNeeded(*this); }
+ virtual ~DBException() throw() { }
+
+ virtual const char* what() const throw() { return _ei.msg.c_str(); }
+ virtual int getCode() const { return _ei.code; }
+
+ virtual void appendPrefix( stringstream& ss ) const { }
+ virtual void addContext( const string& str ) {
+ _ei.msg = str + causedBy( _ei.msg );
+ }
+
+ virtual string toString() const {
+ stringstream ss; ss << getCode() << " " << what(); return ss.str();
+ return ss.str();
+ }
+
+ const ExceptionInfo& getInfo() const { return _ei; }
+
+ static void traceIfNeeded( const DBException& e ) {
+ if( traceExceptions && ! inShutdown() ){
+ warning() << "DBException thrown" << causedBy( e ) << endl;
+ printStackTrace();
+ }
+ }
+
+ static bool traceExceptions;
+
+ protected:
+ ExceptionInfo _ei;
+ };
+
+ class AssertionException : public DBException {
+ public:
+
+ AssertionException( const ExceptionInfo& ei ) : DBException(ei) {}
+ AssertionException( const char * msg , int code ) : DBException(msg,code) {}
+ AssertionException( const string& msg , int code ) : DBException(msg,code) {}
+
+ virtual ~AssertionException() throw() { }
+
+ virtual bool severe() { return true; }
+ virtual bool isUserAssertion() { return false; }
+
+ /* true if an interrupted exception - see KillCurrentOp */
+ bool interrupted() {
+ return _ei.code == 11600 || _ei.code == 11601;
+ }
+ };
+
+ /* UserExceptions are valid errors that a user can cause, like out of disk space or duplicate key */
+ class UserException : public AssertionException {
+ public:
+ UserException(int c , const string& m) : AssertionException( m , c ) {}
+
+ virtual bool severe() { return false; }
+ virtual bool isUserAssertion() { return true; }
+ virtual void appendPrefix( stringstream& ss ) const { ss << "userassert:"; }
+ };
+
+ class MsgAssertionException : public AssertionException {
+ public:
+ MsgAssertionException( const ExceptionInfo& ei ) : AssertionException( ei ) {}
+ MsgAssertionException(int c, const string& m) : AssertionException( m , c ) {}
+ virtual bool severe() { return false; }
+ virtual void appendPrefix( stringstream& ss ) const { ss << "massert:"; }
+ };
+
+ void asserted(const char *msg, const char *file, unsigned line) MONGO_NORETURN;
+ void wasserted(const char *msg, const char *file, unsigned line);
+ void verifyFailed( int msgid );
+
+ /** a "user assertion". throws UserAssertion. logs. typically used for errors that a user
+ could cause, such as duplicate key, disk full, etc.
+ */
+ void uasserted(int msgid, const char *msg) MONGO_NORETURN;
+ void uasserted(int msgid , const string &msg);
+
+ /** reported via lasterror, but don't throw exception */
+ void uassert_nothrow(const char *msg);
+
+ /** msgassert and massert are for errors that are internal but have a well defined error text string.
+ a stack trace is logged.
+ */
+ void msgassertedNoTrace(int msgid, const char *msg) MONGO_NORETURN;
+ inline void msgassertedNoTrace(int msgid, const string& msg) { msgassertedNoTrace( msgid , msg.c_str() ); }
+ void msgasserted(int msgid, const char *msg) MONGO_NORETURN;
+ void msgasserted(int msgid, const string &msg);
+
+ /* convert various types of exceptions to strings */
+ inline string causedBy( const char* e ){ return (string)" :: caused by :: " + e; }
+ inline string causedBy( const DBException& e ){ return causedBy( e.toString().c_str() ); }
+ inline string causedBy( const std::exception& e ){ return causedBy( e.what() ); }
+ inline string causedBy( const string& e ){ return causedBy( e.c_str() ); }
+
+ /** in the mongodb source, use verify() instead of assert(). verify is always evaluated even in release builds. */
+ inline void verify( int msgid , bool testOK ) { if ( ! testOK ) verifyFailed( msgid ); }
+
+#ifdef assert
+#undef assert
+#endif
+
+#define MONGO_assert(_Expression) (void)( MONGO_likely(!!(_Expression)) || (mongo::asserted(#_Expression, __FILE__, __LINE__), 0) )
+#define assert MONGO_assert
+
+ /* "user assert". if asserts, user did something wrong, not our code */
+#define MONGO_uassert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || (mongo::uasserted(msgid, msg), 0) )
+#define uassert MONGO_uassert
+
+ /* warning only - keeps going */
+#define MONGO_wassert(_Expression) (void)( MONGO_likely(!!(_Expression)) || (mongo::wasserted(#_Expression, __FILE__, __LINE__), 0) )
+#define wassert MONGO_wassert
+
+ /* display a message, no context, and throw assertionexception
+
+ easy way to throw an exception and log something without our stack trace
+ display happening.
+ */
+#define MONGO_massert(msgid, msg, expr) (void)( MONGO_likely(!!(expr)) || (mongo::msgasserted(msgid, msg), 0) )
+#define massert MONGO_massert
+
+ /* dassert is 'debug assert' -- might want to turn off for production as these
+ could be slow.
+ */
+#if defined(_DEBUG)
+# define MONGO_dassert assert
+#else
+# define MONGO_dassert(x)
+#endif
+#define dassert MONGO_dassert
+
+ // some special ids that we want to duplicate
+
+ // > 10000 asserts
+ // < 10000 UserException
+
+ enum { ASSERT_ID_DUPKEY = 11000 };
+
+ /* throws a uassertion with an appropriate msg */
+ void streamNotGood( int code , string msg , std::ios& myios ) MONGO_NORETURN;
+
+ inline void assertStreamGood(unsigned msgid, string msg, std::ios& myios) {
+ if( !myios.good() ) streamNotGood(msgid, msg, myios);
+ }
+
+ string demangleName( const type_info& typeinfo );
+
+} // namespace mongo
+
+#define BOOST_CHECK_EXCEPTION MONGO_BOOST_CHECK_EXCEPTION
+#define MONGO_BOOST_CHECK_EXCEPTION( expression ) \
+ try { \
+ expression; \
+ } catch ( const std::exception &e ) { \
+ stringstream ss; \
+ ss << "caught boost exception: " << e.what() << ' ' << __FILE__ << ' ' << __LINE__; \
+ msgasserted( 13294 , ss.str() ); \
+ } catch ( ... ) { \
+ massert( 10437 , "unknown boost failed" , false ); \
+ }
+
+#define MONGO_BOOST_CHECK_EXCEPTION_WITH_MSG( expression, msg ) \
+ try { \
+ expression; \
+ } catch ( const std::exception &e ) { \
+ stringstream ss; \
+ ss << msg << " caught boost exception: " << e.what(); \
+ msgasserted( 14043 , ss.str() ); \
+ } catch ( ... ) { \
+ msgasserted( 14044 , string("unknown boost failed ") + msg ); \
+ }
+
+#define DESTRUCTOR_GUARD MONGO_DESTRUCTOR_GUARD
+#define MONGO_DESTRUCTOR_GUARD( expression ) \
+ try { \
+ expression; \
+ } catch ( const std::exception &e ) { \
+ problem() << "caught exception (" << e.what() << ") in destructor (" << __FUNCTION__ << ")" << endl; \
+ } catch ( ... ) { \
+ problem() << "caught unknown exception in destructor (" << __FUNCTION__ << ")" << endl; \
+ }
+
+#undef MONGO_NORETURN
diff --git a/src/mongo/util/background.cpp b/src/mongo/util/background.cpp
new file mode 100644
index 00000000000..ef3ee9426b9
--- /dev/null
+++ b/src/mongo/util/background.cpp
@@ -0,0 +1,190 @@
+// @file background.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+
+#include "concurrency/mutex.h"
+#include "concurrency/spin_lock.h"
+
+#include "background.h"
+#include "time_support.h"
+#include "timer.h"
+
+#include "mongoutils/str.h"
+
+namespace mongo {
+
+ // both the BackgroundJob and the internal thread point to JobStatus
+ struct BackgroundJob::JobStatus {
+ JobStatus( bool delFlag )
+ : deleteSelf(delFlag), m("backgroundJob"), state(NotStarted) { }
+
+ const bool deleteSelf;
+
+ mongo::mutex m; // protects state below
+ boost::condition finished; // means _state == Done
+ State state;
+ };
+
+ BackgroundJob::BackgroundJob( bool selfDelete ) {
+ _status.reset( new JobStatus( selfDelete ) );
+ }
+
+ // Background object can be only be destroyed after jobBody() ran
+ void BackgroundJob::jobBody( boost::shared_ptr<JobStatus> status ) {
+ LOG(1) << "BackgroundJob starting: " << name() << endl;
+ {
+ scoped_lock l( status->m );
+ massert( 13643 , mongoutils::str::stream() << "backgroundjob already started: " << name() , status->state == NotStarted );
+ status->state = Running;
+ }
+
+ const string threadName = name();
+ if( ! threadName.empty() )
+ setThreadName( threadName.c_str() );
+
+ try {
+ run();
+ }
+ catch ( std::exception& e ) {
+ log( LL_ERROR ) << "backgroundjob " << name() << "error: " << e.what() << endl;
+ }
+ catch(...) {
+ log( LL_ERROR ) << "uncaught exception in BackgroundJob " << name() << endl;
+ }
+
+ {
+ scoped_lock l( status->m );
+ status->state = Done;
+ status->finished.notify_all();
+ }
+
+ if( status->deleteSelf )
+ delete this;
+ }
+
+ BackgroundJob& BackgroundJob::go() {
+ boost::thread t( boost::bind( &BackgroundJob::jobBody , this, _status ) );
+ return *this;
+ }
+
+ bool BackgroundJob::wait( unsigned msTimeOut ) {
+ assert( !_status->deleteSelf ); // you cannot call wait on a self-deleting job
+ scoped_lock l( _status->m );
+ while ( _status->state != Done ) {
+ if ( msTimeOut ) {
+ // add msTimeOut millisecond to current time
+ boost::xtime xt;
+ boost::xtime_get( &xt, boost::TIME_UTC );
+
+ unsigned long long ns = msTimeOut * 1000000ULL; // milli to nano
+ if ( xt.nsec + ns < 1000000000 ) {
+ xt.nsec = (boost::xtime::xtime_nsec_t) (xt.nsec + ns);
+ }
+ else {
+ xt.sec += 1 + ns / 1000000000;
+ xt.nsec = ( ns + xt.nsec ) % 1000000000;
+ }
+
+ if ( ! _status->finished.timed_wait( l.boost() , xt ) )
+ return false;
+
+ }
+ else {
+ _status->finished.wait( l.boost() );
+ }
+ }
+ return true;
+ }
+
+ BackgroundJob::State BackgroundJob::getState() const {
+ scoped_lock l( _status->m);
+ return _status->state;
+ }
+
+ bool BackgroundJob::running() const {
+ scoped_lock l( _status->m);
+ return _status->state == Running;
+ }
+
+ // -------------------------
+
+ PeriodicTask::PeriodicTask() {
+ if ( ! theRunner )
+ theRunner = new Runner();
+ theRunner->add( this );
+ }
+
+ PeriodicTask::~PeriodicTask() {
+ theRunner->remove( this );
+ }
+
+ void PeriodicTask::Runner::add( PeriodicTask* task ) {
+ scoped_spinlock lk( _lock );
+ _tasks.push_back( task );
+ }
+
+ void PeriodicTask::Runner::remove( PeriodicTask* task ) {
+ scoped_spinlock lk( _lock );
+ for ( size_t i=0; i<_tasks.size(); i++ ) {
+ if ( _tasks[i] == task ) {
+ _tasks[i] = 0;
+ break;
+ }
+ }
+ }
+
+ void PeriodicTask::Runner::run() {
+ int sleeptime = 60;
+ DEV sleeptime = 5; // to catch race conditions
+
+ while ( ! inShutdown() ) {
+
+ sleepsecs( sleeptime );
+
+ scoped_spinlock lk( _lock );
+
+ size_t size = _tasks.size();
+
+ for ( size_t i=0; i<size; i++ ) {
+ PeriodicTask * t = _tasks[i];
+ if ( ! t )
+ continue;
+
+ if ( inShutdown() )
+ break;
+
+ Timer timer;
+ try {
+ t->taskDoWork();
+ }
+ catch ( std::exception& e ) {
+ error() << "task: " << t->taskName() << " failed: " << e.what() << endl;
+ }
+ catch ( ... ) {
+ error() << "task: " << t->taskName() << " failed with unknown error" << endl;
+ }
+
+ int ms = timer.millis();
+ LOG( ms <= 3 ? 1 : 0 ) << "task: " << t->taskName() << " took: " << ms << "ms" << endl;
+ }
+ }
+ }
+
+ PeriodicTask::Runner* PeriodicTask::theRunner = 0;
+
+} // namespace mongo
diff --git a/src/mongo/util/background.h b/src/mongo/util/background.h
new file mode 100644
index 00000000000..496a1f44f88
--- /dev/null
+++ b/src/mongo/util/background.h
@@ -0,0 +1,155 @@
+// @file background.h
+
+/* 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 "concurrency/spin_lock.h"
+
+namespace mongo {
+
+ /**
+ * Background thread dispatching.
+ * subclass and define run()
+ *
+ * It is ok to call go(), that is, run the job, more than once -- if the
+ * previous invocation has finished. Thus one pattern of use is to embed
+ * a backgroundjob in your object and reuse it (or same thing with
+ * inheritance). Each go() call spawns a new thread.
+ *
+ * Thread safety:
+ * note when job destructs, the thread is not terminated if still running.
+ * generally if the thread could still be running, allocate the job dynamically
+ * and set deleteSelf to true.
+ *
+ * go() and wait() are not thread safe
+ * run() will be executed on the background thread
+ * BackgroundJob object must exist for as long the background thread is running
+ */
+
+ class BackgroundJob : boost::noncopyable {
+ protected:
+ /**
+ * sub-class must intantiate the BackgrounJob
+ *
+ * @param selfDelete if set to true, object will destruct itself after the run() finished
+ * @note selfDelete instantes cannot be wait()-ed upon
+ */
+ explicit BackgroundJob(bool selfDelete = false);
+
+ virtual string name() const = 0;
+
+ /**
+ * define this to do your work.
+ * after this returns, state is set to done.
+ * after this returns, deleted if deleteSelf true.
+ *
+ * NOTE:
+ * if run() throws, the exception will be caught within 'this' object and will ultimately lead to the
+ * BackgroundJob's thread being finished, as if run() returned.
+ *
+ */
+ virtual void run() = 0;
+
+ public:
+ enum State {
+ NotStarted,
+ Running,
+ Done
+ };
+
+ virtual ~BackgroundJob() { }
+
+ /**
+ * starts job.
+ * returns immediatelly after dispatching.
+ *
+ * @note the BackgroundJob object must live for as long the thread is still running, ie
+ * until getState() returns Done.
+ */
+ BackgroundJob& go();
+
+ /**
+ * wait for completion.
+ *
+ * @param msTimeOut maximum amount of time to wait in millisecons
+ * @return true if did not time out. false otherwise.
+ *
+ * @note you can call wait() more than once if the first call times out.
+ * but you cannot call wait on a self-deleting job.
+ */
+ bool wait( unsigned msTimeOut = 0 );
+
+ // accessors
+ State getState() const;
+ bool running() const;
+
+ private:
+ struct JobStatus;
+ boost::shared_ptr<JobStatus> _status; // shared between 'this' and body() thread
+
+ void jobBody( boost::shared_ptr<JobStatus> status );
+
+ };
+
+ /**
+ * these run "roughly" every minute
+ * instantiate statically
+ * class MyTask : public PeriodicTask {
+ * public:
+ * virtual string name() const { return "MyTask; " }
+ * virtual void doWork() { log() << "hi" << endl; }
+ * } myTask;
+ */
+ class PeriodicTask {
+ public:
+ PeriodicTask();
+ virtual ~PeriodicTask();
+
+ virtual void taskDoWork() = 0;
+ virtual string taskName() const = 0;
+
+ class Runner : public BackgroundJob {
+ public:
+ virtual ~Runner(){}
+
+ virtual string name() const { return "PeriodicTask::Runner"; }
+
+ virtual void run();
+
+ void add( PeriodicTask* task );
+ void remove( PeriodicTask* task );
+
+ private:
+
+ SpinLock _lock;
+
+ // these are NOT owned by Runner
+ // Runner will not delete these
+ // this never gets smaller
+ // only fields replaced with nulls
+ vector<PeriodicTask*> _tasks;
+
+ };
+
+ static Runner* theRunner;
+
+ };
+
+
+
+
+} // namespace mongo
diff --git a/src/mongo/util/base64.cpp b/src/mongo/util/base64.cpp
new file mode 100644
index 00000000000..aff06e26126
--- /dev/null
+++ b/src/mongo/util/base64.cpp
@@ -0,0 +1,109 @@
+// util/base64.cpp
+
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "base64.h"
+
+namespace mongo {
+ namespace base64 {
+
+ Alphabet alphabet;
+
+ void encode( stringstream& ss , const char * data , int size ) {
+ for ( int i=0; i<size; i+=3 ) {
+ int left = size - i;
+ const unsigned char * start = (const unsigned char*)data + i;
+
+ // byte 0
+ ss << alphabet.e(start[0]>>2);
+
+ // byte 1
+ unsigned char temp = ( start[0] << 4 );
+ if ( left == 1 ) {
+ ss << alphabet.e(temp);
+ break;
+ }
+ temp |= ( ( start[1] >> 4 ) & 0xF );
+ ss << alphabet.e(temp);
+
+ // byte 2
+ temp = ( start[1] & 0xF ) << 2;
+ if ( left == 2 ) {
+ ss << alphabet.e(temp);
+ break;
+ }
+ temp |= ( ( start[2] >> 6 ) & 0x3 );
+ ss << alphabet.e(temp);
+
+ // byte 3
+ ss << alphabet.e(start[2] & 0x3f);
+ }
+
+ int mod = size % 3;
+ if ( mod == 1 ) {
+ ss << "==";
+ }
+ else if ( mod == 2 ) {
+ ss << "=";
+ }
+ }
+
+
+ string encode( const char * data , int size ) {
+ stringstream ss;
+ encode( ss , data ,size );
+ return ss.str();
+ }
+
+ string encode( const string& s ) {
+ return encode( s.c_str() , s.size() );
+ }
+
+
+ void decode( stringstream& ss , const string& s ) {
+ uassert( 10270 , "invalid base64" , s.size() % 4 == 0 );
+ const unsigned char * data = (const unsigned char*)s.c_str();
+ int size = s.size();
+
+ unsigned char buf[3];
+ for ( int i=0; i<size; i+=4) {
+ const unsigned char * start = data + i;
+ buf[0] = ( ( alphabet.decode[start[0]] << 2 ) & 0xFC ) | ( ( alphabet.decode[start[1]] >> 4 ) & 0x3 );
+ buf[1] = ( ( alphabet.decode[start[1]] << 4 ) & 0xF0 ) | ( ( alphabet.decode[start[2]] >> 2 ) & 0xF );
+ buf[2] = ( ( alphabet.decode[start[2]] << 6 ) & 0xC0 ) | ( ( alphabet.decode[start[3]] & 0x3F ) );
+
+ int len = 3;
+ if ( start[3] == '=' ) {
+ len = 2;
+ if ( start[2] == '=' ) {
+ len = 1;
+ }
+ }
+ ss.write( (const char*)buf , len );
+ }
+ }
+
+ string decode( const string& s ) {
+ stringstream ss;
+ decode( ss , s );
+ return ss.str();
+ }
+
+ }
+}
+
diff --git a/src/mongo/util/base64.h b/src/mongo/util/base64.h
new file mode 100644
index 00000000000..505b5d78cca
--- /dev/null
+++ b/src/mongo/util/base64.h
@@ -0,0 +1,68 @@
+// util/base64.h
+
+/* 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
+
+namespace mongo {
+ namespace base64 {
+
+ class Alphabet {
+ public:
+ Alphabet()
+ : encode((unsigned char*)
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "+/")
+ , decode(new unsigned char[257]) {
+ memset( decode.get() , 0 , 256 );
+ for ( int i=0; i<64; i++ ) {
+ decode[ encode[i] ] = i;
+ }
+
+ test();
+ }
+ void test() {
+ assert( strlen( (char*)encode ) == 64 );
+ for ( int i=0; i<26; i++ )
+ assert( encode[i] == toupper( encode[i+26] ) );
+ }
+
+ char e( int x ) {
+ return encode[x&0x3f];
+ }
+
+ private:
+ const unsigned char * encode;
+ public:
+ boost::scoped_array<unsigned char> decode;
+ };
+
+ extern Alphabet alphabet;
+
+
+ void encode( stringstream& ss , const char * data , int size );
+ string encode( const char * data , int size );
+ string encode( const string& s );
+
+ void decode( stringstream& ss , const string& s );
+ string decode( const string& s );
+
+
+ void testAlphabet();
+ }
+}
diff --git a/src/mongo/util/bson_util.h b/src/mongo/util/bson_util.h
new file mode 100644
index 00000000000..973e31f1af1
--- /dev/null
+++ b/src/mongo/util/bson_util.h
@@ -0,0 +1,42 @@
+// bson_util.h
+
+/* 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 "../pch.h"
+
+namespace mongo {
+
+template <typename T>
+void bsonArrToNumVector(BSONElement el, vector<T>& results){
+
+ if(el.type() == Array){
+
+ vector<BSONElement> elements = el.Array();
+
+ for(vector<BSONElement>::iterator i = elements.begin(); i != elements.end(); ++i){
+ results.push_back( (T) (*i).Number() );
+ }
+ }
+ else if(el.isNumber()){
+ results.push_back( (T) el.Number() );
+ }
+
+}
+
+
+}
diff --git a/src/mongo/util/bufreader.h b/src/mongo/util/bufreader.h
new file mode 100644
index 00000000000..53f0ba744e2
--- /dev/null
+++ b/src/mongo/util/bufreader.h
@@ -0,0 +1,100 @@
+// @file bufreader.h parse a memory region into usable pieces
+
+/**
+* Copyright (C) 2009 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+namespace mongo {
+
+ /** helper to read and parse a block of memory
+ methods throw the eof exception if the operation would pass the end of the
+ buffer with which we are working.
+ */
+ class BufReader : boost::noncopyable {
+ public:
+ class eof : public std::exception {
+ public:
+ eof() { }
+ virtual const char * what() { return "BufReader eof"; }
+ };
+
+ BufReader(const void *p, unsigned len) : _start(p), _pos(p), _end(((char *)_pos)+len) { }
+
+ bool atEof() const { return _pos == _end; }
+
+ /** read in the object specified, and advance buffer pointer */
+ template <typename T>
+ void read(T &t) {
+ T* cur = (T*) _pos;
+ T *next = cur + 1;
+ if( _end < next ) throw eof();
+ t = *cur;
+ _pos = next;
+ }
+
+ /** verify we can look at t, but do not advance */
+ template <typename T>
+ void peek(T &t) {
+ T* cur = (T*) _pos;
+ T *next = cur + 1;
+ if( _end < next ) throw eof();
+ t = *cur;
+ }
+
+ /** return current offset into buffer */
+ unsigned offset() const { return (char*)_pos - (char*)_start; }
+
+ /** return remaining bytes */
+ unsigned remaining() const { return (char*)_end -(char*)_pos; }
+
+ /** back up by nbytes */
+ void rewind(unsigned nbytes) {
+ _pos = ((char *) _pos) - nbytes;
+ assert( _pos >= _start );
+ }
+
+ /** return current position pointer, and advance by len */
+ const void* skip(unsigned len) {
+ const char *nxt = ((char *) _pos) + len;
+ if( _end < nxt ) throw eof();
+ const void *p = _pos;
+ _pos = nxt;
+ return p;
+ }
+
+ void readStr(string& s) {
+ StringBuilder b;
+ while( 1 ) {
+ char ch;
+ read(ch);
+ if( ch == 0 )
+ break;
+ b << ch;
+ }
+ s = b.str();
+ }
+
+ const void* pos() { return _pos; }
+ const void* start() { return _start; }
+
+ private:
+ const void *_start;
+ const void *_pos;
+ const void *_end;
+ };
+
+}
diff --git a/src/mongo/util/checksum.h b/src/mongo/util/checksum.h
new file mode 100644
index 00000000000..009ab56fbeb
--- /dev/null
+++ b/src/mongo/util/checksum.h
@@ -0,0 +1,37 @@
+#pragma once
+#include "../pch.h"
+namespace mongo {
+ /** a simple, rather dumb, but very fast checksum. see perftests.cpp for unit tests. */
+ struct Checksum {
+ union {
+ unsigned char bytes[16];
+ unsigned long long words[2];
+ };
+
+ // if you change this you must bump dur::CurrentVersion
+ void gen(const void *buf, unsigned len) {
+ wassert( ((size_t)buf) % 8 == 0 ); // performance warning
+ unsigned n = len / 8 / 2;
+ const unsigned long long *p = (const unsigned long long *) buf;
+ unsigned long long a = 0;
+ for( unsigned i = 0; i < n; i++ ) {
+ a += (*p ^ i);
+ p++;
+ }
+ unsigned long long b = 0;
+ for( unsigned i = 0; i < n; i++ ) {
+ b += (*p ^ i);
+ p++;
+ }
+ unsigned long long c = 0;
+ for( unsigned i = n * 2 * 8; i < len; i++ ) { // 0-7 bytes left
+ c = (c << 8) | ((const char *)buf)[i];
+ }
+ words[0] = a ^ len;
+ words[1] = b ^ c;
+ }
+
+ bool operator==(const Checksum& rhs) const { return words[0]==rhs.words[0] && words[1]==rhs.words[1]; }
+ bool operator!=(const Checksum& rhs) const { return words[0]!=rhs.words[0] || words[1]!=rhs.words[1]; }
+ };
+}
diff --git a/src/mongo/util/compress.cpp b/src/mongo/util/compress.cpp
new file mode 100644
index 00000000000..bcde488b88b
--- /dev/null
+++ b/src/mongo/util/compress.cpp
@@ -0,0 +1,31 @@
+// @file compress.cpp
+
+#include "../third_party/snappy/snappy.h"
+#include "compress.h"
+#include <string>
+#include <string.h>
+#include <assert.h>
+
+namespace mongo {
+
+ void rawCompress(const char* input,
+ size_t input_length,
+ char* compressed,
+ size_t* compressed_length)
+ {
+ snappy::RawCompress(input, input_length, compressed, compressed_length);
+ }
+
+ size_t maxCompressedLength(size_t source_len) {
+ return snappy::MaxCompressedLength(source_len);
+ }
+
+ size_t compress(const char* input, size_t input_length, std::string* output) {
+ return snappy::Compress(input, input_length, output);
+ }
+
+ bool uncompress(const char* compressed, size_t compressed_length, std::string* uncompressed) {
+ return snappy::Uncompress(compressed, compressed_length, uncompressed);
+ }
+
+}
diff --git a/src/mongo/util/compress.h b/src/mongo/util/compress.h
new file mode 100644
index 00000000000..5bc5a3392bb
--- /dev/null
+++ b/src/mongo/util/compress.h
@@ -0,0 +1,21 @@
+// @file compress.h
+
+#pragma once
+
+#include <string>
+
+namespace mongo {
+
+ size_t compress(const char* input, size_t input_length, std::string* output);
+
+ bool uncompress(const char* compressed, size_t compressed_length, std::string* uncompressed);
+
+ size_t maxCompressedLength(size_t source_len);
+ void rawCompress(const char* input,
+ size_t input_length,
+ char* compressed,
+ size_t* compressed_length);
+
+}
+
+
diff --git a/src/mongo/util/concurrency/README b/src/mongo/util/concurrency/README
new file mode 100644
index 00000000000..1a19264f4b6
--- /dev/null
+++ b/src/mongo/util/concurrency/README
@@ -0,0 +1,39 @@
+util/concurrency/ files
+
+msg.h - message passing between threads
+
+mutex.h - small enhancements that wrap boost::mutex
+ also SimpleMutex
+
+mvar.h
+ This is based on haskell's MVar synchronization primitive:
+ http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent-MVar.html
+ It is a thread-safe queue that can hold at most one object.
+ You can also think of it as a box that can be either full or empty.
+
+race.h
+ RACECHECK
+
+rwlock.h - read/write locks (RWLock)
+ RWLock
+ RWLockRecursive
+ RWLockRecursiveNongreedy
+
+spin_lock.h
+
+synchronization.h
+ Notification, NotifyAll
+
+threadlocal.h
+
+thread_pool.h
+
+value.h
+ Guarded
+ DiagStr
+ mapsf
+
+goofy things that need reworking:
+ list.h
+ task.h
+
diff --git a/src/mongo/util/concurrency/list.h b/src/mongo/util/concurrency/list.h
new file mode 100644
index 00000000000..61bdd55f46f
--- /dev/null
+++ b/src/mongo/util/concurrency/list.h
@@ -0,0 +1,99 @@
+// list.h
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+namespace mongo {
+
+ /* DONT USE THIS. it was a dumb idea.
+
+ this class uses a mutex for writes, but not for reads.
+ we can get fancier later...
+
+ struct Member : public List1<Member>::Base {
+ const char *host;
+ int port;
+ };
+ List1<Member> _members;
+ _members.head()->next();
+
+ */
+ template<typename T>
+ class List1 : boost::noncopyable {
+ public:
+ /* next() and head() return 0 at end of list */
+
+ List1() : _head(0), _m("List1"), _orphans(0) { }
+
+ class Base {
+ friend class List1;
+ T *_next;
+ public:
+ Base() : _next(0){}
+ ~Base() { wassert(false); } // we never want this to happen
+ T* next() const { return _next; }
+ };
+
+ /** note this is safe:
+
+ T* p = mylist.head();
+ if( p )
+ use(p);
+
+ and this is not:
+
+ if( mylist.head() )
+ use( mylist.head() ); // could become 0
+ */
+ T* head() const { return (T*) _head; }
+
+ void push(T* t) {
+ assert( t->_next == 0 );
+ scoped_lock lk(_m);
+ t->_next = (T*) _head;
+ _head = t;
+ }
+
+ // intentionally leaks.
+ void orphanAll() {
+ scoped_lock lk(_m);
+ _head = 0;
+ }
+
+ /* t is not deleted, but is removed from the list. (orphaned) */
+ void orphan(T* t) {
+ scoped_lock lk(_m);
+ T *&prev = (T*&) _head;
+ T *n = prev;
+ while( n != t ) {
+ uassert( 14050 , "List1: item to orphan not in list", n );
+ prev = n->_next;
+ n = prev;
+ }
+ prev = t->_next;
+ if( ++_orphans > 500 )
+ log() << "warning List1 orphans=" << _orphans << '\n';
+ }
+
+ private:
+ volatile T *_head;
+ mongo::mutex _m;
+ int _orphans;
+ };
+
+};
diff --git a/src/mongo/util/concurrency/msg.h b/src/mongo/util/concurrency/msg.h
new file mode 100644
index 00000000000..0b9a7c5048c
--- /dev/null
+++ b/src/mongo/util/concurrency/msg.h
@@ -0,0 +1,61 @@
+// @file msg.h - interthread message passing
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,b
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include <deque>
+#include "task.h"
+
+namespace mongo {
+
+ namespace task {
+
+ typedef boost::function<void()> lam;
+
+ /** typical usage is: task::fork( new Server("threadname") ); */
+ class Server : public Task {
+ public:
+ /** send a message to the port */
+ void send(lam);
+
+ Server(string name) : m("server"), _name(name), rq(false) { }
+ virtual ~Server() { }
+
+ /** send message but block until function completes */
+ void call(const lam&);
+
+ void requeue() { rq = true; }
+
+ protected:
+ /* REMINDER : for use in mongod, you will want to have this call Client::initThread(). */
+ virtual void starting() { }
+
+ private:
+ virtual bool initClient() { return true; }
+ virtual string name() const { return _name; }
+ void doWork();
+ deque<lam> d;
+ mongo::mutex m;
+ boost::condition c;
+ string _name;
+ bool rq;
+ };
+
+ }
+
+}
diff --git a/src/mongo/util/concurrency/mutex.h b/src/mongo/util/concurrency/mutex.h
new file mode 100644
index 00000000000..429f280b1cb
--- /dev/null
+++ b/src/mongo/util/concurrency/mutex.h
@@ -0,0 +1,228 @@
+// @file mutex.h
+
+/* 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 "../heapcheck.h"
+#include "threadlocal.h"
+#if defined(_DEBUG)
+#include "mutexdebugger.h"
+#endif
+
+namespace mongo {
+
+ void printStackTrace( ostream &o );
+
+ inline boost::xtime incxtimemillis( long long s ) {
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.sec += (int)( s / 1000 );
+ xt.nsec += (int)(( s % 1000 ) * 1000000);
+ if ( xt.nsec >= 1000000000 ) {
+ xt.nsec -= 1000000000;
+ xt.sec++;
+ }
+ return xt;
+ }
+
+ // 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:
+ static bool _destroyingStatics;
+ ~StaticObserver() { _destroyingStatics = true; }
+ };
+
+ /** On pthread systems, it is an error to destroy a mutex while held (boost mutex
+ * may use pthread). Static global mutexes may be held upon shutdown in our
+ * implementation, and this way we avoid destroying them.
+ * NOT recursive.
+ */
+ class mutex : boost::noncopyable {
+ public:
+ const char * const _name;
+ mutex(const char *name) : _name(name)
+ {
+ _m = new boost::timed_mutex();
+ IGNORE_OBJECT( _m ); // Turn-off heap checking on _m
+ }
+ ~mutex() {
+ if( !StaticObserver::_destroyingStatics ) {
+ UNIGNORE_OBJECT( _m );
+ delete _m;
+ }
+ }
+
+ class try_lock : boost::noncopyable {
+ public:
+ try_lock( mongo::mutex &m , int millis = 0 )
+ : _l( m.boost() , incxtimemillis( millis ) ) ,
+#if BOOST_VERSION >= 103500
+ ok( _l.owns_lock() )
+#else
+ ok( _l.locked() )
+#endif
+ { }
+ private:
+ boost::timed_mutex::scoped_timed_lock _l;
+ public:
+ const bool ok;
+ };
+
+ class scoped_lock : boost::noncopyable {
+ public:
+#if defined(_DEBUG)
+ struct PostStaticCheck {
+ PostStaticCheck() {
+ if ( StaticObserver::_destroyingStatics ) {
+ cout << "_DEBUG warning trying to lock a mongo::mutex during static shutdown" << endl;
+ printStackTrace( cout );
+ }
+ }
+ } _check;
+ mongo::mutex * const _mut;
+#endif
+ scoped_lock( mongo::mutex &m ) :
+#if defined(_DEBUG)
+ _mut(&m),
+#endif
+ _l( m.boost() ) {
+#if defined(_DEBUG)
+ mutexDebugger.entering(_mut->_name);
+#endif
+ }
+ ~scoped_lock() {
+#if defined(_DEBUG)
+ mutexDebugger.leaving(_mut->_name);
+#endif
+ }
+ boost::timed_mutex::scoped_lock &boost() { return _l; }
+ private:
+ boost::timed_mutex::scoped_lock _l;
+ };
+ private:
+ boost::timed_mutex &boost() { return *_m; }
+ boost::timed_mutex *_m;
+ };
+
+ typedef mutex::scoped_lock scoped_lock;
+ typedef boost::recursive_mutex::scoped_lock recursive_scoped_lock;
+
+ /** The concept with SimpleMutex is that it is a basic lock/unlock with no
+ special functionality (such as try and try timeout). Thus it can be
+ implemented using OS-specific facilities in all environments (if desired).
+ On Windows, the implementation below is faster than boost mutex.
+ */
+#if defined(_WIN32)
+ class SimpleMutex : boost::noncopyable {
+ CRITICAL_SECTION _cs;
+ public:
+ SimpleMutex(const char *name) { InitializeCriticalSection(&_cs); }
+ ~SimpleMutex() { DeleteCriticalSection(&_cs); }
+
+#if defined(_DEBUG)
+ ThreadLocalValue<int> _nlocksByMe;
+ void lock() {
+ assert( _nlocksByMe.get() == 0 ); // indicates you rae trying to lock recursively
+ _nlocksByMe.set(1);
+ EnterCriticalSection(&_cs);
+ }
+ void dassertLocked() const {
+ assert( _nlocksByMe.get() == 1 );
+ }
+ void unlock() {
+ dassertLocked();
+ _nlocksByMe.set(0);
+ LeaveCriticalSection(&_cs);
+ }
+#else
+ void dassertLocked() const { }
+ void lock() {
+ EnterCriticalSection(&_cs);
+ }
+ void unlock() {
+ LeaveCriticalSection(&_cs);
+ }
+#endif
+
+ class scoped_lock : boost::noncopyable {
+ SimpleMutex& _m;
+ public:
+ scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); }
+ ~scoped_lock() { _m.unlock(); }
+# if defined(_DEBUG)
+ const SimpleMutex& m() const { return _m; }
+# endif
+ };
+ };
+#else
+ class SimpleMutex : boost::noncopyable {
+ public:
+ void dassertLocked() const { }
+ SimpleMutex(const char* name) { assert( pthread_mutex_init(&_lock,0) == 0 ); }
+ ~SimpleMutex(){
+ if ( ! StaticObserver::_destroyingStatics ) {
+ assert( pthread_mutex_destroy(&_lock) == 0 );
+ }
+ }
+
+ void lock() { assert( pthread_mutex_lock(&_lock) == 0 ); }
+ void unlock() { assert( pthread_mutex_unlock(&_lock) == 0 ); }
+ public:
+ class scoped_lock : boost::noncopyable {
+ SimpleMutex& _m;
+ public:
+ scoped_lock( SimpleMutex &m ) : _m(m) { _m.lock(); }
+ ~scoped_lock() { _m.unlock(); }
+ const SimpleMutex& m() const { return _m; }
+ };
+
+ private:
+ pthread_mutex_t _lock;
+ };
+
+#endif
+
+ /** This can be used instead of boost recursive mutex. The advantage is the _DEBUG checks
+ * and ability to assertLocked(). This has not yet been tested for speed vs. the boost one.
+ */
+ class RecursiveMutex : boost::noncopyable {
+ public:
+ RecursiveMutex(const char* name) : m(name) { }
+ bool isLocked() const { return n.get() > 0; }
+ class scoped_lock : boost::noncopyable {
+ RecursiveMutex& rm;
+ int& nLocksByMe;
+ public:
+ scoped_lock( RecursiveMutex &m ) : rm(m), nLocksByMe(rm.n.getRef()) {
+ if( nLocksByMe++ == 0 )
+ rm.m.lock();
+ }
+ ~scoped_lock() {
+ assert( nLocksByMe > 0 );
+ if( --nLocksByMe == 0 ) {
+ rm.m.unlock();
+ }
+ }
+ };
+ private:
+ SimpleMutex m;
+ ThreadLocalValue<int> n;
+ };
+
+}
diff --git a/src/mongo/util/concurrency/mutexdebugger.h b/src/mongo/util/concurrency/mutexdebugger.h
new file mode 100644
index 00000000000..7dc57f29e98
--- /dev/null
+++ b/src/mongo/util/concurrency/mutexdebugger.h
@@ -0,0 +1,117 @@
+#pragma once
+
+namespace mongo {
+
+ /** only used on _DEBUG builds.
+ MutexDebugger checks that we always acquire locks for multiple mutexes in a consistant (acyclic) order.
+ If we were inconsistent we could deadlock.
+ */
+ class MutexDebugger {
+ typedef const char * mid; // mid = mutex ID
+ typedef map<mid,int> Preceeding;
+ map< mid, int > maxNest;
+ boost::thread_specific_ptr< Preceeding > us;
+ map< mid, set<mid> > followers;
+ boost::mutex &x;
+ unsigned magic;
+ void aBreakPoint() { } // for debugging
+ public:
+ // set these to create an assert that
+ // b must never be locked before a
+ // so
+ // a.lock(); b.lock(); is fine
+ // b.lock(); alone is fine too
+ // only checked on _DEBUG builds.
+ string a,b;
+
+ /** outputs some diagnostic info on mutexes (on _DEBUG builds) */
+ void programEnding();
+
+ MutexDebugger();
+
+ string currentlyLocked() const {
+ Preceeding *_preceeding = us.get();
+ if( _preceeding == 0 )
+ return "";
+ Preceeding &preceeding = *_preceeding;
+ stringstream q;
+ for( Preceeding::const_iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
+ if( i->second > 0 )
+ q << " " << i->first << ' ' << i->second << '\n';
+ }
+ return q.str();
+ }
+
+ void entering(mid m) {
+ if( this == 0 || m == 0 ) return;
+ assert( magic == 0x12345678 );
+
+ Preceeding *_preceeding = us.get();
+ if( _preceeding == 0 )
+ us.reset( _preceeding = new Preceeding() );
+ Preceeding &preceeding = *_preceeding;
+
+ if( a == m ) {
+ aBreakPoint();
+ if( preceeding[b.c_str()] ) {
+ cout << "****** MutexDebugger error! warning " << b << " was locked before " << a << endl;
+ assert(false);
+ }
+ }
+
+ preceeding[m]++;
+ if( preceeding[m] > 1 ) {
+ // recursive re-locking.
+ if( preceeding[m] > maxNest[m] )
+ maxNest[m] = preceeding[m];
+ return;
+ }
+
+ bool failed = false;
+ string err;
+ {
+ boost::mutex::scoped_lock lk(x);
+ followers[m];
+ for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
+ if( m != i->first && i->second > 0 ) {
+ followers[i->first].insert(m);
+ if( followers[m].count(i->first) != 0 ) {
+ failed = true;
+ stringstream ss;
+ mid bad = i->first;
+ ss << "mutex problem" <<
+ "\n when locking " << m <<
+ "\n " << bad << " was already locked and should not be."
+ "\n set a and b above to debug.\n";
+ stringstream q;
+ for( Preceeding::iterator i = preceeding.begin(); i != preceeding.end(); i++ ) {
+ if( i->first != m && i->first != bad && i->second > 0 )
+ q << " " << i->first << '\n';
+ }
+ string also = q.str();
+ if( !also.empty() )
+ ss << "also locked before " << m << " in this thread (no particular order):\n" << also;
+ err = ss.str();
+ break;
+ }
+ }
+ }
+ }
+ if( failed ) {
+ cout << err << endl;
+ assert( 0 );
+ }
+ }
+ void leaving(mid m) {
+ if( this == 0 || m == 0 ) return; // still in startup pre-main()
+ Preceeding& preceeding = *us.get();
+ preceeding[m]--;
+ if( preceeding[m] < 0 ) {
+ cout << "ERROR: lock count for " << m << " is " << preceeding[m] << endl;
+ assert( preceeding[m] >= 0 );
+ }
+ }
+ };
+ extern MutexDebugger &mutexDebugger;
+
+}
diff --git a/src/mongo/util/concurrency/mvar.h b/src/mongo/util/concurrency/mvar.h
new file mode 100644
index 00000000000..bc1855a85cc
--- /dev/null
+++ b/src/mongo/util/concurrency/mvar.h
@@ -0,0 +1,118 @@
+// mvar.h
+
+/* 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
+
+namespace mongo {
+
+ /* This is based on haskell's MVar synchronization primitive:
+ * http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent-MVar.html
+ *
+ * It is a thread-safe queue that can hold at most one object.
+ * You can also think of it as a box that can be either full or empty.
+ */
+
+ template <typename T>
+ class MVar {
+ public:
+ enum State {EMPTY=0, FULL};
+
+ // create an empty MVar
+ MVar()
+ : _state(EMPTY)
+ {}
+
+ // creates a full MVar
+ MVar(const T& val)
+ : _state(FULL)
+ , _value(val)
+ {}
+
+ // puts val into the MVar and returns true or returns false if full
+ // never blocks
+ bool tryPut(const T& val) {
+ // intentionally repeat test before and after lock
+ if (_state == FULL) return false;
+ Mutex::scoped_lock lock(_mutex);
+ if (_state == FULL) return false;
+
+ _state = FULL;
+ _value = val;
+
+ // unblock threads waiting to 'take'
+ _condition.notify_all();
+
+ return true;
+ }
+
+ // puts val into the MVar
+ // will block if the MVar is already full
+ void put(const T& val) {
+ Mutex::scoped_lock lock(_mutex);
+ while (!tryPut(val)) {
+ // unlocks lock while waiting and relocks before returning
+ _condition.wait(lock);
+ }
+ }
+
+ // takes val out of the MVar and returns true or returns false if empty
+ // never blocks
+ bool tryTake(T& out) {
+ // intentionally repeat test before and after lock
+ if (_state == EMPTY) return false;
+ Mutex::scoped_lock lock(_mutex);
+ if (_state == EMPTY) return false;
+
+ _state = EMPTY;
+ out = _value;
+
+ // unblock threads waiting to 'put'
+ _condition.notify_all();
+
+ return true;
+ }
+
+ // takes val out of the MVar
+ // will block if the MVar is empty
+ T take() {
+ T ret = T();
+
+ Mutex::scoped_lock lock(_mutex);
+ while (!tryTake(ret)) {
+ // unlocks lock while waiting and relocks before returning
+ _condition.wait(lock);
+ }
+
+ return ret;
+ }
+
+
+ // Note: this is fast because there is no locking, but state could
+ // change before you get a chance to act on it.
+ // Mainly useful for sanity checks / asserts.
+ State getState() { return _state; }
+
+
+ private:
+ State _state;
+ T _value;
+ typedef boost::recursive_mutex Mutex;
+ Mutex _mutex;
+ boost::condition _condition;
+ };
+
+}
diff --git a/src/mongo/util/concurrency/race.h b/src/mongo/util/concurrency/race.h
new file mode 100644
index 00000000000..837ae23ac13
--- /dev/null
+++ b/src/mongo/util/concurrency/race.h
@@ -0,0 +1,77 @@
+#pragma once
+
+#include "../goodies.h" // printStackTrace
+#include "mutexdebugger.h"
+
+namespace mongo {
+
+ namespace race {
+
+#ifdef _WIN32
+ typedef unsigned threadId_t;
+#else
+ typedef pthread_t threadId_t;
+#endif
+
+#if defined(_DEBUG)
+
+ class Block {
+ volatile int n;
+ unsigned ncalls;
+ const string file;
+ const unsigned line;
+ void fail() {
+ log() << "\n\n\nrace: synchronization (race condition) failure\ncurrent locks this thread (" << getThreadName() << "):\n"
+ << mutexDebugger.currentlyLocked() << endl;
+ printStackTrace();
+ ::abort();
+ }
+ void enter() {
+ if( ++n != 1 ) fail();
+ ncalls++;
+ if( ncalls < 100 ) {
+ sleepmillis(0);
+ }
+ else {
+ RARELY {
+ sleepmillis(0);
+ if( ncalls < 128 * 20 ) {
+ OCCASIONALLY {
+ sleepmillis(3);
+ }
+ }
+ }
+ }
+ }
+ void leave() {
+ if( --n != 0 ) fail();
+ }
+ public:
+ Block(string f, unsigned l) : n(0), ncalls(0), file(f), line(l) { }
+ ~Block() {
+ if( ncalls > 1000000 ) {
+ // just so we know if we are slowing things down
+ log() << "race::Block lots of calls " << file << ' ' << line << " n:" << ncalls << endl;
+ }
+ }
+ class Within {
+ Block& _s;
+ public:
+ Within(Block& s) : _s(s) { _s.enter(); }
+ ~Within() { _s.leave(); }
+ };
+ };
+
+ /* in a rwlock situation this will fail, so not appropriate for things like that. */
+# define RACECHECK \
+ static race::Block __cp(__FILE__, __LINE__); \
+ race::Block::Within __ck(__cp);
+
+#else
+ /* !_DEBUG */
+# define RACECHECK
+
+#endif
+
+ }
+}
diff --git a/src/mongo/util/concurrency/rwlock.h b/src/mongo/util/concurrency/rwlock.h
new file mode 100644
index 00000000000..3dbfc35ed6e
--- /dev/null
+++ b/src/mongo/util/concurrency/rwlock.h
@@ -0,0 +1,271 @@
+// @file rwlock.h generic reader-writer lock (cross platform support)
+
+/*
+ * Copyright (C) 2010 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "mutex.h"
+#include "../time_support.h"
+#include "rwlockimpl.h"
+
+#if defined(_DEBUG)
+#include "mutexdebugger.h"
+#endif
+
+namespace mongo {
+
+ /** separated out as later the implementation of this may be different than RWLock,
+ depending on OS, as there is no upgrade etc. facility herein.
+ */
+ class SimpleRWLock : public RWLockBase {
+ public:
+ void lock() { RWLockBase::lock(); }
+ void unlock() { RWLockBase::unlock(); }
+ void lock_shared() { RWLockBase::lock_shared(); }
+ void unlock_shared() { RWLockBase::unlock_shared(); }
+ class Shared : boost::noncopyable {
+ SimpleRWLock& _r;
+ public:
+ Shared(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock_shared(); }
+ ~Shared() { _r.unlock_shared(); }
+ };
+ class Exclusive : boost::noncopyable {
+ SimpleRWLock& _r;
+ public:
+ Exclusive(SimpleRWLock& rwlock) : _r(rwlock) {_r.lock(); }
+ ~Exclusive() { _r.unlock(); }
+ };
+ };
+
+ class RWLock : public RWLockBase {
+ enum { NilState, UpgradableState, Exclusive } x; // only bother to set when doing upgradable related things
+ public:
+ const char * const _name;
+ RWLock(const char *name) : _name(name) {
+ x = NilState;
+ }
+ void lock() {
+ RWLockBase::lock();
+#if defined(_DEBUG)
+ mutexDebugger.entering(_name);
+#endif
+ }
+ void unlock() {
+#if defined(_DEBUG)
+ mutexDebugger.leaving(_name);
+#endif
+ RWLockBase::unlock();
+ }
+
+ void lock_shared() { RWLockBase::lock_shared(); }
+ void unlock_shared() { RWLockBase::unlock_shared(); }
+ private:
+ void lockAsUpgradable() { RWLockBase::lockAsUpgradable(); }
+ void unlockFromUpgradable() { // upgradable -> unlocked
+ RWLockBase::unlockFromUpgradable();
+ }
+ public:
+ void upgrade() { // upgradable -> exclusive lock
+ assert( x == UpgradableState );
+ RWLockBase::upgrade();
+ x = Exclusive;
+ }
+
+ bool lock_shared_try( int millis ) { return RWLockBase::lock_shared_try(millis); }
+
+ bool lock_try( int millis = 0 ) {
+ if( RWLockBase::lock_try(millis) ) {
+#if defined(_DEBUG)
+ mutexDebugger.entering(_name);
+#endif
+ return true;
+ }
+ return false;
+ }
+
+ /** acquire upgradable state. You must be unlocked before creating.
+ unlocks on destruction, whether in upgradable state or upgraded to exclusive
+ in the interim.
+ */
+ class Upgradable : boost::noncopyable {
+ RWLock& _r;
+ public:
+ Upgradable(RWLock& r) : _r(r) {
+ r.lockAsUpgradable();
+ assert( _r.x == NilState );
+ _r.x = RWLock::UpgradableState;
+ }
+ ~Upgradable() {
+ if( _r.x == RWLock::UpgradableState ) {
+ _r.x = NilState;
+ _r.unlockFromUpgradable();
+ }
+ else {
+ //TEMP assert( _r.x == Exclusive ); // has been upgraded
+ _r.x = NilState;
+ _r.unlock();
+ }
+ }
+ };
+ };
+
+ /** throws on failure to acquire in the specified time period. */
+ class rwlock_try_write : boost::noncopyable {
+ public:
+ struct exception { };
+ rwlock_try_write(RWLock& l, int millis = 0) : _l(l) {
+ if( !l.lock_try(millis) )
+ throw exception();
+ }
+ ~rwlock_try_write() { _l.unlock(); }
+ private:
+ RWLock& _l;
+ };
+
+ class rwlock_shared : boost::noncopyable {
+ public:
+ rwlock_shared(RWLock& rwlock) : _r(rwlock) {_r.lock_shared(); }
+ ~rwlock_shared() { _r.unlock_shared(); }
+ private:
+ RWLock& _r;
+ };
+
+ /* scoped lock for RWLock */
+ class rwlock : boost::noncopyable {
+ public:
+ /**
+ * @param write acquire write lock if true sharable if false
+ * @param lowPriority if > 0, will try to get the lock non-greedily for that many ms
+ */
+ rwlock( const RWLock& lock , bool write, /* bool alreadyHaveLock = false , */int lowPriorityWaitMS = 0 )
+ : _lock( (RWLock&)lock ) , _write( write ) {
+ {
+ if ( _write ) {
+ _lock.lock();
+ }
+ else {
+ _lock.lock_shared();
+ }
+ }
+ }
+ ~rwlock() {
+ if ( _write )
+ _lock.unlock();
+ else
+ _lock.unlock_shared();
+ }
+ private:
+ RWLock& _lock;
+ const bool _write;
+ };
+
+ // ----------------------------------------------------------------------------------------
+
+ /** recursive on shared locks is ok for this implementation */
+ class RWLockRecursive : protected RWLockBase {
+ protected:
+ ThreadLocalValue<int> _state;
+ void lock(); // not implemented - Lock() should be used; didn't overload this name to avoid mistakes
+ virtual void Lock() { RWLockBase::lock(); }
+ public:
+ virtual ~RWLockRecursive() { }
+ const char * const _name;
+ RWLockRecursive(const char *name) : _name(name) { }
+
+ void assertExclusivelyLocked() {
+ assert( _state.get() < 0 );
+ }
+
+ class Exclusive : boost::noncopyable {
+ RWLockRecursive& _r;
+ public:
+ Exclusive(RWLockRecursive& r) : _r(r) {
+ int s = _r._state.get();
+ dassert( s <= 0 );
+ if( s == 0 )
+ _r.Lock();
+ _r._state.set(s-1);
+ }
+ ~Exclusive() {
+ int s = _r._state.get();
+ DEV wassert( s < 0 ); // wassert: don't throw from destructors
+ ++s;
+ _r._state.set(s);
+ if ( s == 0 )
+ _r.unlock();
+ }
+ };
+
+ class Shared : boost::noncopyable {
+ RWLockRecursive& _r;
+ bool _alreadyLockedExclusiveByUs;
+ public:
+ Shared(RWLockRecursive& r) : _r(r) {
+ int s = _r._state.get();
+ _alreadyLockedExclusiveByUs = s < 0;
+ if( !_alreadyLockedExclusiveByUs ) {
+ dassert( s >= 0 ); // -1 would mean exclusive
+ if( s == 0 )
+ _r.lock_shared();
+ _r._state.set(s+1);
+ }
+ }
+ ~Shared() {
+ if( _alreadyLockedExclusiveByUs ) {
+ DEV wassert( _r._state.get() < 0 );
+ }
+ else {
+ int s = _r._state.get() - 1;
+ DEV wassert( s >= 0 );
+ _r._state.set(s);
+ if( s == 0 )
+ _r.unlock_shared();
+ }
+ }
+ };
+ };
+
+ class RWLockRecursiveNongreedy : public RWLockRecursive {
+ virtual void Lock() {
+ bool got = false;
+ for ( int i=0; i<lowPriorityWaitMS; i++ ) {
+ if ( lock_try(0) ) {
+ got = true;
+ break;
+ }
+ int sleep = 1;
+ if ( i > ( lowPriorityWaitMS / 20 ) )
+ sleep = 10;
+ sleepmillis(sleep);
+ i += ( sleep - 1 );
+ }
+ if ( ! got ) {
+ log() << "couldn't lazily get rwlock" << endl;
+ RWLockBase::lock();
+ }
+ }
+
+ public:
+ const int lowPriorityWaitMS;
+ RWLockRecursiveNongreedy(const char *nm, int lpwaitms) : RWLockRecursive(nm), lowPriorityWaitMS(lpwaitms) { }
+ const char * implType() const { return RWLockRecursive::implType(); }
+
+ //just for testing:
+ bool __lock_try( int millis ) { return RWLockRecursive::lock_try(millis); }
+ };
+
+}
diff --git a/src/mongo/util/concurrency/rwlockimpl.h b/src/mongo/util/concurrency/rwlockimpl.h
new file mode 100644
index 00000000000..4e07231447b
--- /dev/null
+++ b/src/mongo/util/concurrency/rwlockimpl.h
@@ -0,0 +1,170 @@
+// @file rwlockimpl.h
+
+#pragma once
+
+#if defined(MONGO_USE_SRW_ON_WINDOWS) && defined(_WIN32)
+
+// windows slimreaderwriter version. newer windows versions only
+
+namespace mongo {
+ class RWLockBase : boost::noncopyable {
+ SRWLOCK _lock;
+ protected:
+ RWLockBase() { InitializeSRWLock(&_lock); }
+ ~RWLockBase() {
+ // no special action needed to destroy a SRWLOCK
+ }
+ void lock() { AcquireSRWLockExclusive(&_lock); }
+ void unlock() { ReleaseSRWLockExclusive(&_lock); }
+ void lock_shared() { AcquireSRWLockShared(&_lock); }
+ void unlock_shared() { ReleaseSRWLockShared(&_lock); }
+ bool lock_shared_try( int millis ) {
+ if( TryAcquireSRWLockShared(&_lock) )
+ return true;
+ if( millis == 0 )
+ return false;
+ unsigned long long end = curTimeMicros64() + millis*1000;
+ while( 1 ) {
+ Sleep(1);
+ if( TryAcquireSRWLockShared(&_lock) )
+ return true;
+ if( curTimeMicros64() >= end )
+ break;
+ }
+ return false;
+ }
+ bool lock_try( int millis = 0 ) {
+ if( TryAcquireSRWLockExclusive(&_lock) ) // quick check to optimistically avoid calling curTimeMicros64
+ return true;
+ if( millis == 0 )
+ return false;
+ unsigned long long end = curTimeMicros64() + millis*1000;
+ do {
+ Sleep(1);
+ if( TryAcquireSRWLockExclusive(&_lock) )
+ return true;
+ } while( curTimeMicros64() < end );
+ return false;
+ }
+ // no upgradable for this impl
+ void lockAsUpgradable() { lock(); }
+ void unlockFromUpgradable() { unlock(); }
+ void upgrade() { }
+ public:
+ const char * implType() const { return "WINSRW"; }
+ };
+}
+
+#elif( BOOST_VERSION < 103500 )
+
+# if defined(_WIN32)
+# error need boost >= 1.35 for windows
+# endif
+
+// pthreads version
+
+# include <pthread.h>
+
+namespace mongo {
+ class RWLockBase : boost::noncopyable {
+ pthread_rwlock_t _lock;
+ static void check( int x ) {
+ if( x == 0 ) return;
+ log() << "pthread rwlock failed: " << x << endl;
+ assert( x == 0 );
+ }
+
+ ~RWLockBase() {
+ if ( ! StaticObserver::_destroyingStatics ) {
+ wassert( pthread_rwlock_destroy( &_lock ) == 0 ); // wassert as don't want to throw from a destructor
+ }
+ }
+
+ protected:
+ RWLockBase() {
+ check( pthread_rwlock_init( &_lock , 0 ) );
+ }
+
+ void lock() { check( pthread_rwlock_wrlock( &_lock ) ); }
+ void unlock() { check( pthread_rwlock_unlock( &_lock ) ); }
+ void lock_shared() { check( pthread_rwlock_rdlock( &_lock ) ); }
+ void unlock_shared() { check( pthread_rwlock_unlock( &_lock ) ); }
+ bool lock_shared_try( int millis ) { return _try( millis , false ); }
+ bool lock_try( int millis = 0 ) { return _try( millis , true ); }
+ bool _try( int millis , bool write ) {
+ while ( true ) {
+ int x = write ?
+ pthread_rwlock_trywrlock( &_lock ) :
+ pthread_rwlock_tryrdlock( &_lock );
+ if ( x <= 0 )
+ return true;
+ if ( millis-- <= 0 )
+ return false;
+ if ( x == EBUSY ) {
+ sleepmillis(1);
+ continue;
+ }
+ check(x);
+ }
+ return false;
+ }
+ // no upgradable for this impl
+ void lockAsUpgradable() { lock(); }
+ void unlockFromUpgradable() { unlock(); }
+ void upgrade() { }
+ public:
+ const char * implType() const { return "posix"; }
+ };
+}
+
+#else
+
+// Boost version
+
+# if defined(_WIN32)
+# include "shared_mutex_win.hpp"
+namespace mongo { typedef boost::modified_shared_mutex shared_mutex; }
+# else
+# include <boost/thread/shared_mutex.hpp>
+namespace mongo { using boost::shared_mutex; }
+# endif
+# undef assert
+# define assert MONGO_assert
+
+namespace mongo {
+ class RWLockBase : boost::noncopyable {
+ shared_mutex _m;
+ protected:
+ void lock() {
+ _m.lock();
+ }
+ void unlock() {
+ _m.unlock();
+ }
+ void lockAsUpgradable() {
+ _m.lock_upgrade();
+ }
+ void unlockFromUpgradable() { // upgradable -> unlocked
+ _m.unlock_upgrade();
+ }
+ void upgrade() { // upgradable -> exclusive lock
+ _m.unlock_upgrade_and_lock();
+ }
+ void lock_shared() {
+ _m.lock_shared();
+ }
+ void unlock_shared() {
+ _m.unlock_shared();
+ }
+ bool lock_shared_try( int millis ) {
+ return _m.timed_lock_shared( boost::posix_time::milliseconds(millis) );
+ }
+ bool lock_try( int millis = 0 ) {
+ return _m.timed_lock( boost::posix_time::milliseconds(millis) );
+ }
+ public:
+ const char * implType() const { return "boost"; }
+ };
+}
+
+#endif
diff --git a/src/mongo/util/concurrency/shared_mutex_win.hpp b/src/mongo/util/concurrency/shared_mutex_win.hpp
new file mode 100644
index 00000000000..e850fc6bab4
--- /dev/null
+++ b/src/mongo/util/concurrency/shared_mutex_win.hpp
@@ -0,0 +1,594 @@
+#ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+#define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+
+// (C) Copyright 2006-8 Anthony Williams
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/* MongoDB :
+ Slightly modified boost file to not die above 127 pending writes
+ Here is what changed (from boost 1.42.0 shared_mutex.hpp):
+ 1,2c1,2
+ < #ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP
+ < #define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP
+ ---
+ > #ifndef BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+ > #define BOOST_THREAD_WIN32_SHARED_MUTEX_HPP_MODIFIED
+ 22c27
+ < class shared_mutex:
+ ---
+ > class modified_shared_mutex:
+ 73c78
+ < shared_mutex():
+ ---
+ > modified_shared_mutex():
+ 84c89
+ < ~shared_mutex()
+ ---
+ > ~modified_shared_mutex()
+ 283a289,290
+ > if( new_state.exclusive_waiting == 127 ) // the maximum already!
+ > break;
+*/
+
+#include <boost/assert.hpp>
+#include <boost/detail/interlocked.hpp>
+#include <boost/thread/win32/thread_primitives.hpp>
+#include <boost/static_assert.hpp>
+#include <limits.h>
+#include <boost/utility.hpp>
+#include <boost/thread/thread_time.hpp>
+
+#include <boost/config/abi_prefix.hpp>
+
+namespace boost
+{
+ class modified_shared_mutex:
+ private boost::noncopyable
+ {
+ private:
+ struct state_data
+ {
+ unsigned shared_count:11,
+ shared_waiting:11,
+ exclusive:1,
+ upgrade:1,
+ exclusive_waiting:7,
+ exclusive_waiting_blocked:1;
+
+ friend bool operator==(state_data const& lhs,state_data const& rhs)
+ {
+ return *reinterpret_cast<unsigned const*>(&lhs)==*reinterpret_cast<unsigned const*>(&rhs);
+ }
+ };
+
+
+ template<typename T>
+ T interlocked_compare_exchange(T* target,T new_value,T comparand)
+ {
+ BOOST_STATIC_ASSERT(sizeof(T)==sizeof(long));
+ long const res=BOOST_INTERLOCKED_COMPARE_EXCHANGE(reinterpret_cast<long*>(target),
+ *reinterpret_cast<long*>(&new_value),
+ *reinterpret_cast<long*>(&comparand));
+ return *reinterpret_cast<T const*>(&res);
+ }
+
+ state_data state;
+ detail::win32::handle semaphores[2];
+ detail::win32::handle &unlock_sem;
+ detail::win32::handle &exclusive_sem;
+ detail::win32::handle upgrade_sem;
+
+ void release_waiters(state_data old_state)
+ {
+ if(old_state.exclusive_waiting)
+ {
+ BOOST_VERIFY(detail::win32::ReleaseSemaphore(exclusive_sem,1,0)!=0);
+ }
+
+ if(old_state.shared_waiting || old_state.exclusive_waiting)
+ {
+ BOOST_VERIFY(detail::win32::ReleaseSemaphore(unlock_sem,old_state.shared_waiting + (old_state.exclusive_waiting?1:0),0)!=0);
+ }
+ }
+
+
+ public:
+ modified_shared_mutex():
+ unlock_sem(semaphores[0]),
+ exclusive_sem(semaphores[1])
+ {
+ unlock_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX);
+ exclusive_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX);
+ upgrade_sem=detail::win32::create_anonymous_semaphore(0,LONG_MAX);
+ state_data state_={0};
+ state=state_;
+ }
+
+ ~modified_shared_mutex()
+ {
+ detail::win32::CloseHandle(upgrade_sem);
+ detail::win32::CloseHandle(unlock_sem);
+ detail::win32::CloseHandle(exclusive_sem);
+ }
+
+ bool try_lock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(!new_state.exclusive && !new_state.exclusive_waiting_blocked)
+ {
+ ++new_state.shared_count;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ return !(old_state.exclusive| old_state.exclusive_waiting_blocked);
+ }
+
+ void lock_shared()
+ {
+ BOOST_VERIFY(timed_lock_shared(::boost::detail::get_system_time_sentinel()));
+ }
+
+ template<typename TimeDuration>
+ bool timed_lock_shared(TimeDuration const & relative_time)
+ {
+ return timed_lock_shared(get_system_time()+relative_time);
+ }
+
+ bool timed_lock_shared(boost::system_time const& wait_until)
+ {
+ for(;;)
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked)
+ {
+ ++new_state.shared_waiting;
+ }
+ else
+ {
+ ++new_state.shared_count;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!(old_state.exclusive| old_state.exclusive_waiting_blocked))
+ {
+ return true;
+ }
+
+ unsigned long const res=detail::win32::WaitForSingleObject(unlock_sem,::boost::detail::get_milliseconds_until(wait_until));
+ if(res==detail::win32::timeout)
+ {
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked)
+ {
+ if(new_state.shared_waiting)
+ {
+ --new_state.shared_waiting;
+ }
+ }
+ else
+ {
+ ++new_state.shared_count;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!(old_state.exclusive| old_state.exclusive_waiting_blocked))
+ {
+ return true;
+ }
+ return false;
+ }
+
+ BOOST_ASSERT(res==0);
+ }
+ }
+
+ void unlock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ bool const last_reader=!--new_state.shared_count;
+
+ if(last_reader)
+ {
+ if(new_state.upgrade)
+ {
+ new_state.upgrade=false;
+ new_state.exclusive=true;
+ }
+ else
+ {
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+ }
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ if(last_reader)
+ {
+ if(old_state.upgrade)
+ {
+ BOOST_VERIFY(detail::win32::ReleaseSemaphore(upgrade_sem,1,0)!=0);
+ }
+ else
+ {
+ release_waiters(old_state);
+ }
+ }
+ break;
+ }
+ old_state=current_state;
+ }
+ }
+
+ void lock()
+ {
+ BOOST_VERIFY(timed_lock(::boost::detail::get_system_time_sentinel()));
+ }
+
+ template<typename TimeDuration>
+ bool timed_lock(TimeDuration const & relative_time)
+ {
+ return timed_lock(get_system_time()+relative_time);
+ }
+
+ bool try_lock()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.shared_count || new_state.exclusive)
+ {
+ return false;
+ }
+ else
+ {
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ return true;
+ }
+
+
+ bool timed_lock(boost::system_time const& wait_until)
+ {
+ for(;;)
+ {
+ state_data old_state=state;
+
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.shared_count || new_state.exclusive)
+ {
+ if( new_state.exclusive_waiting == 127 ) // the maximum already!
+ break;
+ ++new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=true;
+ }
+ else
+ {
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!old_state.shared_count && !old_state.exclusive)
+ {
+ return true;
+ }
+ unsigned long const wait_res=detail::win32::WaitForMultipleObjects(2,semaphores,true,::boost::detail::get_milliseconds_until(wait_until));
+ if(wait_res==detail::win32::timeout)
+ {
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.shared_count || new_state.exclusive)
+ {
+ if(new_state.exclusive_waiting)
+ {
+ if(!--new_state.exclusive_waiting)
+ {
+ new_state.exclusive_waiting_blocked=false;
+ }
+ }
+ }
+ else
+ {
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ if(!old_state.shared_count && !old_state.exclusive)
+ {
+ return true;
+ }
+ return false;
+ }
+ BOOST_ASSERT(wait_res<2);
+ }
+ }
+
+ void unlock()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.exclusive=false;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ void lock_upgrade()
+ {
+ for(;;)
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked || new_state.upgrade)
+ {
+ ++new_state.shared_waiting;
+ }
+ else
+ {
+ ++new_state.shared_count;
+ new_state.upgrade=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+
+ if(!(old_state.exclusive|| old_state.exclusive_waiting_blocked|| old_state.upgrade))
+ {
+ return;
+ }
+
+ BOOST_VERIFY(!detail::win32::WaitForSingleObject(unlock_sem,detail::win32::infinite));
+ }
+ }
+
+ bool try_lock_upgrade()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ if(new_state.exclusive || new_state.exclusive_waiting_blocked || new_state.upgrade)
+ {
+ return false;
+ }
+ else
+ {
+ ++new_state.shared_count;
+ new_state.upgrade=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ return true;
+ }
+
+ void unlock_upgrade()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.upgrade=false;
+ bool const last_reader=!--new_state.shared_count;
+
+ if(last_reader)
+ {
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ if(last_reader)
+ {
+ release_waiters(old_state);
+ }
+ break;
+ }
+ old_state=current_state;
+ }
+ }
+
+ void unlock_upgrade_and_lock()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ bool const last_reader=!--new_state.shared_count;
+
+ if(last_reader)
+ {
+ new_state.upgrade=false;
+ new_state.exclusive=true;
+ }
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ if(!last_reader)
+ {
+ BOOST_VERIFY(!detail::win32::WaitForSingleObject(upgrade_sem,detail::win32::infinite));
+ }
+ break;
+ }
+ old_state=current_state;
+ }
+ }
+
+ void unlock_and_lock_upgrade()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.exclusive=false;
+ new_state.upgrade=true;
+ ++new_state.shared_count;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ void unlock_and_lock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.exclusive=false;
+ ++new_state.shared_count;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ void unlock_upgrade_and_lock_shared()
+ {
+ state_data old_state=state;
+ for(;;)
+ {
+ state_data new_state=old_state;
+ new_state.upgrade=false;
+ if(new_state.exclusive_waiting)
+ {
+ --new_state.exclusive_waiting;
+ new_state.exclusive_waiting_blocked=false;
+ }
+ new_state.shared_waiting=0;
+
+ state_data const current_state=interlocked_compare_exchange(&state,new_state,old_state);
+ if(current_state==old_state)
+ {
+ break;
+ }
+ old_state=current_state;
+ }
+ release_waiters(old_state);
+ }
+
+ };
+}
+
+#include <boost/config/abi_suffix.hpp>
+
+#endif
diff --git a/src/mongo/util/concurrency/spin_lock.cpp b/src/mongo/util/concurrency/spin_lock.cpp
new file mode 100644
index 00000000000..cbf517b2746
--- /dev/null
+++ b/src/mongo/util/concurrency/spin_lock.cpp
@@ -0,0 +1,107 @@
+// spin_lock.cpp
+
+/**
+* Copyright (C) 2010 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h" // todo eliminate this include
+#include <time.h>
+#include "spin_lock.h"
+
+namespace mongo {
+
+ SpinLock::~SpinLock() {
+#if defined(_WIN32)
+ DeleteCriticalSection(&_cs);
+#elif defined(__USE_XOPEN2K)
+ pthread_spin_destroy(&_lock);
+#endif
+ }
+
+ SpinLock::SpinLock()
+#if defined(_WIN32)
+ { InitializeCriticalSectionAndSpinCount(&_cs, 4000); }
+#elif defined(__USE_XOPEN2K)
+ { pthread_spin_init( &_lock , 0 ); }
+#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+ : _locked( false ) { }
+#else
+ : _mutex( "SpinLock" ) { }
+#endif
+
+#if defined(__USE_XOPEN2K)
+ NOINLINE_DECL void SpinLock::_lk() {
+ /**
+ * this is designed to perform close to the default spin lock
+ * the reason for the mild insanity is to prevent horrible performance
+ * when contention spikes
+ * it allows spinlocks to be used in many more places
+ * which is good because even with this change they are about 8x faster on linux
+ */
+
+ for ( int i=0; i<1000; i++ ) {
+ if ( pthread_spin_trylock( &_lock ) == 0 )
+ return;
+ asm volatile ( "pause" ) ; // maybe trylock does this; just in case.
+ }
+
+ for ( int i=0; i<1000; i++ ) {
+ if ( pthread_spin_trylock( &_lock ) == 0 )
+ return;
+ pthread_yield();
+ }
+
+ struct timespec t;
+ t.tv_sec = 0;
+ t.tv_nsec = 5000000;
+
+ while ( pthread_spin_trylock( &_lock ) != 0 ) {
+ nanosleep(&t, NULL);
+ }
+ }
+#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+ void SpinLock::lock() {
+
+ // fast path
+ if (!_locked && !__sync_lock_test_and_set(&_locked, true)) {
+ return;
+ }
+
+ // wait for lock
+ int wait = 1000;
+ while ((wait-- > 0) && (_locked)) {
+ asm volatile ( "pause" ) ;
+ }
+
+ // if failed to grab lock, sleep
+ struct timespec t;
+ t.tv_sec = 0;
+ t.tv_nsec = 5000000;
+ while (__sync_lock_test_and_set(&_locked, true)) {
+ nanosleep(&t, NULL);
+ }
+ }
+#endif
+
+ bool SpinLock::isfast() {
+#if defined(_WIN32) || defined(__USE_XOPEN2K) || defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+ return true;
+#else
+ return false;
+#endif
+ }
+
+
+} // namespace mongo
diff --git a/src/mongo/util/concurrency/spin_lock.h b/src/mongo/util/concurrency/spin_lock.h
new file mode 100644
index 00000000000..d90de51afac
--- /dev/null
+++ b/src/mongo/util/concurrency/spin_lock.h
@@ -0,0 +1,77 @@
+// spin_lock.h
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "mutex.h"
+
+namespace mongo {
+
+ /**
+ * The spinlock currently requires late GCC support routines to be efficient.
+ * Other platforms default to a mutex implemenation.
+ */
+ class SpinLock : boost::noncopyable {
+ public:
+ SpinLock();
+ ~SpinLock();
+
+ static bool isfast(); // true if a real spinlock on this platform
+
+ private:
+#if defined(_WIN32)
+ CRITICAL_SECTION _cs;
+ public:
+ void lock() {EnterCriticalSection(&_cs); }
+ void unlock() { LeaveCriticalSection(&_cs); }
+#elif defined(__USE_XOPEN2K)
+ pthread_spinlock_t _lock;
+ void _lk();
+ public:
+ void unlock() { pthread_spin_unlock(&_lock); }
+ void lock() {
+ if ( pthread_spin_trylock( &_lock ) == 0 )
+ return;
+ _lk();
+ }
+#elif defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)
+ volatile bool _locked;
+ public:
+ void unlock() {__sync_lock_release(&_locked); }
+ void lock();
+#else
+ // default to a mutex if not implemented
+ SimpleMutex _mutex;
+ public:
+ void unlock() { _mutex.unlock(); }
+ void lock() { _mutex.lock(); }
+#endif
+ };
+
+ class scoped_spinlock : boost::noncopyable {
+ public:
+ scoped_spinlock( SpinLock& l ) : _l(l) {
+ _l.lock();
+ }
+ ~scoped_spinlock() {
+ _l.unlock();}
+ private:
+ SpinLock& _l;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/util/concurrency/synchronization.cpp b/src/mongo/util/concurrency/synchronization.cpp
new file mode 100644
index 00000000000..4186745dc16
--- /dev/null
+++ b/src/mongo/util/concurrency/synchronization.cpp
@@ -0,0 +1,81 @@
+// synchronization.cpp
+
+/* Copyright 2010 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.
+ */
+
+#include "pch.h"
+#include "synchronization.h"
+
+namespace mongo {
+
+ Notification::Notification() : _mutex ( "Notification" ){
+ lookFor = 1;
+ cur = 0;
+ }
+
+ Notification::~Notification() { }
+
+ void Notification::waitToBeNotified() {
+ scoped_lock lock( _mutex );
+ while ( lookFor != cur )
+ _condition.wait( lock.boost() );
+ lookFor++;
+ }
+
+ void Notification::notifyOne() {
+ scoped_lock lock( _mutex );
+ assert( cur != lookFor );
+ cur++;
+ _condition.notify_one();
+ }
+
+ /* --- NotifyAll --- */
+
+ NotifyAll::NotifyAll() : _mutex("NotifyAll") {
+ _lastDone = 0;
+ _lastReturned = 0;
+ _nWaiting = 0;
+ }
+
+ NotifyAll::When NotifyAll::now() {
+ scoped_lock lock( _mutex );
+ return ++_lastReturned;
+ }
+
+ void NotifyAll::waitFor(When e) {
+ scoped_lock lock( _mutex );
+ ++_nWaiting;
+ while( _lastDone < e ) {
+ _condition.wait( lock.boost() );
+ }
+ }
+
+ void NotifyAll::awaitBeyondNow() {
+ scoped_lock lock( _mutex );
+ ++_nWaiting;
+ When e = ++_lastReturned;
+ while( _lastDone <= e ) {
+ _condition.wait( lock.boost() );
+ }
+ }
+
+ void NotifyAll::notifyAll(When e) {
+ scoped_lock lock( _mutex );
+ _lastDone = e;
+ _nWaiting = 0;
+ _condition.notify_all();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/concurrency/synchronization.h b/src/mongo/util/concurrency/synchronization.h
new file mode 100644
index 00000000000..f9a40cc3ab9
--- /dev/null
+++ b/src/mongo/util/concurrency/synchronization.h
@@ -0,0 +1,86 @@
+// synchronization.h
+
+/* Copyright 2010 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 <boost/thread/condition.hpp>
+#include "mutex.h"
+
+namespace mongo {
+
+ /*
+ * A class to establish a synchronization point between two threads. One thread is the waiter and one is
+ * the notifier. After the notification event, both proceed normally.
+ *
+ * This class is thread-safe.
+ */
+ class Notification {
+ public:
+ Notification();
+ ~Notification();
+
+ /*
+ * Blocks until the method 'notifyOne()' is called.
+ */
+ void waitToBeNotified();
+
+ /*
+ * Notifies the waiter of '*this' that it can proceed. Can only be called once.
+ */
+ void notifyOne();
+
+ private:
+ mongo::mutex _mutex; // protects state below
+ unsigned long long lookFor;
+ unsigned long long cur;
+ boost::condition _condition; // cond over _notified being true
+ };
+
+ /** establishes a synchronization point between threads. N threads are waits and one is notifier.
+ threadsafe.
+ */
+ class NotifyAll : boost::noncopyable {
+ public:
+ NotifyAll();
+
+ typedef unsigned long long When;
+
+ When now();
+
+ /** awaits the next notifyAll() call by another thread. notifications that precede this
+ call are ignored -- we are looking for a fresh event.
+ */
+ void waitFor(When);
+
+ /** a bit faster than waitFor( now() ) */
+ void awaitBeyondNow();
+
+ /** may be called multiple times. notifies all waiters */
+ void notifyAll(When);
+
+ /** indicates how many threads are waiting for a notify. */
+ unsigned nWaiting() const { return _nWaiting; }
+
+ private:
+ mongo::mutex _mutex;
+ boost::condition _condition;
+ When _lastDone;
+ When _lastReturned;
+ unsigned _nWaiting;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/util/concurrency/task.cpp b/src/mongo/util/concurrency/task.cpp
new file mode 100644
index 00000000000..0b6ab166f19
--- /dev/null
+++ b/src/mongo/util/concurrency/task.cpp
@@ -0,0 +1,181 @@
+// @file task.cpp
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,b
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+
+#include <boost/thread/condition.hpp>
+
+#include "task.h"
+#include "../goodies.h"
+#include "../unittest.h"
+#include "../time_support.h"
+
+namespace mongo {
+
+ namespace task {
+
+ /*void foo() {
+ boost::mutex m;
+ boost::mutex::scoped_lock lk(m);
+ boost::condition cond;
+ cond.wait(lk);
+ cond.notify_one();
+ }*/
+
+ Task::Task()
+ : BackgroundJob( true /* deleteSelf */ ) {
+ n = 0;
+ repeat = 0;
+ }
+
+ void Task::halt() { repeat = 0; }
+
+ void Task::run() {
+ assert( n == 0 );
+ while( 1 ) {
+ n++;
+ try {
+ doWork();
+ }
+ catch(...) { }
+ if( repeat == 0 )
+ break;
+ sleepmillis(repeat);
+ if( inShutdown() )
+ break;
+ }
+ }
+
+ void Task::begin() {
+ go();
+ }
+
+ void fork(Task *t) {
+ t->begin();
+ }
+
+ void repeat(Task *t, unsigned millis) {
+ t->repeat = millis;
+ t->begin();
+ }
+
+ }
+}
+
+#include "msg.h"
+
+/* task::Server */
+
+namespace mongo {
+ namespace task {
+
+ /* to get back a return value */
+ struct Ret {
+ Ret() : done(false),m("Ret") { }
+ bool done;
+ mongo::mutex m;
+ boost::condition c;
+ const lam *msg;
+ void f() {
+ (*msg)();
+ done = true;
+ c.notify_one();
+ }
+ };
+
+ void Server::call( const lam& msg ) {
+ Ret r;
+ r.msg = &msg;
+ lam f = boost::bind(&Ret::f, &r);
+ send(f);
+ {
+ scoped_lock lk(r.m);
+ while( !r.done )
+ r.c.wait(lk.boost());
+ }
+ }
+
+ void Server::send( lam msg ) {
+ {
+ scoped_lock lk(m);
+ d.push_back(msg);
+ wassert( d.size() < 1024 );
+ }
+ c.notify_one();
+ }
+
+ void Server::doWork() {
+ starting();
+ while( 1 ) {
+ lam f;
+ try {
+ scoped_lock lk(m);
+ while( d.empty() )
+ c.wait(lk.boost());
+ f = d.front();
+ d.pop_front();
+ }
+ catch(...) {
+ log() << "ERROR exception in Server:doWork?" << endl;
+ }
+ try {
+ f();
+ if( rq ) {
+ rq = false;
+ {
+ scoped_lock lk(m);
+ d.push_back(f);
+ }
+ }
+ }
+ catch(std::exception& e) {
+ log() << "Server::doWork task:" << name() << " exception:" << e.what() << endl;
+ }
+ catch(const char *p) {
+ log() << "Server::doWork task:" << name() << " unknown c exception:" <<
+ ((p&&strlen(p)<800)?p:"?") << endl;
+ }
+ catch(...) {
+ log() << "Server::doWork unknown exception task:" << name() << endl;
+ }
+ }
+ }
+
+ static Server *s;
+ static void abc(int i) {
+ cout << "Hello " << i << endl;
+ s->requeue();
+ }
+ class TaskUnitTest : public mongo::UnitTest {
+ public:
+ virtual void run() {
+ lam f = boost::bind(abc, 3);
+ //f();
+
+ s = new Server("unittest");
+ fork(s);
+ s->send(f);
+
+ sleepsecs(30);
+ cout <<" done" << endl;
+
+ }
+ }; // not running. taskunittest;
+
+ }
+}
diff --git a/src/mongo/util/concurrency/task.h b/src/mongo/util/concurrency/task.h
new file mode 100644
index 00000000000..d7b45eeef24
--- /dev/null
+++ b/src/mongo/util/concurrency/task.h
@@ -0,0 +1,72 @@
+// @file task.h
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,b
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "../background.h"
+
+namespace mongo {
+
+ namespace task {
+
+ /** abstraction around threads. simpler than BackgroundJob which is used behind the scenes.
+ allocate the Task dynamically. when the thread terminates, the Task object will delete itself.
+ */
+ class Task : private BackgroundJob {
+ protected:
+ virtual void doWork() = 0; // implement the task here.
+ virtual string name() const = 0; // name the threada
+ public:
+ Task();
+
+ /** for a repeating task, stop after current invocation ends. can be called by other threads
+ as long as the Task is still in scope.
+ */
+ void halt();
+ private:
+ unsigned n, repeat;
+ friend void fork(Task* t);
+ friend void repeat(Task* t, unsigned millis);
+ virtual void run();
+ //virtual void ending() { }
+ void begin();
+ };
+
+ /** run once */
+ void fork(Task *t);
+
+ /** run doWork() over and over, with a pause between runs of millis */
+ void repeat(Task *t, unsigned millis);
+
+ /*** Example ***
+ inline void sample() {
+ class Sample : public Task {
+ public:
+ int result;
+ virtual void doWork() { result = 1234; }
+ Sample() : result(0) { }
+ };
+ shared_ptr<Sample> q( new Sample() );
+ fork(q);
+ cout << q->result << endl; // could print 1234 or 0.
+ }
+ */
+
+ }
+
+}
diff --git a/src/mongo/util/concurrency/thread_pool.cpp b/src/mongo/util/concurrency/thread_pool.cpp
new file mode 100644
index 00000000000..1c258847cb5
--- /dev/null
+++ b/src/mongo/util/concurrency/thread_pool.cpp
@@ -0,0 +1,141 @@
+/* threadpool.cpp
+*/
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "thread_pool.h"
+#include "mvar.h"
+
+namespace mongo {
+ namespace threadpool {
+
+ // Worker thread
+ class Worker : boost::noncopyable {
+ public:
+ explicit Worker(ThreadPool& owner)
+ : _owner(owner)
+ , _is_done(true)
+ , _thread(boost::bind(&Worker::loop, this))
+ {}
+
+ // destructor will block until current operation is completed
+ // Acts as a "join" on this thread
+ ~Worker() {
+ _task.put(Task());
+ _thread.join();
+ }
+
+ void set_task(Task& func) {
+ assert(!func.empty());
+ assert(_is_done);
+ _is_done = false;
+
+ _task.put(func);
+ }
+
+ private:
+ ThreadPool& _owner;
+ MVar<Task> _task;
+ bool _is_done; // only used for error detection
+ boost::thread _thread;
+
+ void loop() {
+ while (true) {
+ Task task = _task.take();
+ if (task.empty())
+ break; // ends the thread
+
+ try {
+ task();
+ }
+ catch (std::exception e) {
+ log() << "Unhandled exception in worker thread: " << e.what() << endl;;
+ }
+ catch (...) {
+ log() << "Unhandled non-exception in worker thread" << endl;
+ }
+ _is_done = true;
+ _owner.task_done(this);
+ }
+ }
+ };
+
+ ThreadPool::ThreadPool(int nThreads)
+ : _mutex("ThreadPool"), _tasksRemaining(0)
+ , _nThreads(nThreads) {
+ scoped_lock lock(_mutex);
+ while (nThreads-- > 0) {
+ Worker* worker = new Worker(*this);
+ _freeWorkers.push_front(worker);
+ }
+ }
+
+ ThreadPool::~ThreadPool() {
+ join();
+
+ assert(_tasks.empty());
+
+ // O(n) but n should be small
+ assert(_freeWorkers.size() == (unsigned)_nThreads);
+
+ while(!_freeWorkers.empty()) {
+ delete _freeWorkers.front();
+ _freeWorkers.pop_front();
+ }
+ }
+
+ void ThreadPool::join() {
+ scoped_lock lock(_mutex);
+ while(_tasksRemaining) {
+ _condition.wait(lock.boost());
+ }
+ }
+
+ void ThreadPool::schedule(Task task) {
+ scoped_lock lock(_mutex);
+
+ _tasksRemaining++;
+
+ if (!_freeWorkers.empty()) {
+ _freeWorkers.front()->set_task(task);
+ _freeWorkers.pop_front();
+ }
+ else {
+ _tasks.push_back(task);
+ }
+ }
+
+ // should only be called by a worker from the worker thread
+ void ThreadPool::task_done(Worker* worker) {
+ scoped_lock lock(_mutex);
+
+ if (!_tasks.empty()) {
+ worker->set_task(_tasks.front());
+ _tasks.pop_front();
+ }
+ else {
+ _freeWorkers.push_front(worker);
+ }
+
+ _tasksRemaining--;
+
+ if(_tasksRemaining == 0)
+ _condition.notify_all();
+ }
+
+ } //namespace threadpool
+} //namespace mongo
diff --git a/src/mongo/util/concurrency/thread_pool.h b/src/mongo/util/concurrency/thread_pool.h
new file mode 100644
index 00000000000..b348ed1d01b
--- /dev/null
+++ b/src/mongo/util/concurrency/thread_pool.h
@@ -0,0 +1,82 @@
+// thread_pool.h
+
+/* 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 <boost/function.hpp>
+#include <boost/bind.hpp>
+#undef assert
+#define assert MONGO_assert
+
+namespace mongo {
+
+ namespace threadpool {
+ class Worker;
+
+ typedef boost::function<void(void)> Task; //nullary function or functor
+
+ // exported to the mongo namespace
+ class ThreadPool : boost::noncopyable {
+ public:
+ explicit ThreadPool(int nThreads=8);
+
+ // blocks until all tasks are complete (tasks_remaining() == 0)
+ // You should not call schedule while in the destructor
+ ~ThreadPool();
+
+ // blocks until all tasks are complete (tasks_remaining() == 0)
+ // does not prevent new tasks from being scheduled so could wait forever.
+ // Also, new tasks could be scheduled after this returns.
+ void join();
+
+ // task will be copied a few times so make sure it's relatively cheap
+ void schedule(Task task);
+
+ // Helpers that wrap schedule and boost::bind.
+ // Functor and args will be copied a few times so make sure it's relatively cheap
+ template<typename F, typename A>
+ void schedule(F f, A a) { schedule(boost::bind(f,a)); }
+ template<typename F, typename A, typename B>
+ void schedule(F f, A a, B b) { schedule(boost::bind(f,a,b)); }
+ template<typename F, typename A, typename B, typename C>
+ void schedule(F f, A a, B b, C c) { schedule(boost::bind(f,a,b,c)); }
+ template<typename F, typename A, typename B, typename C, typename D>
+ void schedule(F f, A a, B b, C c, D d) { schedule(boost::bind(f,a,b,c,d)); }
+ template<typename F, typename A, typename B, typename C, typename D, typename E>
+ void schedule(F f, A a, B b, C c, D d, E e) { schedule(boost::bind(f,a,b,c,d,e)); }
+
+ int tasks_remaining() { return _tasksRemaining; }
+
+ private:
+ mongo::mutex _mutex;
+ boost::condition _condition;
+
+ list<Worker*> _freeWorkers; //used as LIFO stack (always front)
+ list<Task> _tasks; //used as FIFO queue (push_back, pop_front)
+ int _tasksRemaining; // in queue + currently processing
+ int _nThreads; // only used for sanity checking. could be removed in the future.
+
+ // should only be called by a worker from the worker's thread
+ void task_done(Worker* worker);
+ friend class Worker;
+ };
+
+ } //namespace threadpool
+
+ using threadpool::ThreadPool;
+
+} //namespace mongo
diff --git a/src/mongo/util/concurrency/threadlocal.h b/src/mongo/util/concurrency/threadlocal.h
new file mode 100644
index 00000000000..57a4f799dfa
--- /dev/null
+++ b/src/mongo/util/concurrency/threadlocal.h
@@ -0,0 +1,126 @@
+#pragma once
+
+/**
+* Copyright (C) 2011 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <boost/thread/tss.hpp>
+
+namespace mongo {
+
+ using boost::thread_specific_ptr;
+
+ /* 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() const {
+ 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 );
+ }
+
+ T& getRef() {
+ T *v = _val.get();
+ if( v ) {
+ return *v;
+ }
+ v = new T(_default);
+ _val.reset( v );
+ return *v;
+ }
+
+ private:
+ boost::thread_specific_ptr<T> _val;
+ const T _default;
+ };
+
+ /* TSP
+ These macros use intrinsics which are faster than boost::thread_specific_ptr.
+ However the intrinsics don't free up objects on thread closure. Thus we use
+ a combination here, with the assumption that reset's are infrequent, so that
+ get's are fast.
+ */
+#if defined(_WIN32) || (defined(__GNUC__) && defined(__linux__))
+
+ template< class T >
+ struct TSP {
+ boost::thread_specific_ptr<T> tsp;
+ public:
+ T* get() const;
+ void reset(T* v);
+ };
+
+# if defined(_WIN32)
+
+# define TSP_DECLARE(T,p) extern TSP<T> p;
+
+# define TSP_DEFINE(T,p) __declspec( thread ) T* _ ## p; \
+ TSP<T> p; \
+ template<> T* TSP<T>::get() const { return _ ## p; } \
+ void TSP<T>::reset(T* v) { \
+ tsp.reset(v); \
+ _ ## p = v; \
+ }
+# else
+
+# define TSP_DECLARE(T,p) \
+ extern __thread T* _ ## p; \
+ template<> inline T* TSP<T>::get() const { return _ ## p; } \
+ extern TSP<T> p;
+
+# define TSP_DEFINE(T,p) \
+ __thread T* _ ## p; \
+ template<> void TSP<T>::reset(T* v) { \
+ tsp.reset(v); \
+ _ ## p = v; \
+ } \
+ TSP<T> p;
+# endif
+
+#else
+
+ template< class T >
+ struct TSP {
+ thread_specific_ptr<T> tsp;
+ public:
+ T* get() const { return tsp.get(); }
+ void reset(T* v) { tsp.reset(v); }
+ };
+
+# define TSP_DECLARE(T,p) extern TSP<T> p;
+
+# define TSP_DEFINE(T,p) TSP<T> p;
+
+#endif
+
+}
diff --git a/src/mongo/util/concurrency/value.h b/src/mongo/util/concurrency/value.h
new file mode 100644
index 00000000000..fdd0d9bbb42
--- /dev/null
+++ b/src/mongo/util/concurrency/value.h
@@ -0,0 +1,139 @@
+/* @file value.h
+ concurrency helpers DiagStr, Guarded
+*/
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,b
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "spin_lock.h"
+
+namespace mongo {
+
+ /** declare that a variable that is "guarded" by a mutex.
+
+ The decl documents the rule. For example "counta and countb are guarded by xyzMutex":
+
+ Guarded<int, xyzMutex> counta;
+ Guarded<int, xyzMutex> countb;
+
+ Upon use, specify the scoped_lock object. This makes it hard for someone
+ later to forget to be in the lock. Check is made that it is the right lock in _DEBUG
+ builds at runtime.
+ */
+ template <typename T, SimpleMutex& BY>
+ class Guarded {
+ T _val;
+ public:
+ T& ref(const SimpleMutex::scoped_lock& lk) {
+ dassert( &lk.m() == &BY );
+ return _val;
+ }
+ };
+
+ // todo: rename this to ThreadSafeString or something
+ /** there is now one mutex per DiagStr. If you have hundreds or millions of
+ DiagStrs you'll need to do something different.
+ */
+ class DiagStr {
+ mutable SpinLock m;
+ string _s;
+ public:
+ DiagStr(const DiagStr& r) : _s(r.get()) { }
+ DiagStr(const string& r) : _s(r) { }
+ DiagStr() { }
+ bool empty() const {
+ scoped_spinlock lk(m);
+ return _s.empty();
+ }
+ string get() const {
+ scoped_spinlock lk(m);
+ return _s;
+ }
+ void set(const char *s) {
+ scoped_spinlock lk(m);
+ _s = s;
+ }
+ void set(const string& s) {
+ scoped_spinlock lk(m);
+ _s = s;
+ }
+ operator string() const { return get(); }
+ void operator=(const string& s) { set(s); }
+ void operator=(const DiagStr& rhs) {
+ set( rhs.get() );
+ }
+
+ // == is not defined. use get() == ... instead. done this way so one thinks about if composing multiple operations
+ bool operator==(const string& s) const;
+ };
+
+ /** Thread safe map.
+ Be careful not to use this too much or it could make things slow;
+ if not a hot code path no problem.
+
+ Examples:
+
+ mapsf<int,int> mp;
+
+ int x = mp.get();
+
+ map<int,int> two;
+ mp.swap(two);
+
+ {
+ mapsf<int,int>::ref r(mp);
+ r[9] = 1;
+ map<int,int>::iterator i = r.r.begin();
+ }
+
+ */
+ template< class K, class V >
+ struct mapsf : boost::noncopyable {
+ SimpleMutex m;
+ map<K,V> val;
+ friend struct ref;
+ public:
+ mapsf() : m("mapsf") { }
+ void swap(map<K,V>& rhs) {
+ SimpleMutex::scoped_lock lk(m);
+ val.swap(rhs);
+ }
+ bool empty() {
+ SimpleMutex::scoped_lock lk(m);
+ return val.empty();
+ }
+ // safe as we pass by value:
+ V get(K k) {
+ SimpleMutex::scoped_lock lk(m);
+ typename map<K,V>::iterator i = val.find(k);
+ if( i == val.end() )
+ return V();
+ return i->second;
+ }
+ // think about deadlocks when using ref. the other methods
+ // above will always be safe as they are "leaf" operations.
+ struct ref {
+ SimpleMutex::scoped_lock lk;
+ public:
+ map<K,V> &r;
+ ref(mapsf<K,V> &m) : lk(m.m), r(m.val) { }
+ V& operator[](const K& k) { return r[k]; }
+ };
+ };
+
+}
diff --git a/src/mongo/util/concurrency/vars.cpp b/src/mongo/util/concurrency/vars.cpp
new file mode 100644
index 00000000000..0b2fc960c04
--- /dev/null
+++ b/src/mongo/util/concurrency/vars.cpp
@@ -0,0 +1,56 @@
+// vars.cpp
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,b
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+#include "mutex.h"
+#include "value.h"
+
+namespace mongo {
+
+#if defined(_DEBUG)
+
+ // intentional leak. otherwise destructor orders can be problematic at termination.
+ MutexDebugger &mutexDebugger = *(new MutexDebugger());
+
+ MutexDebugger::MutexDebugger() :
+ x( *(new boost::mutex()) ), magic(0x12345678) {
+ // optional way to debug lock order
+ /*
+ a = "a_lock";
+ b = "b_lock";
+ */
+ }
+
+ void MutexDebugger::programEnding() {
+ if( logLevel>=1 && followers.size() ) {
+ std::cout << followers.size() << " mutexes in program" << endl;
+ for( map< mid, set<mid> >::iterator i = followers.begin(); i != followers.end(); i++ ) {
+ cout << i->first;
+ if( maxNest[i->first] > 1 )
+ cout << " maxNest:" << maxNest[i->first];
+ cout << '\n';
+ for( set<mid>::iterator j = i->second.begin(); j != i->second.end(); j++ )
+ cout << " " << *j << '\n';
+ }
+ cout.flush();
+ }
+ }
+
+#endif
+
+}
diff --git a/src/mongo/util/debug_util.cpp b/src/mongo/util/debug_util.cpp
new file mode 100644
index 00000000000..8ba6534ef7c
--- /dev/null
+++ b/src/mongo/util/debug_util.cpp
@@ -0,0 +1,60 @@
+// debug_util.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "../db/cmdline.h"
+#include "../db/jsobj.h"
+
+namespace mongo {
+
+#if defined(USE_GDBSERVER)
+ /* Magic gdb trampoline
+ * Do not call directly! call setupSIGTRAPforGDB()
+ * Assumptions:
+ * 1) gdbserver is on your path
+ * 2) You have run "handle SIGSTOP noprint" in gdb
+ * 3) cmdLine.port + 2000 is free
+ */
+ void launchGDB(int) {
+ // Don't come back here
+ signal(SIGTRAP, SIG_IGN);
+
+ int newPort = cmdLine.port + 2000;
+ string newPortStr = "localhost:" + BSONObjBuilder::numStr(newPort);
+ string pidToDebug = BSONObjBuilder::numStr(getpid());
+
+ cout << "\n\n\t**** Launching gdbserver on " << newPortStr << " ****" << endl << endl;
+ if (fork() == 0) {
+ //child
+ execlp("gdbserver", "gdbserver", "--attach", newPortStr.c_str(), pidToDebug.c_str(), NULL);
+ perror(NULL);
+ }
+ else {
+ //parent
+ raise(SIGSTOP); // pause all threads until gdb connects and continues
+ raise(SIGTRAP); // break inside gdbserver
+ }
+ }
+
+ void setupSIGTRAPforGDB() {
+ assert( signal(SIGTRAP , launchGDB ) != SIG_ERR );
+ }
+#else
+ void setupSIGTRAPforGDB() {
+ }
+#endif
+}
diff --git a/src/mongo/util/debug_util.h b/src/mongo/util/debug_util.h
new file mode 100644
index 00000000000..abed8d94924
--- /dev/null
+++ b/src/mongo/util/debug_util.h
@@ -0,0 +1,106 @@
+// debug_util.h
+
+/* 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
+
+#ifndef _WIN32
+#include <signal.h>
+#endif
+
+namespace mongo {
+
+// for debugging
+ typedef struct _Ints {
+ int i[100];
+ } *Ints;
+ typedef struct _Chars {
+ char c[200];
+ } *Chars;
+
+ typedef char CHARS[400];
+
+ typedef struct _OWS {
+ int size;
+ char type;
+ char string[400];
+ } *OWS;
+
+#if defined(_DEBUG)
+ enum {DEBUG_BUILD = 1};
+#else
+ enum {DEBUG_BUILD = 0};
+#endif
+
+#define MONGO_DEV if( DEBUG_BUILD )
+#define DEV MONGO_DEV
+
+#define MONGO_DEBUGGING if( 0 )
+#define DEBUGGING MONGO_DEBUGGING
+
+// The following declare one unique counter per enclosing function.
+// NOTE The implementation double-increments on a match, but we don't really care.
+#define MONGO_SOMETIMES( occasion, howOften ) for( static unsigned occasion = 0; ++occasion % howOften == 0; )
+#define SOMETIMES MONGO_SOMETIMES
+
+#define MONGO_OCCASIONALLY SOMETIMES( occasionally, 16 )
+#define OCCASIONALLY MONGO_OCCASIONALLY
+
+#define MONGO_RARELY SOMETIMES( rarely, 128 )
+#define RARELY MONGO_RARELY
+
+#define MONGO_ONCE for( static bool undone = true; undone; undone = false )
+#define ONCE MONGO_ONCE
+
+#if defined(_WIN32)
+ inline int strcasecmp(const char* s1, const char* s2) {return _stricmp(s1, s2);}
+#endif
+
+ // Sets SIGTRAP handler to launch GDB
+ // Noop unless on *NIX and compiled with _DEBUG
+ void setupSIGTRAPforGDB();
+
+ extern int tlogLevel;
+
+ inline void breakpoint() {
+ if ( tlogLevel < 0 )
+ return;
+#ifdef _WIN32
+ //DEV DebugBreak();
+#endif
+#ifndef _WIN32
+ // code to raise a breakpoint in GDB
+ ONCE {
+ //prevent SIGTRAP from crashing the program if default action is specified and we are not in gdb
+ struct sigaction current;
+ sigaction(SIGTRAP, NULL, &current);
+ if (current.sa_handler == SIG_DFL) {
+ signal(SIGTRAP, SIG_IGN);
+ }
+ }
+
+ raise(SIGTRAP);
+#endif
+ }
+
+
+ // conditional breakpoint
+ inline void breakif(bool test) {
+ if (test)
+ breakpoint();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/embedded_builder.h b/src/mongo/util/embedded_builder.h
new file mode 100644
index 00000000000..abf518e2583
--- /dev/null
+++ b/src/mongo/util/embedded_builder.h
@@ -0,0 +1,92 @@
+// embedded_builder.h
+
+/* 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
+
+namespace mongo {
+
+ // utility class for assembling hierarchical objects
+ class EmbeddedBuilder {
+ public:
+ EmbeddedBuilder( BSONObjBuilder *b ) {
+ _builders.push_back( make_pair( "", b ) );
+ }
+ // It is assumed that the calls to prepareContext will be made with the 'name'
+ // parameter in lex ascending order.
+ void prepareContext( string &name ) {
+ int i = 1, n = _builders.size();
+ while( i < n &&
+ name.substr( 0, _builders[ i ].first.length() ) == _builders[ i ].first &&
+ ( name[ _builders[i].first.length() ] == '.' || name[ _builders[i].first.length() ] == 0 )
+ ) {
+ name = name.substr( _builders[ i ].first.length() + 1 );
+ ++i;
+ }
+ for( int j = n - 1; j >= i; --j ) {
+ popBuilder();
+ }
+ for( string next = splitDot( name ); !next.empty(); next = splitDot( name ) ) {
+ addBuilder( next );
+ }
+ }
+ void appendAs( const BSONElement &e, string name ) {
+ if ( e.type() == Object && e.valuesize() == 5 ) { // empty object -- this way we can add to it later
+ string dummyName = name + ".foo";
+ prepareContext( dummyName );
+ return;
+ }
+ prepareContext( name );
+ back()->appendAs( e, name );
+ }
+ BufBuilder &subarrayStartAs( string name ) {
+ prepareContext( name );
+ return back()->subarrayStart( name );
+ }
+ void done() {
+ while( ! _builderStorage.empty() )
+ popBuilder();
+ }
+
+ static string splitDot( string & str ) {
+ size_t pos = str.find( '.' );
+ if ( pos == string::npos )
+ return "";
+ string ret = str.substr( 0, pos );
+ str = str.substr( pos + 1 );
+ return ret;
+ }
+
+ private:
+ void addBuilder( const string &name ) {
+ shared_ptr< BSONObjBuilder > newBuilder( new BSONObjBuilder( back()->subobjStart( name ) ) );
+ _builders.push_back( make_pair( name, newBuilder.get() ) );
+ _builderStorage.push_back( newBuilder );
+ }
+ void popBuilder() {
+ back()->done();
+ _builders.pop_back();
+ _builderStorage.pop_back();
+ }
+
+ BSONObjBuilder *back() { return _builders.back().second; }
+
+ vector< pair< string, BSONObjBuilder * > > _builders;
+ vector< shared_ptr< BSONObjBuilder > > _builderStorage;
+
+ };
+
+} //namespace mongo
diff --git a/src/mongo/util/file.h b/src/mongo/util/file.h
new file mode 100644
index 00000000000..368e6927b43
--- /dev/null
+++ b/src/mongo/util/file.h
@@ -0,0 +1,230 @@
+// file.h cross platform basic file class. supports 64 bit offsets and such.
+
+/* 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
+
+#if !defined(_WIN32)
+#include "errno.h"
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/statvfs.h>
+#endif
+#include "text.h"
+
+namespace mongo {
+
+#ifndef __sunos__
+ typedef uint64_t fileofs;
+#else
+ typedef boost::uint64_t fileofs;
+#endif
+
+ /* NOTE: not thread-safe. (at least the windows implementation isn't. */
+
+ class FileInterface {
+ public:
+ void open(const char *fn) {}
+ void write(fileofs o, const char *data, unsigned len) {}
+ void read(fileofs o, char *data, unsigned len) {}
+ bool bad() {return false;}
+ bool is_open() {return false;}
+ fileofs len() { return 0; }
+ void fsync() { assert(false); }
+
+ // shrink file to size bytes. No-op if file already smaller.
+ void truncate(fileofs size);
+
+ /** @return -1 if error or unavailable */
+ static boost::intmax_t freeSpace(const string &path) { assert(false); return -1; }
+ };
+
+#if defined(_WIN32)
+#include <io.h>
+
+ class File : public FileInterface {
+ HANDLE fd;
+ bool _bad;
+ string _name;
+ void err(BOOL b=false) { /* false = error happened */
+ if( !b && !_bad ) {
+ _bad = true;
+ log() << "File " << _name << "I/O error " << GetLastError() << '\n';
+ }
+ }
+ public:
+ File() {
+ fd = INVALID_HANDLE_VALUE;
+ _bad = true;
+ }
+ ~File() {
+ if( is_open() ) CloseHandle(fd);
+ fd = INVALID_HANDLE_VALUE;
+ }
+ void open(const char *filename, bool readOnly=false , bool direct=false) {
+ _name = filename;
+ fd = CreateFile(
+ toNativeString(filename).c_str(),
+ ( readOnly ? 0 : GENERIC_WRITE ) | GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ,
+ NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ if( !is_open() ) {
+ DWORD e = GetLastError();
+ log() << "Create/Open File failed " << filename << ' ' << errnoWithDescription(e) << endl;
+ }
+ else
+ _bad = false;
+ }
+ static boost::intmax_t freeSpace(const string &path) {
+ ULARGE_INTEGER avail;
+ if( GetDiskFreeSpaceEx(toNativeString(path.c_str()).c_str(), &avail, NULL, NULL) ) {
+ return avail.QuadPart;
+ }
+ DWORD e = GetLastError();
+ log() << "GetDiskFreeSpaceEx fails errno: " << e << endl;
+ return -1;
+ }
+ void write(fileofs o, const char *data, unsigned len) {
+ LARGE_INTEGER li;
+ li.QuadPart = o;
+ SetFilePointerEx(fd, li, NULL, FILE_BEGIN);
+ DWORD written;
+ err( WriteFile(fd, data, len, &written, NULL) );
+ }
+ void read(fileofs o, char *data, unsigned len) {
+ DWORD read;
+ LARGE_INTEGER li;
+ li.QuadPart = o;
+ SetFilePointerEx(fd, li, NULL, FILE_BEGIN);
+ int ok = ReadFile(fd, data, len, &read, 0);
+ if( !ok )
+ err(ok);
+ else
+ massert( 10438 , "ReadFile error - truncated file?", read == len);
+ }
+ bool bad() { return _bad; }
+ bool is_open() { return fd != INVALID_HANDLE_VALUE; }
+ fileofs len() {
+ LARGE_INTEGER li;
+ li.LowPart = GetFileSize(fd, (DWORD *) &li.HighPart);
+ if( li.HighPart == 0 && li.LowPart == INVALID_FILE_SIZE ) {
+ err( false );
+ return 0;
+ }
+ return li.QuadPart;
+ }
+ void fsync() { FlushFileBuffers(fd); }
+
+ void truncate(fileofs size) {
+ if (len() <= size)
+ return;
+
+ LARGE_INTEGER li;
+ li.QuadPart = size;
+ if (SetFilePointerEx(fd, li, NULL, FILE_BEGIN) == 0){
+ err(false);
+ return; //couldn't seek
+ }
+
+ err(SetEndOfFile(fd));
+ }
+ };
+
+#else
+
+ class File : public FileInterface {
+ public:
+ int fd;
+ private:
+ bool _bad;
+ void err(bool ok) {
+ if( !ok && !_bad ) {
+ _bad = true;
+ log() << "File I/O " << errnoWithDescription() << '\n';
+ }
+ }
+ public:
+ File() {
+ fd = -1;
+ _bad = true;
+ }
+ ~File() {
+ if( is_open() ) ::close(fd);
+ fd = -1;
+ }
+
+#ifndef O_NOATIME
+#define O_NOATIME 0
+#endif
+
+ void open(const char *filename, bool readOnly=false , bool direct=false) {
+ fd = ::open(filename,
+ O_CREAT | ( readOnly ? 0 : ( O_RDWR | O_NOATIME ) )
+#if defined(O_DIRECT)
+ | ( direct ? O_DIRECT : 0 )
+#endif
+ ,
+ S_IRUSR | S_IWUSR);
+ if ( fd <= 0 ) {
+ out() << "couldn't open " << filename << ' ' << errnoWithDescription() << endl;
+ return;
+ }
+ _bad = false;
+ }
+ void write(fileofs o, const char *data, unsigned len) {
+ err( ::pwrite(fd, data, len, o) == (int) len );
+ }
+ void read(fileofs o, char *data, unsigned len) {
+ ssize_t s = ::pread(fd, data, len, o);
+ if( s == -1 ) {
+ err(false);
+ }
+ else if( s != (int) len ) {
+ _bad = true;
+ log() << "File error read:" << s << " bytes, wanted:" << len << " ofs:" << o << endl;
+ }
+ }
+ bool bad() { return _bad; }
+ bool is_open() { return fd > 0; }
+ fileofs len() {
+ off_t o = lseek(fd, 0, SEEK_END);
+ if( o != (off_t) -1 )
+ return o;
+ err(false);
+ return 0;
+ }
+ void fsync() { ::fsync(fd); }
+ static boost::intmax_t freeSpace ( const string &path ) {
+ struct statvfs info;
+ assert( !statvfs( path.c_str() , &info ) );
+ return boost::intmax_t( info.f_bavail ) * info.f_frsize;
+ }
+
+ void truncate(fileofs size) {
+ if (len() <= size)
+ return;
+
+ err(ftruncate(fd, size) == 0);
+ }
+ };
+
+
+#endif
+
+
+}
+
diff --git a/src/mongo/util/file_allocator.cpp b/src/mongo/util/file_allocator.cpp
new file mode 100644
index 00000000000..b0572f971bd
--- /dev/null
+++ b/src/mongo/util/file_allocator.cpp
@@ -0,0 +1,329 @@
+// @file file_allocator.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include <fcntl.h>
+#include <errno.h>
+
+#if defined(__freebsd__) || defined(__openbsd__)
+#include <sys/stat.h>
+#endif
+
+#include "timer.h"
+#include "mongoutils/str.h"
+using namespace mongoutils;
+
+#ifndef O_NOATIME
+#define O_NOATIME (0)
+#endif
+
+#include "file_allocator.h"
+#include "paths.h"
+
+namespace mongo {
+
+ boost::filesystem::path ensureParentDirCreated(const boost::filesystem::path& p){
+ const boost::filesystem::path parent = p.branch_path();
+
+ if (! boost::filesystem::exists(parent)){
+ ensureParentDirCreated(parent);
+ log() << "creating directory " << parent.string() << endl;
+ boost::filesystem::create_directory(parent);
+ flushMyDirectory(parent); // flushes grandparent to ensure parent exists after crash
+ }
+
+ assert(boost::filesystem::is_directory(parent));
+ return parent;
+ }
+
+#if defined(_WIN32)
+ FileAllocator::FileAllocator() {
+ }
+
+ void FileAllocator::start() {
+ }
+
+ void FileAllocator::requestAllocation( const string &name, long &size ) {
+ /* Some of the system calls in the file allocator don't work in win,
+ so no win support - 32 or 64 bit. Plus we don't seem to need preallocation
+ on windows anyway as we don't have to pre-zero the file there.
+ */
+ }
+
+ void FileAllocator::allocateAsap( const string &name, unsigned long long &size ) {
+ // no-op
+ }
+
+ void FileAllocator::waitUntilFinished() const {
+ // no-op
+ }
+
+ void FileAllocator::ensureLength(int fd , long size) {
+ // we don't zero on windows
+ // TODO : we should to avoid fragmentation
+ }
+
+ bool FileAllocator::hasFailed() const {
+ return false;
+ }
+
+#else
+
+ FileAllocator::FileAllocator()
+ : _pendingMutex("FileAllocator"), _failed() {
+ }
+
+
+ void FileAllocator::start() {
+ boost::thread t( boost::bind( &FileAllocator::run , this ) );
+ }
+
+ void FileAllocator::requestAllocation( const string &name, long &size ) {
+ scoped_lock lk( _pendingMutex );
+ if ( _failed )
+ return;
+ long oldSize = prevSize( name );
+ if ( oldSize != -1 ) {
+ size = oldSize;
+ return;
+ }
+ _pending.push_back( name );
+ _pendingSize[ name ] = size;
+ _pendingUpdated.notify_all();
+ }
+
+ void FileAllocator::allocateAsap( const string &name, unsigned long long &size ) {
+ scoped_lock lk( _pendingMutex );
+ long oldSize = prevSize( name );
+ if ( oldSize != -1 ) {
+ size = oldSize;
+ if ( !inProgress( name ) )
+ return;
+ }
+ checkFailure();
+ _pendingSize[ name ] = size;
+ if ( _pending.size() == 0 )
+ _pending.push_back( name );
+ else if ( _pending.front() != name ) {
+ _pending.remove( name );
+ list< string >::iterator i = _pending.begin();
+ ++i;
+ _pending.insert( i, name );
+ }
+ _pendingUpdated.notify_all();
+ while( inProgress( name ) ) {
+ checkFailure();
+ _pendingUpdated.wait( lk.boost() );
+ }
+
+ }
+
+ void FileAllocator::waitUntilFinished() const {
+ if ( _failed )
+ return;
+ scoped_lock lk( _pendingMutex );
+ while( _pending.size() != 0 )
+ _pendingUpdated.wait( lk.boost() );
+ }
+
+ void FileAllocator::ensureLength(int fd , long size) {
+#if defined(__linux__)
+ int ret = posix_fallocate(fd,0,size);
+ if ( ret == 0 )
+ return;
+
+ log() << "FileAllocator: posix_fallocate failed: " << errnoWithDescription( ret ) << " falling back" << endl;
+#endif
+
+ off_t filelen = lseek(fd, 0, SEEK_END);
+ if ( filelen < size ) {
+ if (filelen != 0) {
+ stringstream ss;
+ ss << "failure creating new datafile; lseek failed for fd " << fd << " with errno: " << errnoWithDescription();
+ uassert( 10440 , ss.str(), filelen == 0 );
+ }
+ // Check for end of disk.
+
+ uassert( 10441 , str::stream() << "Unable to allocate new file of size " << size << ' ' << errnoWithDescription(),
+ size - 1 == lseek(fd, size - 1, SEEK_SET) );
+ uassert( 10442 , str::stream() << "Unable to allocate new file of size " << size << ' ' << errnoWithDescription(),
+ 1 == write(fd, "", 1) );
+ lseek(fd, 0, SEEK_SET);
+
+ const long z = 256 * 1024;
+ const boost::scoped_array<char> buf_holder (new char[z]);
+ char* buf = buf_holder.get();
+ memset(buf, 0, z);
+ long left = size;
+ while ( left > 0 ) {
+ long towrite = left;
+ if ( towrite > z )
+ towrite = z;
+
+ int written = write( fd , buf , towrite );
+ uassert( 10443 , errnoWithPrefix("FileAllocator: file write failed" ), written > 0 );
+ left -= written;
+ }
+ }
+ }
+
+ bool FileAllocator::hasFailed() const {
+ return _failed;
+ }
+
+ void FileAllocator::checkFailure() {
+ if (_failed) {
+ // we want to log the problem (diskfull.js expects it) but we do not want to dump a stack tracke
+ msgassertedNoTrace( 12520, "new file allocation failure" );
+ }
+ }
+
+ long FileAllocator::prevSize( const string &name ) const {
+ if ( _pendingSize.count( name ) > 0 )
+ return _pendingSize[ name ];
+ if ( boost::filesystem::exists( name ) )
+ return boost::filesystem::file_size( name );
+ return -1;
+ }
+
+ // caller must hold _pendingMutex lock.
+ bool FileAllocator::inProgress( const string &name ) const {
+ for( list< string >::const_iterator i = _pending.begin(); i != _pending.end(); ++i )
+ if ( *i == name )
+ return true;
+ return false;
+ }
+
+ string makeTempFileName( path root ) {
+ while( 1 ) {
+ path p = root / "_tmp";
+ stringstream ss;
+ ss << (unsigned) rand();
+ p /= ss.str();
+ string fn = p.string();
+ if( !boost::filesystem::exists(p) )
+ return fn;
+ }
+ return "";
+ }
+
+ void FileAllocator::run( FileAllocator * fa ) {
+ setThreadName( "FileAllocator" );
+ while( 1 ) {
+ {
+ scoped_lock lk( fa->_pendingMutex );
+ if ( fa->_pending.size() == 0 )
+ fa->_pendingUpdated.wait( lk.boost() );
+ }
+ while( 1 ) {
+ string name;
+ long size;
+ {
+ scoped_lock lk( fa->_pendingMutex );
+ if ( fa->_pending.size() == 0 )
+ break;
+ name = fa->_pending.front();
+ size = fa->_pendingSize[ name ];
+ }
+
+ string tmp;
+ long fd = 0;
+ try {
+ log() << "allocating new datafile " << name << ", filling with zeroes..." << endl;
+
+ boost::filesystem::path parent = ensureParentDirCreated(name);
+ tmp = makeTempFileName( parent );
+ ensureParentDirCreated(tmp);
+
+ fd = open(tmp.c_str(), O_CREAT | O_RDWR | O_NOATIME, S_IRUSR | S_IWUSR);
+ if ( fd <= 0 ) {
+ log() << "FileAllocator: couldn't create " << name << " (" << tmp << ") " << errnoWithDescription() << endl;
+ uasserted(10439, "");
+ }
+
+#if defined(POSIX_FADV_DONTNEED)
+ if( posix_fadvise(fd, 0, size, POSIX_FADV_DONTNEED) ) {
+ log() << "warning: posix_fadvise fails " << name << " (" << tmp << ") " << errnoWithDescription() << endl;
+ }
+#endif
+
+ Timer t;
+
+ /* make sure the file is the full desired length */
+ ensureLength( fd , size );
+
+ close( fd );
+ fd = 0;
+
+ if( rename(tmp.c_str(), name.c_str()) ) {
+ log() << "error: couldn't rename " << tmp << " to " << name << ' ' << errnoWithDescription() << endl;
+ uasserted(13653, "");
+ }
+ flushMyDirectory(name);
+
+ log() << "done allocating datafile " << name << ", "
+ << "size: " << size/1024/1024 << "MB, "
+ << " took " << ((double)t.millis())/1000.0 << " secs"
+ << endl;
+
+ // no longer in a failed state. allow new writers.
+ fa->_failed = false;
+ }
+ catch ( ... ) {
+ if ( fd > 0 )
+ close( fd );
+ log() << "error failed to allocate new file: " << name
+ << " size: " << size << ' ' << errnoWithDescription() << warnings;
+ log() << " will try again in 10 seconds" << endl; // not going to warning logs
+ try {
+ if ( tmp.size() )
+ BOOST_CHECK_EXCEPTION( boost::filesystem::remove( tmp ) );
+ BOOST_CHECK_EXCEPTION( boost::filesystem::remove( name ) );
+ }
+ catch ( ... ) {
+ }
+ scoped_lock lk( fa->_pendingMutex );
+ fa->_failed = true;
+ // not erasing from pending
+ fa->_pendingUpdated.notify_all();
+
+
+ sleepsecs(10);
+ continue;
+ }
+
+ {
+ scoped_lock lk( fa->_pendingMutex );
+ fa->_pendingSize.erase( name );
+ fa->_pending.pop_front();
+ fa->_pendingUpdated.notify_all();
+ }
+ }
+ }
+ }
+
+#endif
+
+ FileAllocator* FileAllocator::_instance = 0;
+
+ FileAllocator* FileAllocator::get(){
+ if ( ! _instance )
+ _instance = new FileAllocator();
+ return _instance;
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/file_allocator.h b/src/mongo/util/file_allocator.h
new file mode 100644
index 00000000000..7c3cacb2888
--- /dev/null
+++ b/src/mongo/util/file_allocator.h
@@ -0,0 +1,91 @@
+// @file file_allocator.h
+
+/* 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.
+ */
+
+#include "../pch.h"
+
+namespace mongo {
+
+ /*
+ * Handles allocation of contiguous files on disk. Allocation may be
+ * requested asynchronously or synchronously.
+ * singleton
+ */
+ class FileAllocator : boost::noncopyable {
+ /*
+ * The public functions may not be called concurrently. The allocation
+ * functions may be called multiple times per file, but only the first
+ * size specified per file will be used.
+ */
+ public:
+ void start();
+
+ /**
+ * May be called if file exists. If file exists, or its allocation has
+ * been requested, size is updated to match existing file size.
+ */
+ void requestAllocation( const string &name, long &size );
+
+
+ /**
+ * Returns when file has been allocated. If file exists, size is
+ * updated to match existing file size.
+ */
+ void allocateAsap( const string &name, unsigned long long &size );
+
+ void waitUntilFinished() const;
+
+ bool hasFailed() const;
+
+ static void ensureLength(int fd , long size);
+
+ /** @return the singletone */
+ static FileAllocator * get();
+
+ private:
+
+ FileAllocator();
+
+#if !defined(_WIN32)
+ void checkFailure();
+
+ // caller must hold pendingMutex_ lock. Returns size if allocated or
+ // allocation requested, -1 otherwise.
+ long prevSize( const string &name ) const;
+
+ // caller must hold pendingMutex_ lock.
+ bool inProgress( const string &name ) const;
+
+ /** called from the worked thread */
+ static void run( FileAllocator * fa );
+
+ mutable mongo::mutex _pendingMutex;
+ mutable boost::condition _pendingUpdated;
+
+ list< string > _pending;
+ mutable map< string, long > _pendingSize;
+
+ bool _failed;
+#endif
+
+ static FileAllocator* _instance;
+
+ };
+
+ /** like "mkdir -p" but on parent dir of p rather than p itself */
+ boost::filesystem::path ensureParentDirCreated(const boost::filesystem::path& p);
+
+} // namespace mongo
diff --git a/src/mongo/util/goodies.h b/src/mongo/util/goodies.h
new file mode 100644
index 00000000000..9398b5c3f1d
--- /dev/null
+++ b/src/mongo/util/goodies.h
@@ -0,0 +1,475 @@
+// @file goodies.h
+// miscellaneous
+
+/* 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"
+#include "concurrency/mutex.h"
+
+namespace mongo {
+
+ /* @return a dump of the buffer as hex byte ascii output */
+ string hexdump(const char *data, unsigned len);
+
+ /**
+ * @return if this name has an increasing counter associated, return the value
+ * otherwise 0
+ */
+ unsigned setThreadName(const char * name);
+ string getThreadName();
+
+ template<class T>
+ inline string ToString(const T& t) {
+ stringstream s;
+ s << t;
+ return s.str();
+ }
+
+#if !defined(_WIN32) && !defined(NOEXECINFO) && !defined(__freebsd__) && !defined(__openbsd__) && !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];
+
+ int size = backtrace(b, 20);
+ for (int i = 0; i < size; i++)
+ o << hex << b[i] << dec << ' ';
+ o << endl;
+
+ char **strings;
+
+ strings = backtrace_symbols(b, size);
+ for (int i = 0; i < size; i++)
+ o << ' ' << strings[i] << '\n';
+ o.flush();
+ free (strings);
+ }
+#else
+ inline void printStackTrace( ostream &o = cout ) { }
+#endif
+
+ 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
+#define MONGO_FLOG log() << __FILE__ ":" << __LINE__ << endl
+#define FLOG MONGO_FLOG
+
+#undef assert
+#define assert MONGO_assert
+
+ 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;
+ }
+
+ 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
+
+#if !defined(_WIN32)
+ typedef int HANDLE;
+ inline void strcpy_s(char *dst, unsigned len, const char *src) {
+ assert( strlen(src) < len );
+ strcpy(dst, src);
+ }
+#else
+ typedef void *HANDLE;
+#endif
+
+ class ProgressMeter : boost::noncopyable {
+ public:
+ ProgressMeter( unsigned long long total , int secondsBetween = 3 , int checkInterval = 100 , string units = "" ) : _units(units) {
+ reset( total , secondsBetween , checkInterval );
+ }
+
+ ProgressMeter() {
+ _active = 0;
+ _units = "";
+ }
+
+ // typically you do ProgressMeterHolder
+ void reset( unsigned 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;
+ }
+
+ /**
+ * @param n how far along we are relative to the total # we set in CurOp::setMessage
+ * @return if row was printed
+ */
+ bool hit( int n = 1 ) {
+ if ( ! _active ) {
+ cout << "warning: hit an inactive ProgressMeter" << endl;
+ return false;
+ }
+
+ _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 << "%";
+
+ if ( ! _units.empty() ) {
+ cout << "\t(" << _units << ")" << endl;
+ }
+ else {
+ cout << endl;
+ }
+ }
+ _lastTime = t;
+ return true;
+ }
+
+ void setUnits( string units ) {
+ _units = units;
+ }
+
+ void setTotalWhileRunning( unsigned long long total ) {
+ _total = total;
+ }
+
+ unsigned long long done() const { return _done; }
+
+ unsigned long long hits() const { return _hits; }
+
+ unsigned long long total() const { return _total; }
+
+ string toString() const {
+ if ( ! _active )
+ return "";
+ stringstream buf;
+ buf << _done << "/" << _total << " " << (_done*100)/_total << "%";
+
+ if ( ! _units.empty() ) {
+ buf << "\t(" << _units << ")" << endl;
+ }
+
+ return buf.str();
+ }
+
+ bool operator==( const ProgressMeter& other ) const {
+ return this == &other;
+ }
+ private:
+
+ bool _active;
+
+ unsigned long long _total;
+ int _secondsBetween;
+ int _checkInterval;
+
+ unsigned long long _done;
+ unsigned long long _hits;
+ int _lastTime;
+
+ string _units;
+ };
+
+ // e.g.:
+ // CurOp * op = cc().curop();
+ // ProgressMeterHolder pm( op->setMessage( "index: (1/3) external sort" , d->stats.nrecords , 10 ) );
+ // loop { pm.hit(); }
+ class ProgressMeterHolder : boost::noncopyable {
+ public:
+ ProgressMeterHolder( ProgressMeter& pm )
+ : _pm( pm ) {
+ }
+
+ ~ProgressMeterHolder() {
+ _pm.finished();
+ }
+
+ ProgressMeter* operator->() {
+ return &_pm;
+ }
+
+ bool hit( int n = 1 ) {
+ return _pm.hit( n );
+ }
+
+ void finished() {
+ _pm.finished();
+ }
+
+ bool operator==( const ProgressMeter& other ) {
+ return _pm == other;
+ }
+
+ private:
+ ProgressMeter& _pm;
+ };
+
+ class TicketHolder {
+ public:
+ TicketHolder( int num ) : _mutex("TicketHolder") {
+ _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() const {
+ return _num;
+ }
+
+ int used() const {
+ return _outof - _num;
+ }
+
+ int outof() const { return _outof; }
+
+ 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 : boost::noncopyable {
+ public:
+ ThreadSafeString( size_t size=256 )
+ : _size( size ) , _buf( new char[size] ) {
+ 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;
+ }
+
+ string toString() 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 ) != 0;
+ }
+
+ bool empty() const {
+ return _buf == 0 || _buf[0] == 0;
+ }
+
+ private:
+ size_t _size;
+ char * _buf;
+ };
+
+ ostream& operator<<( ostream &s, const ThreadSafeString &o );
+
+ /** A generic pointer type for function arguments.
+ * It will convert from any pointer type except auto_ptr.
+ * Semantics are the same as passing the pointer returned from get()
+ * const ptr<T> => T * const
+ * ptr<const T> => T const * or const T*
+ */
+ template <typename T>
+ struct ptr {
+
+ ptr() : _p(NULL) {}
+
+ // convert to ptr<T>
+ ptr(T* p) : _p(p) {} // needed for NULL
+ template<typename U> ptr(U* p) : _p(p) {}
+ template<typename U> ptr(const ptr<U>& p) : _p(p) {}
+ template<typename U> ptr(const boost::shared_ptr<U>& p) : _p(p.get()) {}
+ template<typename U> ptr(const boost::scoped_ptr<U>& p) : _p(p.get()) {}
+ //template<typename U> ptr(const auto_ptr<U>& p) : _p(p.get()) {}
+
+ // assign to ptr<T>
+ ptr& operator= (T* p) { _p = p; return *this; } // needed for NULL
+ template<typename U> ptr& operator= (U* p) { _p = p; return *this; }
+ template<typename U> ptr& operator= (const ptr<U>& p) { _p = p; return *this; }
+ template<typename U> ptr& operator= (const boost::shared_ptr<U>& p) { _p = p.get(); return *this; }
+ template<typename U> ptr& operator= (const boost::scoped_ptr<U>& p) { _p = p.get(); return *this; }
+ //template<typename U> ptr& operator= (const auto_ptr<U>& p) { _p = p.get(); return *this; }
+
+ // use
+ T* operator->() const { return _p; }
+ T& operator*() const { return *_p; }
+
+ // convert from ptr<T>
+ operator T* () const { return _p; }
+
+ private:
+ T* _p;
+ };
+
+
+
+ using boost::shared_ptr;
+ using boost::scoped_ptr;
+ using boost::scoped_array;
+ using boost::intrusive_ptr;
+ using boost::bad_lexical_cast;
+ using boost::dynamic_pointer_cast;
+} // namespace mongo
diff --git a/src/mongo/util/hashtab.h b/src/mongo/util/hashtab.h
new file mode 100644
index 00000000000..f1a33068e07
--- /dev/null
+++ b/src/mongo/util/hashtab.h
@@ -0,0 +1,179 @@
+/* hashtab.h
+
+ Simple, fixed size hash table. Darn simple.
+
+ Uses a contiguous block of memory, so you can put it in a memory mapped file very easily.
+*/
+
+/* 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 "../pch.h"
+#include <map>
+#include "../db/dur.h"
+
+namespace mongo {
+
+#pragma pack(1)
+
+ /* you should define:
+
+ int Key::hash() return > 0 always.
+ */
+
+ template <
+ class Key,
+ class Type
+ >
+ class HashTable : boost::noncopyable {
+ public:
+ const char *name;
+ struct Node {
+ int hash;
+ Key k;
+ Type value;
+ bool inUse() {
+ return hash != 0;
+ }
+ void setUnused() {
+ hash = 0;
+ }
+ };
+ void* _buf;
+ int n; // number of hashtable buckets
+ int maxChain;
+
+ Node& nodes(int i) {
+ Node *nodes = (Node *) _buf;
+ return nodes[i];
+ }
+
+ int _find(const Key& k, bool& found) {
+ found = false;
+ int h = k.hash();
+ int i = h % n;
+ int start = i;
+ int chain = 0;
+ int firstNonUsed = -1;
+ while ( 1 ) {
+ if ( !nodes(i).inUse() ) {
+ if ( firstNonUsed < 0 )
+ firstNonUsed = i;
+ }
+
+ if ( nodes(i).hash == h && nodes(i).k == k ) {
+ if ( chain >= 200 )
+ out() << "warning: hashtable " << name << " long chain " << endl;
+ found = true;
+ return i;
+ }
+ chain++;
+ i = (i+1) % n;
+ if ( i == start ) {
+ // shouldn't get here / defensive for infinite loops
+ out() << "error: hashtable " << name << " is full n:" << n << endl;
+ return -1;
+ }
+ if( chain >= maxChain ) {
+ if ( firstNonUsed >= 0 )
+ return firstNonUsed;
+ out() << "error: hashtable " << name << " max chain reached:" << maxChain << endl;
+ return -1;
+ }
+ }
+ }
+
+ public:
+ /* buf must be all zeroes on initialization. */
+ HashTable(void* buf, int buflen, const char *_name) : name(_name) {
+ int m = sizeof(Node);
+ // out() << "hashtab init, buflen:" << buflen << " m:" << m << endl;
+ n = buflen / m;
+ if ( (n & 1) == 0 )
+ n--;
+ maxChain = (int) (n * 0.05);
+ _buf = buf;
+ //nodes = (Node *) buf;
+
+ if ( sizeof(Node) != 628 ) {
+ out() << "HashTable() " << _name << " sizeof(node):" << sizeof(Node) << " n:" << n << " sizeof(Key): " << sizeof(Key) << " sizeof(Type):" << sizeof(Type) << endl;
+ assert( sizeof(Node) == 628 );
+ }
+
+ }
+
+ Type* get(const Key& k) {
+ bool found;
+ int i = _find(k, found);
+ if ( found )
+ return &nodes(i).value;
+ return 0;
+ }
+
+ void kill(const Key& k) {
+ bool found;
+ int i = _find(k, found);
+ if ( i >= 0 && found ) {
+ Node* n = &nodes(i);
+ n = getDur().writing(n);
+ n->k.kill();
+ n->setUnused();
+ }
+ }
+
+ /** returns false if too full */
+ bool put(const Key& k, const Type& value) {
+ bool found;
+ int i = _find(k, found);
+ if ( i < 0 )
+ return false;
+ Node* n = getDur().writing( &nodes(i) );
+ if ( !found ) {
+ n->k = k;
+ n->hash = k.hash();
+ }
+ else {
+ assert( n->hash == k.hash() );
+ }
+ n->value = value;
+ return true;
+ }
+
+ typedef void (*IteratorCallback)( const Key& k , Type& v );
+ void iterAll( IteratorCallback callback ) {
+ for ( int i=0; i<n; i++ ) {
+ if ( nodes(i).inUse() ) {
+ callback( nodes(i).k , nodes(i).value );
+ }
+ }
+ }
+
+ // TODO: should probably use boost::bind for this, but didn't want to look at it
+ typedef void (*IteratorCallback2)( const Key& k , Type& v , void * extra );
+ void iterAll( IteratorCallback2 callback , void * extra ) {
+ for ( int i=0; i<n; i++ ) {
+ if ( nodes(i).inUse() ) {
+ callback( nodes(i).k , nodes(i).value , extra );
+ }
+ }
+ }
+
+ };
+
+#pragma pack()
+
+} // namespace mongo
diff --git a/src/mongo/util/heapcheck.h b/src/mongo/util/heapcheck.h
new file mode 100644
index 00000000000..95da9538db5
--- /dev/null
+++ b/src/mongo/util/heapcheck.h
@@ -0,0 +1,33 @@
+// @file heapcheck.h
+
+/**
+* Copyright (C) 2010 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#if defined(HEAP_CHECKING)
+
+#include <google/heap-checker.h>
+
+#define IGNORE_OBJECT( a ) HeapLeakChecker::IgnoreObject( a )
+#define UNIGNORE_OBJECT( a ) HeapLeakChecker::UnIgnoreObject( a )
+
+#else
+
+#define IGNORE_OBJECT( a )
+#define UNIGNORE_OBJECT( a )
+
+#endif
diff --git a/src/mongo/util/hex.h b/src/mongo/util/hex.h
new file mode 100644
index 00000000000..8cf30f2d9d3
--- /dev/null
+++ b/src/mongo/util/hex.h
@@ -0,0 +1,67 @@
+// util/hex.h
+
+/* 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
+
+namespace mongo {
+ //can't use hex namespace because it conflicts with hex iostream function
+ inline int fromHex( char c ) {
+ if ( '0' <= c && c <= '9' )
+ return c - '0';
+ if ( 'a' <= c && c <= 'f' )
+ return c - 'a' + 10;
+ if ( 'A' <= c && c <= 'F' )
+ return c - 'A' + 10;
+ assert( false );
+ return 0xff;
+ }
+ inline char fromHex( const char *c ) {
+ return (char)(( fromHex( c[ 0 ] ) << 4 ) | fromHex( c[ 1 ] ));
+ }
+
+ inline string toHex(const void* inRaw, int len) {
+ static const char hexchars[] = "0123456789ABCDEF";
+
+ StringBuilder out;
+ const char* in = reinterpret_cast<const char*>(inRaw);
+ for (int i=0; i<len; ++i) {
+ char c = in[i];
+ char hi = hexchars[(c & 0xF0) >> 4];
+ char lo = hexchars[(c & 0x0F)];
+
+ out << hi << lo;
+ }
+
+ return out.str();
+ }
+
+ inline string toHexLower(const void* inRaw, int len) {
+ static const char hexchars[] = "0123456789abcdef";
+
+ StringBuilder out;
+ const char* in = reinterpret_cast<const char*>(inRaw);
+ for (int i=0; i<len; ++i) {
+ char c = in[i];
+ char hi = hexchars[(c & 0xF0) >> 4];
+ char lo = hexchars[(c & 0x0F)];
+
+ out << hi << lo;
+ }
+
+ return out.str();
+ }
+}
diff --git a/src/mongo/util/histogram.cpp b/src/mongo/util/histogram.cpp
new file mode 100644
index 00000000000..17a85059d58
--- /dev/null
+++ b/src/mongo/util/histogram.cpp
@@ -0,0 +1,131 @@
+// histogram.cc
+
+/**
+* Copyright (C) 2010 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <iomanip>
+#include <limits>
+#include <sstream>
+
+#include "histogram.h"
+
+namespace mongo {
+
+ using std::ostringstream;
+ using std::setfill;
+ using std::setw;
+
+ Histogram::Histogram( const Options& opts )
+ : _initialValue( opts.initialValue )
+ , _numBuckets( opts.numBuckets )
+ , _boundaries( new uint32_t[_numBuckets] )
+ , _buckets( new uint64_t[_numBuckets] ) {
+
+ // TODO more sanity checks
+ // + not too few buckets
+ // + initialBucket and bucketSize fit within 32 bit ints
+
+ // _boundaries store the maximum value falling in that bucket.
+ if ( opts.exponential ) {
+ uint32_t twoPow = 1; // 2^0
+ for ( uint32_t i = 0; i < _numBuckets - 1; i++) {
+ _boundaries[i] = _initialValue + opts.bucketSize * twoPow;
+ twoPow *= 2; // 2^i+1
+ }
+ }
+ else {
+ _boundaries[0] = _initialValue + opts.bucketSize;
+ for ( uint32_t i = 1; i < _numBuckets - 1; i++ ) {
+ _boundaries[i] = _boundaries[ i-1 ] + opts.bucketSize;
+ }
+ }
+ _boundaries[ _numBuckets-1 ] = std::numeric_limits<uint32_t>::max();
+
+ for ( uint32_t i = 0; i < _numBuckets; i++ ) {
+ _buckets[i] = 0;
+ }
+ }
+
+ Histogram::~Histogram() {
+ delete [] _boundaries;
+ delete [] _buckets;
+ }
+
+ void Histogram::insert( uint32_t element ) {
+ if ( element < _initialValue) return;
+
+ _buckets[ _findBucket(element) ] += 1;
+ }
+
+ string Histogram::toHTML() const {
+ uint64_t max = 0;
+ for ( uint32_t i = 0; i < _numBuckets; i++ ) {
+ if ( _buckets[i] > max ) {
+ max = _buckets[i];
+ }
+ }
+ if ( max == 0 ) {
+ return "histogram is empty\n";
+ }
+
+ // normalize buckets to max
+ const int maxBar = 20;
+ ostringstream ss;
+ for ( uint32_t i = 0; i < _numBuckets; i++ ) {
+ int barSize = _buckets[i] * maxBar / max;
+ ss << string( barSize,'*' )
+ << setfill(' ') << setw( maxBar-barSize + 12 )
+ << _boundaries[i] << '\n';
+ }
+
+ return ss.str();
+ }
+
+ uint64_t Histogram::getCount( uint32_t bucket ) const {
+ if ( bucket >= _numBuckets ) return 0;
+
+ return _buckets[ bucket ];
+ }
+
+ uint32_t Histogram::getBoundary( uint32_t bucket ) const {
+ if ( bucket >= _numBuckets ) return 0;
+
+ return _boundaries[ bucket ];
+ }
+
+ uint32_t Histogram::getBucketsNum() const {
+ return _numBuckets;
+ }
+
+ uint32_t Histogram::_findBucket( uint32_t element ) const {
+ // TODO assert not too small a value?
+
+ uint32_t low = 0;
+ uint32_t high = _numBuckets - 1;
+ while ( low < high ) {
+ // low + ( (high - low) / 2 );
+ uint32_t mid = ( low + high ) >> 1;
+ if ( element > _boundaries[ mid ] ) {
+ low = mid + 1;
+ }
+ else {
+ high = mid;
+ }
+ }
+ return low;
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/histogram.h b/src/mongo/util/histogram.h
new file mode 100644
index 00000000000..40ec5628dda
--- /dev/null
+++ b/src/mongo/util/histogram.h
@@ -0,0 +1,128 @@
+// histogram.h
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef UTIL_HISTOGRAM_HEADER
+#define UTIL_HISTOGRAM_HEADER
+
+#include "../pch.h"
+
+#include <string>
+
+namespace mongo {
+
+ using std::string;
+
+ /**
+ * A histogram for a 32-bit integer range.
+ */
+ class Histogram {
+ public:
+ /**
+ * Construct a histogram with 'numBuckets' buckets, optionally
+ * having the first bucket start at 'initialValue' rather than
+ * 0. By default, the histogram buckets will be 'bucketSize' wide.
+ *
+ * Usage example:
+ * Histogram::Options opts;
+ * opts.numBuckets = 3;
+ * opts.bucketSize = 10;
+ * Histogram h( opts );
+ *
+ * Generates the bucket ranges [0..10],[11..20],[21..max_int]
+ *
+ * Alternatively, the flag 'exponential' could be turned on, in
+ * which case a bucket's maximum value will be
+ * initialValue + bucketSize * 2 ^ [0..numBuckets-1]
+ *
+ * Usage example:
+ * Histogram::Options opts;
+ * opts.numBuckets = 4;
+ * opts.bucketSize = 125;
+ * opts.exponential = true;
+ * Histogram h( opts );
+ *
+ * Generates the bucket ranges [0..125],[126..250],[251..500],[501..max_int]
+ */
+ struct Options {
+ boost::uint32_t numBuckets;
+ boost::uint32_t bucketSize;
+ boost::uint32_t initialValue;
+
+ // use exponential buckets?
+ bool exponential;
+
+ Options()
+ : numBuckets(0)
+ , bucketSize(0)
+ , initialValue(0)
+ , exponential(false) {}
+ };
+ explicit Histogram( const Options& opts );
+ ~Histogram();
+
+ /**
+ * Find the bucket that 'element' falls into and increment its count.
+ */
+ void insert( boost::uint32_t element );
+
+ /**
+ * Render the histogram as string that can be used inside an
+ * HTML doc.
+ */
+ string toHTML() const;
+
+ // testing interface below -- consider it private
+
+ /**
+ * Return the count for the 'bucket'-th bucket.
+ */
+ boost::uint64_t getCount( boost::uint32_t bucket ) const;
+
+ /**
+ * Return the maximum element that would fall in the
+ * 'bucket'-th bucket.
+ */
+ boost::uint32_t getBoundary( boost::uint32_t bucket ) const;
+
+ /**
+ * Return the number of buckets in this histogram.
+ */
+ boost::uint32_t getBucketsNum() const;
+
+ private:
+ /**
+ * Returns the bucket where 'element' should fall
+ * into. Currently assumes that 'element' is greater than the
+ * minimum 'inialValue'.
+ */
+ boost::uint32_t _findBucket( boost::uint32_t element ) const;
+
+ boost::uint32_t _initialValue; // no value lower than it is recorded
+ boost::uint32_t _numBuckets; // total buckets in the histogram
+
+ // all below owned here
+ boost::uint32_t* _boundaries; // maximum element of each bucket
+ boost::uint64_t* _buckets; // current count of each bucket
+
+ Histogram( const Histogram& );
+ Histogram& operator=( const Histogram& );
+ };
+
+} // namespace mongo
+
+#endif // UTIL_HISTOGRAM_HEADER
diff --git a/src/mongo/util/intrusive_counter.cpp b/src/mongo/util/intrusive_counter.cpp
new file mode 100755
index 00000000000..fc01f40b41a
--- /dev/null
+++ b/src/mongo/util/intrusive_counter.cpp
@@ -0,0 +1,30 @@
+/**
+ * Copyright (c) 2011 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "util/intrusive_counter.h"
+
+namespace mongo {
+
+ void IntrusiveCounterUnsigned::addRef() const {
+ ++counter;
+ }
+
+ void IntrusiveCounterUnsigned::release() const {
+ if (!--counter)
+ delete this;
+ }
+
+}
diff --git a/src/mongo/util/intrusive_counter.h b/src/mongo/util/intrusive_counter.h
new file mode 100755
index 00000000000..bcebb6288cf
--- /dev/null
+++ b/src/mongo/util/intrusive_counter.h
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) 2011 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <boost/intrusive_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+namespace mongo {
+
+/*
+ IntrusiveCounter is a sharable implementation of a reference counter that
+ objects can use to be compatible with boost::intrusive_ptr<>.
+
+ Some objects that use IntrusiveCounter are immutable, and only have
+ const methods. This may require their pointers to be declared as
+ intrusive_ptr<const ClassName> . In order to be able to share pointers to
+ these immutables, the methods associated with IntrusiveCounter are declared
+ as const, and the counter itself is marked as mutable.
+
+ IntrusiveCounter itself is abstract, allowing for multiple implementations.
+ For example, IntrusiveCounterUnsigned uses ordinary unsigned integers for
+ the reference count, and is good for situations where thread safety is not
+ required. For others, other implementations using atomic integers should
+ be used. For static objects, the implementations of addRef() and release()
+ can be overridden to do nothing.
+ */
+ class IntrusiveCounter :
+ boost::noncopyable {
+ public:
+ virtual ~IntrusiveCounter() {};
+
+ // these are here for the boost intrusive_ptr<> class
+ friend inline void intrusive_ptr_add_ref(const IntrusiveCounter *pIC) {
+ pIC->addRef(); };
+ friend inline void intrusive_ptr_release(const IntrusiveCounter *pIC) {
+ pIC->release(); };
+
+ virtual void addRef() const = 0;
+ virtual void release() const = 0;
+ };
+
+ class IntrusiveCounterUnsigned :
+ public IntrusiveCounter {
+ public:
+ // virtuals from IntrusiveCounter
+ virtual void addRef() const;
+ virtual void release() const;
+
+ IntrusiveCounterUnsigned();
+
+ private:
+ mutable unsigned counter;
+ };
+
+};
+
+/* ======================= INLINED IMPLEMENTATIONS ========================== */
+
+namespace mongo {
+
+ inline IntrusiveCounterUnsigned::IntrusiveCounterUnsigned():
+ counter(0) {
+ }
+
+};
diff --git a/src/mongo/util/log.cpp b/src/mongo/util/log.cpp
new file mode 100644
index 00000000000..aa249597b57
--- /dev/null
+++ b/src/mongo/util/log.cpp
@@ -0,0 +1,197 @@
+/** @file log.cpp
+ */
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "assert_util.h"
+#include "assert.h"
+#include <cmath>
+#include "time_support.h"
+using namespace std;
+
+#ifdef _WIN32
+# include <io.h>
+# include <fcntl.h>
+#else
+# include <cxxabi.h>
+# include <sys/file.h>
+#endif
+
+#ifdef _WIN32
+# define dup2 _dup2 // Microsoft headers use ISO C names
+# define fileno _fileno
+#endif
+
+namespace mongo {
+
+ Nullstream nullstream;
+ vector<Tee*>* Logstream::globalTees = 0;
+
+ thread_specific_ptr<Logstream> Logstream::tsp;
+
+ class LoggingManager {
+ public:
+ LoggingManager()
+ : _enabled(0) , _file(0) {
+ }
+
+ void start( const string& lp , bool append ) {
+ uassert( 10268 , "LoggingManager already started" , ! _enabled );
+ _append = append;
+
+ bool exists = boost::filesystem::exists(lp);
+ bool isdir = boost::filesystem::is_directory(lp);
+ bool isreg = boost::filesystem::is_regular_file(lp);
+
+ if ( exists ) {
+ if ( isdir ) {
+ cout << "logpath [" << lp << "] should be a filename, not a directory" << endl;
+
+ dbexit( EXIT_BADOPTIONS );
+ assert( 0 );
+ }
+
+ if ( ! append ) {
+ // only attempt rename if log is regular file
+ if ( isreg ) {
+ stringstream ss;
+ ss << lp << "." << terseCurrentTime( false );
+ string s = ss.str();
+
+ if ( ! rename( lp.c_str() , s.c_str() ) ) {
+ cout << "log file [" << lp << "] exists; copied to temporary file [" << s << "]" << endl;
+ } else {
+ cout << "log file [" << lp << "] exists and couldn't make backup; run with --logappend or manually remove file (" << strerror(errno) << ")" << endl;
+
+ dbexit( EXIT_BADOPTIONS );
+ assert( 0 );
+ }
+ }
+ }
+ }
+ // test path
+ FILE * test = fopen( lp.c_str() , _append ? "a" : "w" );
+ if ( ! test ) {
+ cout << "can't open [" << lp << "] for log file: " << errnoWithDescription() << endl;
+ dbexit( EXIT_BADOPTIONS );
+ assert( 0 );
+ }
+
+ if (append && exists){
+ // two blank lines before and after
+ const string msg = "\n\n***** SERVER RESTARTED *****\n\n\n";
+ massert(14036, errnoWithPrefix("couldn't write to log file"),
+ fwrite(msg.data(), 1, msg.size(), test) == msg.size());
+ }
+
+ fclose( test );
+
+ _path = lp;
+ _enabled = 1;
+ rotate();
+ }
+
+ void rotate() {
+ if ( ! _enabled ) {
+ cout << "LoggingManager not enabled" << endl;
+ return;
+ }
+
+ if ( _file ) {
+
+#ifdef POSIX_FADV_DONTNEED
+ posix_fadvise(fileno(_file), 0, 0, POSIX_FADV_DONTNEED);
+#endif
+
+ // Rename the (open) existing log file to a timestamped name
+ stringstream ss;
+ ss << _path << "." << terseCurrentTime( false );
+ string s = ss.str();
+ rename( _path.c_str() , s.c_str() );
+ }
+
+ FILE* tmp = 0; // The new file using the original logpath name
+
+#if _WIN32
+ // We rename an open log file (above, on next rotation) and the trick to getting Windows to do that is
+ // to open the file with FILE_SHARE_DELETE. So, we can't use the freopen() call that non-Windows
+ // versions use because it would open the file without the FILE_SHARE_DELETE flag we need.
+ //
+ HANDLE newFileHandle = CreateFileA(
+ _path.c_str(),
+ GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+ if ( INVALID_HANDLE_VALUE != newFileHandle ) {
+ int newFileDescriptor = _open_osfhandle( reinterpret_cast<intptr_t>(newFileHandle), _O_APPEND );
+ tmp = _fdopen( newFileDescriptor, _append ? "a" : "w" );
+ }
+#else
+ tmp = freopen(_path.c_str(), _append ? "a" : "w", stdout);
+#endif
+ if ( !tmp ) {
+ cerr << "can't open: " << _path.c_str() << " for log file" << endl;
+ dbexit( EXIT_BADOPTIONS );
+ assert( 0 );
+ }
+
+ // redirect stdout and stderr to log file
+ dup2( fileno( tmp ), 1 ); // stdout
+ dup2( fileno( tmp ), 2 ); // stderr
+
+ Logstream::setLogFile(tmp); // after this point no thread will be using old file
+
+#if _WIN32
+ if ( _file )
+ fclose( _file ); // In Windows, we still have the old file open, close it now
+#endif
+
+#if 0 // enable to test redirection
+ cout << "written to cout" << endl;
+ cerr << "written to cerr" << endl;
+ log() << "written to log()" << endl;
+#endif
+
+ _file = tmp; // Save new file for next rotation
+ }
+
+ private:
+ bool _enabled;
+ string _path;
+ bool _append;
+ FILE * _file;
+
+ } loggingManager;
+
+ void initLogging( const string& lp , bool append ) {
+ cout << "all output going to: " << lp << endl;
+ loggingManager.start( lp , append );
+ }
+
+ void rotateLogs( int signal ) {
+ loggingManager.rotate();
+ }
+
+ // done *before* static initialization
+ FILE* Logstream::logfile = stdout;
+ bool Logstream::isSyslog = false;
+
+}
diff --git a/src/mongo/util/log.h b/src/mongo/util/log.h
new file mode 100644
index 00000000000..a393d4d29a5
--- /dev/null
+++ b/src/mongo/util/log.h
@@ -0,0 +1,581 @@
+// @file log.h
+
+/* 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 <string.h>
+#include <errno.h>
+#include "../bson/util/builder.h"
+
+#ifndef _WIN32
+#include <syslog.h>
+#endif
+
+namespace mongo {
+
+ enum LogLevel { LL_DEBUG , LL_INFO , LL_NOTICE , LL_WARNING , LL_ERROR , LL_SEVERE };
+
+ inline const char * logLevelToString( LogLevel l ) {
+ switch ( l ) {
+ case LL_DEBUG:
+ case LL_INFO:
+ case LL_NOTICE:
+ return "";
+ case LL_WARNING:
+ return "warning" ;
+ case LL_ERROR:
+ return "ERROR";
+ case LL_SEVERE:
+ return "SEVERE";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+#ifndef _WIN32
+ inline const int logLevelToSysLogLevel( LogLevel l) {
+ switch ( l ) {
+ case LL_DEBUG:
+ return LOG_DEBUG;
+ case LL_INFO:
+ return LOG_INFO;
+ case LL_NOTICE:
+ return LOG_NOTICE;
+ case LL_WARNING:
+ return LOG_WARNING;
+ case LL_ERROR:
+ return LOG_ERR;
+ case LL_SEVERE:
+ return LOG_CRIT;
+ default:
+ return LL_INFO;
+ }
+ }
+#endif
+
+ class LabeledLevel {
+ public:
+
+ LabeledLevel( int level ) : _level( level ) {}
+ LabeledLevel( const char* label, int level ) : _label( label ), _level( level ) {}
+ LabeledLevel( const string& label, int level ) : _label( label ), _level( level ) {}
+
+ LabeledLevel operator+( int i ) const {
+ return LabeledLevel( _label, _level + i );
+ }
+
+ LabeledLevel operator+( const char* label ) const {
+ if( _label == "" )
+ return LabeledLevel( label, _level );
+ return LabeledLevel( _label + string("::") + label, _level );
+ }
+
+ LabeledLevel operator+( string& label ) const {
+ return LabeledLevel( _label + string("::") + label, _level );
+ }
+
+ LabeledLevel operator-( int i ) const {
+ return LabeledLevel( _label, _level - i );
+ }
+
+ const string& getLabel() const { return _label; }
+ int getLevel() const { return _level; }
+
+ private:
+ string _label;
+ int _level;
+ };
+
+ class LazyString {
+ public:
+ virtual ~LazyString() {}
+ virtual string val() const = 0;
+ };
+
+ // Utility class for stringifying object only when val() called.
+ template< class T >
+ class LazyStringImpl : public LazyString {
+ public:
+ LazyStringImpl( const T &t ) : t_( t ) {}
+ virtual string val() const { return t_.toString(); }
+ private:
+ const T& t_;
+ };
+
+ class Tee {
+ public:
+ virtual ~Tee() {}
+ virtual void write(LogLevel level , const string& str) = 0;
+ };
+
+ class Nullstream {
+ public:
+ virtual Nullstream& operator<< (Tee* tee) {
+ return *this;
+ }
+ virtual ~Nullstream() {}
+ virtual Nullstream& operator<<(const char *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const string& ) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const StringData& ) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(char *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(char) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(int) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(ExitCode) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned short) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(double) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(void *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const void *) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(long long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(unsigned long long) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(bool) {
+ return *this;
+ }
+ virtual Nullstream& operator<<(const LazyString&) {
+ return *this;
+ }
+ template< class T >
+ Nullstream& operator<<(T *t) {
+ return operator<<( static_cast<void*>( t ) );
+ }
+ template< class T >
+ Nullstream& operator<<(const T *t) {
+ return operator<<( static_cast<const void*>( t ) );
+ }
+ template< class T >
+ Nullstream& operator<<(const shared_ptr<T> p ) {
+ T * t = p.get();
+ if ( ! t )
+ *this << "null";
+ else
+ *this << *t;
+ return *this;
+ }
+ template< class T >
+ Nullstream& operator<<(const T &t) {
+ return operator<<( static_cast<const LazyString&>( LazyStringImpl< T >( t ) ) );
+ }
+
+ virtual Nullstream& operator<< (ostream& ( *endl )(ostream&)) {
+ return *this;
+ }
+ virtual Nullstream& operator<< (ios_base& (*hex)(ios_base&)) {
+ return *this;
+ }
+
+ virtual void flush(Tee *t = 0) {}
+ };
+ extern Nullstream nullstream;
+
+ class Logstream : public Nullstream {
+ static mongo::mutex mutex;
+ static int doneSetup;
+ stringstream ss;
+ int indent;
+ LogLevel logLevel;
+ static FILE* logfile;
+ static boost::scoped_ptr<ostream> stream;
+ static vector<Tee*> * globalTees;
+ static bool isSyslog;
+ public:
+ inline static void logLockless( const StringData& s );
+
+ static void setLogFile(FILE* f) {
+ scoped_lock lk(mutex);
+ logfile = f;
+ }
+#ifndef _WIN32
+ static void useSyslog(const char * name) {
+ cout << "using syslog ident: " << name << endl;
+
+ // openlog requires heap allocated non changing pointer
+ // this should only be called once per pragram execution
+
+ char * newName = (char *) malloc( strlen(name) + 1 );
+ strcpy( newName , name);
+ openlog( newName , LOG_ODELAY , LOG_USER );
+ isSyslog = true;
+ }
+#endif
+
+ static int magicNumber() {
+ return 1717;
+ }
+
+ static int getLogDesc() {
+ int fd = -1;
+ if (logfile != NULL)
+#if defined(_WIN32)
+ // the ISO C++ conformant name is _fileno
+ fd = _fileno( logfile );
+#else
+ fd = fileno( logfile );
+#endif
+ return fd;
+ }
+
+ inline void flush(Tee *t = 0);
+
+ inline Nullstream& setLogLevel(LogLevel l) {
+ logLevel = l;
+ return *this;
+ }
+
+ /** note these are virtual */
+ Logstream& operator<<(const char *x) { ss << x; return *this; }
+ Logstream& operator<<(const string& x) { ss << x; return *this; }
+ Logstream& operator<<(const StringData& x) { ss << x.data(); return *this; }
+ Logstream& operator<<(char *x) { ss << x; return *this; }
+ Logstream& operator<<(char x) { ss << x; return *this; }
+ Logstream& operator<<(int x) { ss << x; return *this; }
+ Logstream& operator<<(ExitCode x) { ss << x; return *this; }
+ Logstream& operator<<(long x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned long x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned short x){ ss << x; return *this; }
+ Logstream& operator<<(double x) { ss << x; return *this; }
+ Logstream& operator<<(void *x) { ss << x; return *this; }
+ Logstream& operator<<(const void *x) { ss << x; return *this; }
+ Logstream& operator<<(long long x) { ss << x; return *this; }
+ Logstream& operator<<(unsigned long long x) { ss << x; return *this; }
+ Logstream& operator<<(bool x) { ss << x; return *this; }
+
+ Logstream& operator<<(const LazyString& x) {
+ ss << x.val();
+ return *this;
+ }
+ Nullstream& operator<< (Tee* tee) {
+ ss << '\n';
+ flush(tee);
+ return *this;
+ }
+ Logstream& operator<< (ostream& ( *_endl )(ostream&)) {
+ ss << '\n';
+ flush(0);
+ return *this;
+ }
+ Logstream& operator<< (ios_base& (*_hex)(ios_base&)) {
+ ss << _hex;
+ return *this;
+ }
+
+ Logstream& prolog() {
+ return *this;
+ }
+
+ void addGlobalTee( Tee * t ) {
+ if ( ! globalTees )
+ globalTees = new vector<Tee*>();
+ globalTees->push_back( t );
+ }
+
+ void indentInc(){ indent++; }
+ void indentDec(){ indent--; }
+ int getIndent() const { return indent; }
+
+ private:
+ static thread_specific_ptr<Logstream> tsp;
+ Logstream() {
+ indent = 0;
+ _init();
+ }
+ void _init() {
+ ss.str("");
+ logLevel = LL_INFO;
+ }
+ public:
+ static Logstream& get() {
+ if ( StaticObserver::_destroyingStatics ) {
+ cout << "Logstream::get called in uninitialized state" << endl;
+ }
+ Logstream *p = tsp.get();
+ if( p == 0 )
+ tsp.reset( p = new Logstream() );
+ return *p;
+ }
+ };
+
+ extern int logLevel;
+ extern int tlogLevel;
+
+ inline Nullstream& out( int level = 0 ) {
+ if ( level > logLevel )
+ return nullstream;
+ return Logstream::get();
+ }
+
+ /* flush the log stream if the log level is
+ at the specified level or higher. */
+ inline void logflush(int level = 0) {
+ if( level > logLevel )
+ Logstream::get().flush(0);
+ }
+
+ /* without prolog */
+ inline Nullstream& _log( int level = 0 ) {
+ if ( level > logLevel )
+ return nullstream;
+ return Logstream::get();
+ }
+
+ /** logging which we may not want during unit tests (dbtests) runs.
+ set tlogLevel to -1 to suppress tlog() output in a test program. */
+ inline Nullstream& tlog( int level = 0 ) {
+ if ( level > tlogLevel || level > logLevel )
+ return nullstream;
+ return Logstream::get().prolog();
+ }
+
+ // log if debug build or if at a certain level
+ inline Nullstream& dlog( int level ) {
+ if ( level <= logLevel || DEBUG_BUILD )
+ return Logstream::get().prolog();
+ return nullstream;
+ }
+
+ inline Nullstream& log( int level ) {
+ if ( level > logLevel )
+ return nullstream;
+ return Logstream::get().prolog();
+ }
+
+#define MONGO_LOG(level) if ( MONGO_likely(logLevel < (level)) ) { } else log( level )
+#define LOG MONGO_LOG
+
+ inline Nullstream& log( LogLevel l ) {
+ return Logstream::get().prolog().setLogLevel( l );
+ }
+
+ inline Nullstream& log( const LabeledLevel& ll ) {
+ Nullstream& stream = log( ll.getLevel() );
+ if( ll.getLabel() != "" )
+ stream << "[" << ll.getLabel() << "] ";
+ return stream;
+ }
+
+ inline Nullstream& log() {
+ return Logstream::get().prolog();
+ }
+
+ inline Nullstream& error() {
+ return log( LL_ERROR );
+ }
+
+ inline Nullstream& warning() {
+ return log( LL_WARNING );
+ }
+
+ /* default impl returns "" -- mongod overrides */
+ extern const char * (*getcurns)();
+
+ inline Nullstream& problem( int level = 0 ) {
+ if ( level > logLevel )
+ return nullstream;
+ Logstream& l = Logstream::get().prolog();
+ l << ' ' << getcurns() << ' ';
+ return l;
+ }
+
+ /**
+ log to a file rather than stdout
+ defined in assert_util.cpp
+ */
+ void initLogging( const string& logpath , bool append );
+ void rotateLogs( int signal = 0 );
+
+ std::string toUtf8String(const std::wstring& wide);
+
+#if defined(_WIN32)
+ inline string errnoWithDescription(DWORD x = GetLastError()) {
+#else
+ inline string errnoWithDescription(int x = errno) {
+#endif
+ stringstream s;
+ s << "errno:" << x << ' ';
+
+#if defined(_WIN32)
+ LPTSTR errorText = NULL;
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM
+ |FORMAT_MESSAGE_ALLOCATE_BUFFER
+ |FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ x, 0,
+ (LPTSTR) &errorText, // output
+ 0, // minimum size for output buffer
+ NULL);
+ if( errorText ) {
+ string x = toUtf8String(errorText);
+ for( string::iterator i = x.begin(); i != x.end(); i++ ) {
+ if( *i == '\n' || *i == '\r' )
+ break;
+ s << *i;
+ }
+ LocalFree(errorText);
+ }
+ else
+ s << strerror(x);
+ /*
+ DWORD n = FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, x,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf, 0, NULL);
+ */
+#else
+ s << strerror(x);
+#endif
+ return s.str();
+ }
+
+ /** output the error # and error message with prefix.
+ handy for use as parm in uassert/massert.
+ */
+ string errnoWithPrefix( const char * prefix );
+
+ void Logstream::logLockless( const StringData& s ) {
+ if ( s.size() == 0 )
+ return;
+
+ if ( doneSetup == 1717 ) {
+#ifndef _WIN32
+ if ( isSyslog ) {
+ syslog( LOG_INFO , "%s" , s.data() );
+ } else
+#endif
+ if (fwrite(s.data(), s.size(), 1, logfile)) {
+ fflush(logfile);
+ }
+ else {
+ int x = errno;
+ cout << "Failed to write to logfile: " << errnoWithDescription(x) << endl;
+ }
+ }
+ else {
+ cout << s.data();
+ cout.flush();
+ }
+ }
+
+ void Logstream::flush(Tee *t) {
+ // this ensures things are sane
+ if ( doneSetup == 1717 ) {
+ string msg = ss.str();
+ string threadName = getThreadName();
+ const char * type = logLevelToString(logLevel);
+
+ int spaceNeeded = (int)(msg.size() + 64 + threadName.size());
+ int bufSize = 128;
+ while ( bufSize < spaceNeeded )
+ bufSize += 128;
+
+ BufBuilder b(bufSize);
+ time_t_to_String( time(0) , b.grow(20) );
+ if (!threadName.empty()) {
+ b.appendChar( '[' );
+ b.appendStr( threadName , false );
+ b.appendChar( ']' );
+ b.appendChar( ' ' );
+ }
+
+ for ( int i=0; i<indent; i++ )
+ b.appendChar( '\t' );
+
+ if ( type[0] ) {
+ b.appendStr( type , false );
+ b.appendStr( ": " , false );
+ }
+
+ b.appendStr( msg );
+
+ string out( b.buf() , b.len() - 1);
+
+ scoped_lock lk(mutex);
+
+ if( t ) t->write(logLevel,out);
+ if ( globalTees ) {
+ for ( unsigned i=0; i<globalTees->size(); i++ )
+ (*globalTees)[i]->write(logLevel,out);
+ }
+#ifndef _WIN32
+ if ( isSyslog ) {
+ syslog( logLevelToSysLogLevel(logLevel) , "%s" , out.data() );
+ } else
+#endif
+ if(fwrite(out.data(), out.size(), 1, logfile)) {
+ fflush(logfile);
+ }
+ else {
+ int x = errno;
+ cout << "Failed to write to logfile: " << errnoWithDescription(x) << ": " << out << endl;
+ }
+#ifdef POSIX_FADV_DONTNEED
+ // This only applies to pages that have already been flushed
+ RARELY posix_fadvise(fileno(logfile), 0, 0, POSIX_FADV_DONTNEED);
+#endif
+ }
+ _init();
+ }
+
+ struct LogIndentLevel {
+ LogIndentLevel(){
+ Logstream::get().indentInc();
+ }
+ ~LogIndentLevel(){
+ Logstream::get().indentDec();
+ }
+ };
+
+ extern Tee* const warnings; // Things put here go in serverStatus
+
+} // namespace mongo
diff --git a/src/mongo/util/logfile.cpp b/src/mongo/util/logfile.cpp
new file mode 100644
index 00000000000..7c362be08d1
--- /dev/null
+++ b/src/mongo/util/logfile.cpp
@@ -0,0 +1,253 @@
+// @file logfile.cpp simple file log writing / journaling
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+#include "logfile.h"
+#include "text.h"
+#include "mongoutils/str.h"
+#include "unittest.h"
+
+using namespace mongoutils;
+
+namespace mongo {
+ struct LogfileTest : public UnitTest {
+ LogfileTest() { }
+ void run() {
+ if( 0 && debug ) {
+ try {
+ LogFile f("logfile_test");
+ void *p = malloc(16384);
+ char *buf = (char*) p;
+ buf += 4095;
+ buf = (char*) (((size_t)buf)&(~0xfff));
+ memset(buf, 'z', 8192);
+ buf[8190] = '\n';
+ buf[8191] = 'B';
+ buf[0] = 'A';
+ f.synchronousAppend(buf, 8192);
+ f.synchronousAppend(buf, 8192);
+ free(p);
+ }
+ catch(DBException& e ) {
+ log() << "logfile.cpp test failed : " << e.what() << endl;
+ throw;
+ }
+ }
+ }
+ } __test;
+}
+
+#if defined(_WIN32)
+
+namespace mongo {
+
+ LogFile::LogFile(string name, bool readwrite) : _name(name) {
+ _fd = CreateFile(
+ toNativeString(name.c_str()).c_str(),
+ (readwrite?GENERIC_READ:0)|GENERIC_WRITE,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
+ NULL);
+ if( _fd == INVALID_HANDLE_VALUE ) {
+ DWORD e = GetLastError();
+ uasserted(13518, str::stream() << "couldn't open file " << name << " for writing " << errnoWithDescription(e));
+ }
+ SetFilePointer(_fd, 0, 0, FILE_BEGIN);
+ }
+
+ LogFile::~LogFile() {
+ if( _fd != INVALID_HANDLE_VALUE )
+ CloseHandle(_fd);
+ }
+
+ void LogFile::truncate() {
+ verify(15870, _fd != INVALID_HANDLE_VALUE);
+
+ if (!SetEndOfFile(_fd)){
+ msgasserted(15871, "Couldn't truncate file: " + errnoWithDescription());
+ }
+ }
+
+ void LogFile::writeAt(unsigned long long offset, const void *_buf, size_t _len) {
+// TODO 64 bit offsets
+ OVERLAPPED o;
+ memset(&o,0,sizeof(o));
+ (unsigned long long&) o.Offset = offset;
+ BOOL ok= WriteFile(_fd, _buf, _len, 0, &o);
+ assert(ok);
+ }
+
+ void LogFile::readAt(unsigned long long offset, void *_buf, size_t _len) {
+// TODO 64 bit offsets
+ OVERLAPPED o;
+ memset(&o,0,sizeof(o));
+ (unsigned long long&) o.Offset = offset;
+ DWORD nr;
+ BOOL ok = ReadFile(_fd, _buf, _len, &nr, &o);
+ if( !ok ) {
+ string e = errnoWithDescription();
+ //DWORD e = GetLastError();
+ log() << "LogFile readAt(" << offset << ") len:" << _len << "errno:" << e << endl;
+ assert(false);
+ }
+ }
+
+ void LogFile::synchronousAppend(const void *_buf, size_t _len) {
+ const size_t BlockSize = 8 * 1024 * 1024;
+ assert(_fd);
+ assert(_len % 4096 == 0);
+ const char *buf = (const char *) _buf;
+ size_t left = _len;
+ while( left ) {
+ size_t toWrite = min(left, BlockSize);
+ DWORD written;
+ if( !WriteFile(_fd, buf, toWrite, &written, NULL) ) {
+ DWORD e = GetLastError();
+ if( e == 87 )
+ msgasserted(13519, "error 87 appending to file - invalid parameter");
+ else
+ uasserted(13517, str::stream() << "error appending to file " << _name << ' ' << _len << ' ' << toWrite << ' ' << errnoWithDescription(e));
+ }
+ else {
+ dassert( written == toWrite );
+ }
+ left -= written;
+ buf += written;
+ }
+ }
+
+}
+
+#else
+
+/// posix
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "paths.h"
+
+namespace mongo {
+
+ LogFile::LogFile(string name, bool readwrite) : _name(name) {
+ int options = O_CREAT
+ | (readwrite?O_RDWR:O_WRONLY)
+#if defined(O_DIRECT)
+ | O_DIRECT
+#endif
+#if defined(O_NOATIME)
+ | O_NOATIME
+#endif
+ ;
+
+ _fd = open(name.c_str(), options, S_IRUSR | S_IWUSR);
+
+#if defined(O_DIRECT)
+ _direct = true;
+ if( _fd < 0 ) {
+ _direct = false;
+ options &= ~O_DIRECT;
+ _fd = open(name.c_str(), options, S_IRUSR | S_IWUSR);
+ }
+#else
+ _direct = false;
+#endif
+
+ if( _fd < 0 ) {
+ uasserted(13516, str::stream() << "couldn't open file " << name << " for writing " << errnoWithDescription());
+ }
+
+ flushMyDirectory(name);
+ }
+
+ LogFile::~LogFile() {
+ if( _fd >= 0 )
+ close(_fd);
+ _fd = -1;
+ }
+
+ void LogFile::truncate() {
+ verify(15872, _fd >= 0);
+
+ BOOST_STATIC_ASSERT(sizeof(off_t) == 8); // we don't want overflow here
+ const off_t pos = lseek(_fd, 0, SEEK_CUR); // doesn't actually seek
+ if (ftruncate(_fd, pos) != 0){
+ msgasserted(15873, "Couldn't truncate file: " + errnoWithDescription());
+ }
+
+ fsync(_fd);
+ }
+
+ void LogFile::writeAt(unsigned long long offset, const void *buf, size_t len) {
+ assert(((size_t)buf)%4096==0); // aligned
+ ssize_t written = pwrite(_fd, buf, len, offset);
+ if( written != (ssize_t) len ) {
+ log() << "writeAt fails " << errnoWithDescription() << endl;
+ }
+#if defined(__linux__)
+ fdatasync(_fd);
+#else
+ fsync(_fd);
+#endif
+ }
+
+ void LogFile::readAt(unsigned long long offset, void *_buf, size_t _len) {
+ assert(((size_t)_buf)%4096==0); // aligned
+ ssize_t rd = pread(_fd, _buf, _len, offset);
+ assert( rd != -1 );
+ }
+
+ void LogFile::synchronousAppend(const void *b, size_t len) {
+#ifdef POSIX_FADV_DONTNEED
+ const off_t pos = lseek(_fd, 0, SEEK_CUR); // doesn't actually seek, just get current position
+#endif
+
+ const char *buf = (char *) b;
+ assert(_fd);
+ assert(((size_t)buf)%4096==0); // aligned
+ if( len % 4096 != 0 ) {
+ log() << len << ' ' << len % 4096 << endl;
+ assert(false);
+ }
+ ssize_t written = write(_fd, buf, len);
+ if( written != (ssize_t) len ) {
+ log() << "write fails written:" << written << " len:" << len << " buf:" << buf << ' ' << errnoWithDescription() << endl;
+ uasserted(13515, str::stream() << "error appending to file " << _fd << ' ' << errnoWithDescription());
+ }
+
+ if(
+#if defined(__linux__)
+ fdatasync(_fd) < 0
+#else
+ fsync(_fd)
+#endif
+ ) {
+ uasserted(13514, str::stream() << "error appending to file on fsync " << ' ' << errnoWithDescription());
+ }
+
+#ifdef POSIX_FADV_DONTNEED
+ if (!_direct)
+ posix_fadvise(_fd, pos, len, POSIX_FADV_DONTNEED);
+#endif
+ }
+
+}
+
+#endif
diff --git a/src/mongo/util/logfile.h b/src/mongo/util/logfile.h
new file mode 100644
index 00000000000..e41ecc2f6ec
--- /dev/null
+++ b/src/mongo/util/logfile.h
@@ -0,0 +1,58 @@
+// @file logfile.h simple file log writing / journaling
+
+/**
+* Copyright (C) 2010 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+namespace mongo {
+
+ class LogFile {
+ public:
+ /** create the file and open. must not already exist.
+ throws UserAssertion on i/o error
+ */
+ LogFile(string name, bool readwrite = false);
+
+ /** closes */
+ ~LogFile();
+
+ /** append to file. does not return until sync'd. uses direct i/o when possible.
+ throws UserAssertion on an i/o error
+ note direct i/o may have alignment requirements
+ */
+ void synchronousAppend(const void *buf, size_t len);
+
+ /** write at specified offset. must be aligned. noreturn until physically written. thread safe */
+ void writeAt(unsigned long long offset, const void *_bug, size_t _len);
+
+ void readAt(unsigned long long offset, void *_buf, size_t _len);
+
+ const string _name;
+
+ void truncate(); // Removes extra data after current position
+
+ private:
+#if defined(_WIN32)
+ typedef HANDLE fd_type;
+#else
+ typedef int fd_type;
+#endif
+ fd_type _fd;
+ bool _direct; // are we using direct I/O
+ };
+
+}
diff --git a/src/mongo/util/lruishmap.h b/src/mongo/util/lruishmap.h
new file mode 100644
index 00000000000..ba91bf6f0f6
--- /dev/null
+++ b/src/mongo/util/lruishmap.h
@@ -0,0 +1,78 @@
+// lru-ish map.h
+
+/* 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 "../pch.h"
+#include "../util/goodies.h"
+
+namespace mongo {
+
+ /* Your K object must define:
+ int hash() - must always return > 0.
+ operator==
+ */
+
+ template <class K, class V, int MaxChain>
+ class LRUishMap {
+ public:
+ LRUishMap(int _n) {
+ n = nextPrime(_n);
+ keys = new K[n];
+ hashes = new int[n];
+ for ( int i = 0; i < n; i++ ) hashes[i] = 0;
+ }
+ ~LRUishMap() {
+ delete[] keys;
+ delete[] hashes;
+ }
+
+ int _find(const K& k, bool& found) {
+ int h = k.hash();
+ assert( h > 0 );
+ int j = h % n;
+ int first = j;
+ for ( int i = 0; i < MaxChain; i++ ) {
+ if ( hashes[j] == h ) {
+ if ( keys[j] == k ) {
+ found = true;
+ return j;
+ }
+ }
+ else if ( hashes[j] == 0 ) {
+ found = false;
+ return j;
+ }
+ }
+ found = false;
+ return first;
+ }
+
+ V* find(const K& k) {
+ bool found;
+ int j = _find(k, found);
+ return found ? &values[j] : 0;
+ }
+
+ private:
+ int n;
+ K *keys;
+ int *hashes;
+ V *values;
+ };
+
+} // namespace mongo
diff --git a/src/mongo/util/md5.c b/src/mongo/util/md5.c
new file mode 100644
index 00000000000..c35d96c5ef5
--- /dev/null
+++ b/src/mongo/util/md5.c
@@ -0,0 +1,381 @@
+/*
+ Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
+ either statically or dynamically; added missing #include <string.h>
+ in library.
+ 2002-03-11 lpd Corrected argument list for main(), and added int return
+ type, in test program and T value program.
+ 2002-02-21 lpd Added missing #include <stdio.h> in test program.
+ 2000-07-03 lpd Patched to eliminate warnings about "constant is
+ unsigned in ANSI C, signed in traditional"; made test program
+ self-checking.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
+ 1999-05-03 lpd Original version.
+ */
+
+#include "md5.h"
+#include <string.h>
+
+#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
+#ifdef ARCH_IS_BIG_ENDIAN
+# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
+#else
+# define BYTE_ORDER 0
+#endif
+
+#define T_MASK ((md5_word_t)~0)
+#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
+#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
+#define T3 0x242070db
+#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
+#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
+#define T6 0x4787c62a
+#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
+#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
+#define T9 0x698098d8
+#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
+#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
+#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
+#define T13 0x6b901122
+#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
+#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
+#define T16 0x49b40821
+#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
+#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
+#define T19 0x265e5a51
+#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
+#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
+#define T22 0x02441453
+#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
+#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
+#define T25 0x21e1cde6
+#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
+#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
+#define T28 0x455a14ed
+#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
+#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
+#define T31 0x676f02d9
+#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
+#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
+#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
+#define T35 0x6d9d6122
+#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
+#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
+#define T38 0x4bdecfa9
+#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
+#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
+#define T41 0x289b7ec6
+#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
+#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
+#define T44 0x04881d05
+#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
+#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
+#define T47 0x1fa27cf8
+#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
+#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
+#define T50 0x432aff97
+#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
+#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
+#define T53 0x655b59c3
+#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
+#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
+#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
+#define T57 0x6fa87e4f
+#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
+#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
+#define T60 0x4e0811a1
+#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
+#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
+#define T63 0x2ad7d2bb
+#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
+
+
+static void
+md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
+{
+ md5_word_t
+ a = pms->abcd[0], b = pms->abcd[1],
+ c = pms->abcd[2], d = pms->abcd[3];
+ md5_word_t t;
+#if BYTE_ORDER > 0
+ /* Define storage only for big-endian CPUs. */
+ md5_word_t X[16];
+#else
+ /* Define storage for little-endian or both types of CPUs. */
+ md5_word_t xbuf[16];
+ const md5_word_t *X;
+#endif
+
+ {
+#if BYTE_ORDER == 0
+ /*
+ * Determine dynamically whether this is a big-endian or
+ * little-endian machine, since we can use a more efficient
+ * algorithm on the latter.
+ */
+ static const int w = 1;
+
+ if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
+#endif
+#if BYTE_ORDER <= 0 /* little-endian */
+ {
+ /*
+ * On little-endian machines, we can process properly aligned
+ * data without copying it.
+ */
+ if (!((data - (const md5_byte_t *)0) & 3)) {
+ /* data are properly aligned */
+ X = (const md5_word_t *)data;
+ } else {
+ /* not aligned */
+ memcpy(xbuf, data, 64);
+ X = xbuf;
+ }
+ }
+#endif
+#if BYTE_ORDER == 0
+ else /* dynamic big-endian */
+#endif
+#if BYTE_ORDER >= 0 /* big-endian */
+ {
+ /*
+ * On big-endian machines, we must arrange the bytes in the
+ * right order.
+ */
+ const md5_byte_t *xp = data;
+ int i;
+
+# if BYTE_ORDER == 0
+ X = xbuf; /* (dynamic only) */
+# else
+# define xbuf X /* (static only) */
+# endif
+ for (i = 0; i < 16; ++i, xp += 4)
+ xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
+ }
+#endif
+ }
+
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
+
+ /* Round 1. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
+#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + F(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 7, T1);
+ SET(d, a, b, c, 1, 12, T2);
+ SET(c, d, a, b, 2, 17, T3);
+ SET(b, c, d, a, 3, 22, T4);
+ SET(a, b, c, d, 4, 7, T5);
+ SET(d, a, b, c, 5, 12, T6);
+ SET(c, d, a, b, 6, 17, T7);
+ SET(b, c, d, a, 7, 22, T8);
+ SET(a, b, c, d, 8, 7, T9);
+ SET(d, a, b, c, 9, 12, T10);
+ SET(c, d, a, b, 10, 17, T11);
+ SET(b, c, d, a, 11, 22, T12);
+ SET(a, b, c, d, 12, 7, T13);
+ SET(d, a, b, c, 13, 12, T14);
+ SET(c, d, a, b, 14, 17, T15);
+ SET(b, c, d, a, 15, 22, T16);
+#undef SET
+
+ /* Round 2. */
+ /* Let [abcd k s i] denote the operation
+ a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
+#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + G(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 1, 5, T17);
+ SET(d, a, b, c, 6, 9, T18);
+ SET(c, d, a, b, 11, 14, T19);
+ SET(b, c, d, a, 0, 20, T20);
+ SET(a, b, c, d, 5, 5, T21);
+ SET(d, a, b, c, 10, 9, T22);
+ SET(c, d, a, b, 15, 14, T23);
+ SET(b, c, d, a, 4, 20, T24);
+ SET(a, b, c, d, 9, 5, T25);
+ SET(d, a, b, c, 14, 9, T26);
+ SET(c, d, a, b, 3, 14, T27);
+ SET(b, c, d, a, 8, 20, T28);
+ SET(a, b, c, d, 13, 5, T29);
+ SET(d, a, b, c, 2, 9, T30);
+ SET(c, d, a, b, 7, 14, T31);
+ SET(b, c, d, a, 12, 20, T32);
+#undef SET
+
+ /* Round 3. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + H(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 5, 4, T33);
+ SET(d, a, b, c, 8, 11, T34);
+ SET(c, d, a, b, 11, 16, T35);
+ SET(b, c, d, a, 14, 23, T36);
+ SET(a, b, c, d, 1, 4, T37);
+ SET(d, a, b, c, 4, 11, T38);
+ SET(c, d, a, b, 7, 16, T39);
+ SET(b, c, d, a, 10, 23, T40);
+ SET(a, b, c, d, 13, 4, T41);
+ SET(d, a, b, c, 0, 11, T42);
+ SET(c, d, a, b, 3, 16, T43);
+ SET(b, c, d, a, 6, 23, T44);
+ SET(a, b, c, d, 9, 4, T45);
+ SET(d, a, b, c, 12, 11, T46);
+ SET(c, d, a, b, 15, 16, T47);
+ SET(b, c, d, a, 2, 23, T48);
+#undef SET
+
+ /* Round 4. */
+ /* Let [abcd k s t] denote the operation
+ a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+#define SET(a, b, c, d, k, s, Ti)\
+ t = a + I(b,c,d) + X[k] + Ti;\
+ a = ROTATE_LEFT(t, s) + b
+ /* Do the following 16 operations. */
+ SET(a, b, c, d, 0, 6, T49);
+ SET(d, a, b, c, 7, 10, T50);
+ SET(c, d, a, b, 14, 15, T51);
+ SET(b, c, d, a, 5, 21, T52);
+ SET(a, b, c, d, 12, 6, T53);
+ SET(d, a, b, c, 3, 10, T54);
+ SET(c, d, a, b, 10, 15, T55);
+ SET(b, c, d, a, 1, 21, T56);
+ SET(a, b, c, d, 8, 6, T57);
+ SET(d, a, b, c, 15, 10, T58);
+ SET(c, d, a, b, 6, 15, T59);
+ SET(b, c, d, a, 13, 21, T60);
+ SET(a, b, c, d, 4, 6, T61);
+ SET(d, a, b, c, 11, 10, T62);
+ SET(c, d, a, b, 2, 15, T63);
+ SET(b, c, d, a, 9, 21, T64);
+#undef SET
+
+ /* Then perform the following additions. (That is increment each
+ of the four registers by the value it had before this block
+ was started.) */
+ pms->abcd[0] += a;
+ pms->abcd[1] += b;
+ pms->abcd[2] += c;
+ pms->abcd[3] += d;
+}
+
+void
+md5_init(md5_state_t *pms)
+{
+ pms->count[0] = pms->count[1] = 0;
+ pms->abcd[0] = 0x67452301;
+ pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
+ pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
+ pms->abcd[3] = 0x10325476;
+}
+
+void
+md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
+{
+ const md5_byte_t *p = data;
+ int left = nbytes;
+ int offset = (pms->count[0] >> 3) & 63;
+ md5_word_t nbits = (md5_word_t)(nbytes << 3);
+
+ if (nbytes <= 0)
+ return;
+
+ /* Update the message length. */
+ pms->count[1] += nbytes >> 29;
+ pms->count[0] += nbits;
+ if (pms->count[0] < nbits)
+ pms->count[1]++;
+
+ /* Process an initial partial block. */
+ if (offset) {
+ int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
+
+ memcpy(pms->buf + offset, p, copy);
+ if (offset + copy < 64)
+ return;
+ p += copy;
+ left -= copy;
+ md5_process(pms, pms->buf);
+ }
+
+ /* Process full blocks. */
+ for (; left >= 64; p += 64, left -= 64)
+ md5_process(pms, p);
+
+ /* Process a final partial block. */
+ if (left)
+ memcpy(pms->buf, p, left);
+}
+
+void
+md5_finish(md5_state_t *pms, md5_byte_t digest[16])
+{
+ static const md5_byte_t pad[64] = {
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ md5_byte_t data[8];
+ int i;
+
+ /* Save the length before padding. */
+ for (i = 0; i < 8; ++i)
+ data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
+ /* Pad to 56 bytes mod 64. */
+ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
+ /* Append the length. */
+ md5_append(pms, data, 8);
+ for (i = 0; i < 16; ++i)
+ digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
+}
diff --git a/src/mongo/util/md5.h b/src/mongo/util/md5.h
new file mode 100644
index 00000000000..a3f3b6db0e2
--- /dev/null
+++ b/src/mongo/util/md5.h
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.h is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Removed support for non-ANSI compilers; removed
+ references to Ghostscript; clarified derivation from RFC 1321;
+ now handles byte order either statically or dynamically.
+ 1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
+ 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
+ added conditionalization for C++ compilation from Martin
+ Purschke <purschke@bnl.gov>.
+ 1999-05-03 lpd Original version.
+ */
+
+#ifndef md5_INCLUDED
+# define md5_INCLUDED
+
+/*
+ * This package supports both compile-time and run-time determination of CPU
+ * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
+ * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
+ * defined as non-zero, the code will be compiled to run only on big-endian
+ * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
+ * run on either big- or little-endian CPUs, but will run slightly less
+ * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
+ */
+
+typedef unsigned char md5_byte_t; /* 8-bit byte */
+typedef unsigned int md5_word_t; /* 32-bit word */
+
+/* Define the state of the MD5 Algorithm. */
+typedef struct md5_state_s {
+ md5_word_t count[2]; /* message length in bits, lsw first */
+ md5_word_t abcd[4]; /* digest buffer */
+ md5_byte_t buf[64]; /* accumulate block */
+} md5_state_t;
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ /* Initialize the algorithm. */
+ void md5_init(md5_state_t *pms);
+
+ /* Append a string to the message. */
+ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
+
+ /* Finish the message and return the digest. */
+ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
+
+#ifdef __cplusplus
+} /* end extern "C" */
+#endif
+
+#endif /* md5_INCLUDED */
diff --git a/src/mongo/util/md5.hpp b/src/mongo/util/md5.hpp
new file mode 100644
index 00000000000..dc061719747
--- /dev/null
+++ b/src/mongo/util/md5.hpp
@@ -0,0 +1,58 @@
+// md5.hpp
+
+/* 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 "md5.h"
+
+namespace mongo {
+
+ typedef unsigned char md5digest[16];
+
+ inline void md5(const void *buf, int nbytes, md5digest digest) {
+ md5_state_t st;
+ md5_init(&st);
+ md5_append(&st, (const md5_byte_t *) buf, nbytes);
+ md5_finish(&st, digest);
+ }
+
+ inline void md5(const char *str, md5digest digest) {
+ md5(str, strlen(str), digest);
+ }
+
+ inline std::string digestToString( md5digest digest ){
+ static const char * letters = "0123456789abcdef";
+ stringstream ss;
+ for ( int i=0; i<16; i++){
+ unsigned char c = digest[i];
+ ss << letters[ ( c >> 4 ) & 0xf ] << letters[ c & 0xf ];
+ }
+ return ss.str();
+ }
+
+ inline std::string md5simpledigest( const void* buf, int nbytes){
+ md5digest d;
+ md5( buf, nbytes , d );
+ return digestToString( d );
+ }
+
+ inline std::string md5simpledigest( string s ){
+ return md5simpledigest(s.data(), s.size());
+ }
+
+
+} // namespace mongo
diff --git a/src/mongo/util/md5main.cpp b/src/mongo/util/md5main.cpp
new file mode 100644
index 00000000000..9995fee8fa7
--- /dev/null
+++ b/src/mongo/util/md5main.cpp
@@ -0,0 +1,142 @@
+/*
+ Copyright (C) 2002 Aladdin Enterprises. All rights reserved.
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ L. Peter Deutsch
+ ghost@aladdin.com
+
+ */
+/* $Id: md5main.c,v 1.1 2002/04/13 19:20:28 lpd Exp $ */
+/*
+ Independent implementation of MD5 (RFC 1321).
+
+ This code implements the MD5 Algorithm defined in RFC 1321, whose
+ text is available at
+ http://www.ietf.org/rfc/rfc1321.txt
+ The code is derived from the text of the RFC, including the test suite
+ (section A.5) but excluding the rest of Appendix A. It does not include
+ any code or documentation that is identified in the RFC as being
+ copyrighted.
+
+ The original and principal author of md5.c is L. Peter Deutsch
+ <ghost@aladdin.com>. Other authors are noted in the change history
+ that follows (in reverse chronological order):
+
+ 2002-04-13 lpd Splits off main program into a separate file, md5main.c.
+ */
+
+#include "pch.h"
+#include "md5.h"
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * This file builds an executable that performs various functions related
+ * to the MD5 library. Typical compilation:
+ * gcc -o md5main -lm md5main.c md5.c
+ */
+static const char *const usage = "\
+Usage:\n\
+ md5main --test # run the self-test (A.5 of RFC 1321)\n\
+ md5main --t-values # print the T values for the library\n\
+ md5main --version # print the version of the package\n\
+";
+static const char *const version = "2002-04-13";
+
+/* modified: not static, renamed */
+/* Run the self-test. */
+/*static*/ int
+//do_test(void)
+do_md5_test(void) {
+ static const char *const test[7*2] = {
+ "", "d41d8cd98f00b204e9800998ecf8427e",
+ "a", "0cc175b9c0f1b6a831c399e269772661",
+ "abc", "900150983cd24fb0d6963f7d28e17f72",
+ "message digest", "f96b697d7cb7938d525a2f31aaf161d0",
+ "abcdefghijklmnopqrstuvwxyz", "c3fcd3d76192e4007dfb496cca67e13b",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "d174ab98d277d9f5a5611c2c9f419d9f",
+ "12345678901234567890123456789012345678901234567890123456789012345678901234567890", "57edf4a22be3c955ac49da2e2107b67a"
+ };
+ int i;
+ int status = 0;
+
+ for (i = 0; i < 7*2; i += 2) {
+ md5_state_t state;
+ md5_byte_t digest[16];
+ char hex_output[16*2 + 1];
+ int di;
+
+ md5_init(&state);
+ md5_append(&state, (const md5_byte_t *)test[i], strlen(test[i]));
+ md5_finish(&state, digest);
+ for (di = 0; di < 16; ++di)
+ sprintf(hex_output + di * 2, "%02x", digest[di]);
+ if (strcmp(hex_output, test[i + 1])) {
+ printf("MD5 (\"%s\") = ", test[i]);
+ puts(hex_output);
+ printf("**** ERROR, should be: %s\n", test[i + 1]);
+ status = 1;
+ }
+ }
+// if (status == 0)
+ /*modified commented out: puts("md5 self-test completed successfully."); */
+ return status;
+}
+
+/* Print the T values. */
+static int
+do_t_values(void) {
+ int i;
+ for (i = 1; i <= 64; ++i) {
+ unsigned long v = (unsigned long)(4294967296.0 * fabs(sin((double)i)));
+
+ /*
+ * The following nonsense is only to avoid compiler warnings about
+ * "integer constant is unsigned in ANSI C, signed with -traditional".
+ */
+ if (v >> 31) {
+ printf("#define T%d /* 0x%08lx */ (T_MASK ^ 0x%08lx)\n", i,
+ v, (unsigned long)(unsigned int)(~v));
+ }
+ else {
+ printf("#define T%d 0x%08lx\n", i, v);
+ }
+ }
+ return 0;
+}
+
+/* modified from original code changed function name main->md5main */
+/* Main program */
+int
+md5main(int argc, char *argv[]) {
+ if (argc == 2) {
+ if (!strcmp(argv[1], "--test"))
+ return do_md5_test();
+ if (!strcmp(argv[1], "--t-values"))
+ return do_t_values();
+ if (!strcmp(argv[1], "--version")) {
+ puts(version);
+ return 0;
+ }
+ }
+ puts(usage);
+ return 0;
+}
+
diff --git a/src/mongo/util/mmap.cpp b/src/mongo/util/mmap.cpp
new file mode 100755
index 00000000000..1eb0242e657
--- /dev/null
+++ b/src/mongo/util/mmap.cpp
@@ -0,0 +1,211 @@
+// mmap.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "mmap.h"
+#include "processinfo.h"
+#include "concurrency/rwlock.h"
+#include "../db/namespace.h"
+#include "../db/cmdline.h"
+
+namespace mongo {
+
+ set<MongoFile*> MongoFile::mmfiles;
+ map<string,MongoFile*> MongoFile::pathToFile;
+
+ /* Create. Must not exist.
+ @param zero fill file with zeros when true
+ */
+ void* MemoryMappedFile::create(string filename, unsigned long long len, bool zero) {
+ uassert( 13468, string("can't create file already exists ") + filename, !exists(filename) );
+ void *p = map(filename.c_str(), len);
+ if( p && zero ) {
+ size_t sz = (size_t) len;
+ assert( len == sz );
+ memset(p, 0, sz);
+ }
+ return p;
+ }
+
+ /*static*/ void MemoryMappedFile::updateLength( const char *filename, unsigned long long &length ) {
+ if ( !boost::filesystem::exists( filename ) )
+ return;
+ // make sure we map full length if preexisting file.
+ boost::uintmax_t l = boost::filesystem::file_size( filename );
+ length = l;
+ }
+
+ void* MemoryMappedFile::map(const char *filename) {
+ unsigned long long l;
+ try {
+ l = boost::filesystem::file_size( filename );
+ }
+ catch(boost::filesystem::filesystem_error& e) {
+ uasserted(15922, str::stream() << "couldn't get file length when opening mapping " << filename << ' ' << e.what() );
+ }
+ return map( filename , l );
+ }
+ void* MemoryMappedFile::mapWithOptions(const char *filename, int options) {
+ unsigned long long l;
+ try {
+ l = boost::filesystem::file_size( filename );
+ }
+ catch(boost::filesystem::filesystem_error& e) {
+ uasserted(15923, str::stream() << "couldn't get file length when opening mapping " << filename << ' ' << e.what() );
+ }
+ return map( filename , l, options );
+ }
+
+ /* --- MongoFile -------------------------------------------------
+ this is the administrative stuff
+ */
+
+ RWLockRecursiveNongreedy LockMongoFilesShared::mmmutex("mmmutex",10*60*1000 /* 10 minutes */);
+ unsigned LockMongoFilesShared::era = 99; // note this rolls over
+
+ /* subclass must call in destructor (or at close).
+ removes this from pathToFile and other maps
+ safe to call more than once, albeit might be wasted work
+ ideal to call close to the close, if the close is well before object destruction
+ */
+ void MongoFile::destroyed() {
+ LockMongoFilesShared::assertExclusivelyLocked();
+ mmfiles.erase(this);
+ pathToFile.erase( filename() );
+ }
+
+ /*static*/
+ void MongoFile::closeAllFiles( stringstream &message ) {
+ static int closingAllFiles = 0;
+ if ( closingAllFiles ) {
+ message << "warning closingAllFiles=" << closingAllFiles << endl;
+ return;
+ }
+ ++closingAllFiles;
+
+ LockMongoFilesExclusive lk;
+
+ ProgressMeter pm( mmfiles.size() , 2 , 1 );
+ set<MongoFile*> temp = mmfiles;
+ for ( set<MongoFile*>::iterator i = temp.begin(); i != temp.end(); i++ ) {
+ (*i)->close(); // close() now removes from mmfiles
+ pm.hit();
+ }
+ message << "closeAllFiles() finished";
+ --closingAllFiles;
+ }
+
+ /*static*/ long long MongoFile::totalMappedLength() {
+ unsigned long long total = 0;
+
+ LockMongoFilesShared lk;
+
+ for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ )
+ total += (*i)->length();
+
+ return total;
+ }
+
+ void nullFunc() { }
+
+ // callback notifications
+ void (*MongoFile::notifyPreFlush)() = nullFunc;
+ void (*MongoFile::notifyPostFlush)() = nullFunc;
+
+ /*static*/ int MongoFile::flushAll( bool sync ) {
+ notifyPreFlush();
+ int x = _flushAll(sync);
+ notifyPostFlush();
+ return x;
+ }
+
+ /*static*/ int MongoFile::_flushAll( bool sync ) {
+ if ( ! sync ) {
+ int num = 0;
+ LockMongoFilesShared lk;
+ for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ ) {
+ num++;
+ MongoFile * mmf = *i;
+ if ( ! mmf )
+ continue;
+
+ mmf->flush( sync );
+ }
+ return num;
+ }
+
+ // want to do it sync
+ set<MongoFile*> seen;
+ while ( true ) {
+ auto_ptr<Flushable> f;
+ {
+ LockMongoFilesShared lk;
+ for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ ) {
+ MongoFile * mmf = *i;
+ if ( ! mmf )
+ continue;
+ if ( seen.count( mmf ) )
+ continue;
+ f.reset( mmf->prepareFlush() );
+ seen.insert( mmf );
+ break;
+ }
+ }
+ if ( ! f.get() )
+ break;
+
+ f->flush();
+ }
+ return seen.size();
+ }
+
+ void MongoFile::created() {
+ LockMongoFilesExclusive lk;
+ mmfiles.insert(this);
+ }
+
+ void MongoFile::setFilename(string fn) {
+ LockMongoFilesExclusive lk;
+ assert( _filename.empty() );
+ _filename = fn;
+ MongoFile *&ptf = pathToFile[fn];
+ massert(13617, "MongoFile : multiple opens of same filename", ptf == 0);
+ ptf = this;
+ }
+
+#if defined(_DEBUG)
+ void MongoFile::markAllWritable() {
+ if( cmdLine.dur )
+ return;
+ LockMongoFilesShared lk;
+ for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ ) {
+ MongoFile * mmf = *i;
+ if (mmf) mmf->_lock();
+ }
+ }
+
+ void MongoFile::unmarkAllWritable() {
+ if( cmdLine.dur )
+ return;
+ LockMongoFilesShared lk;
+ for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ ) {
+ MongoFile * mmf = *i;
+ if (mmf) mmf->_unlock();
+ }
+ }
+#endif
+} // namespace mongo
diff --git a/src/mongo/util/mmap.h b/src/mongo/util/mmap.h
new file mode 100644
index 00000000000..2d4454bbc7f
--- /dev/null
+++ b/src/mongo/util/mmap.h
@@ -0,0 +1,305 @@
+// mmap.h
+
+/* 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 <boost/thread/xtime.hpp>
+#include "concurrency/rwlock.h"
+
+namespace mongo {
+
+ class MAdvise {
+ void *_p;
+ unsigned _len;
+ public:
+ enum Advice { Sequential=1 };
+ MAdvise(void *p, unsigned len, Advice a);
+ ~MAdvise(); // destructor resets the range to MADV_NORMAL
+ };
+
+ // lock order: lock dbMutex before this if you lock both
+ class LockMongoFilesShared {
+ friend class LockMongoFilesExclusive;
+ static RWLockRecursiveNongreedy mmmutex;
+ static unsigned era;
+ RWLockRecursive::Shared lk;
+ public:
+ LockMongoFilesShared() : lk(mmmutex) { }
+
+ /** era changes anytime memory maps come and go. thus you can use this as a cheap way to verify
+ that things are still in the condition you expected. of course you must be shared locked
+ otherwise someone could be in progress. if you have unlocked this is a reasonable way to
+ check your memory mapped pointer is still good.
+ */
+ static unsigned getEra() { return era; }
+
+ static void assertExclusivelyLocked() { mmmutex.assertExclusivelyLocked(); }
+ };
+
+ class LockMongoFilesExclusive {
+ RWLockRecursive::Exclusive lk;
+ public:
+ LockMongoFilesExclusive() : lk(LockMongoFilesShared::mmmutex) {
+ LockMongoFilesShared::era++;
+ }
+ };
+
+ /* the administrative-ish stuff here */
+ class MongoFile : boost::noncopyable {
+ public:
+ /** Flushable has to fail nicely if the underlying object gets killed */
+ class Flushable {
+ public:
+ virtual ~Flushable() {}
+ virtual void flush() = 0;
+ };
+
+ virtual ~MongoFile() {}
+
+ enum Options {
+ SEQUENTIAL = 1, // hint - e.g. FILE_FLAG_SEQUENTIAL_SCAN on windows
+ READONLY = 2 // not contractually guaranteed, but if specified the impl has option to fault writes
+ };
+
+ /** @param fun is called for each MongoFile.
+ calledl from within a mutex that MongoFile uses. so be careful not to deadlock.
+ */
+ template < class F >
+ static void forEach( F fun );
+
+ /** note: you need to be in mmmutex when using this. forEach (above) handles that for you automatically.
+*/
+ static set<MongoFile*>& getAllFiles() { return mmfiles; }
+
+ // callbacks if you need them
+ static void (*notifyPreFlush)();
+ static void (*notifyPostFlush)();
+
+ static int flushAll( bool sync ); // returns n flushed
+ static long long totalMappedLength();
+ static void closeAllFiles( stringstream &message );
+
+#if defined(_DEBUG)
+ static void markAllWritable();
+ static void unmarkAllWritable();
+#else
+ static void markAllWritable() { }
+ static void unmarkAllWritable() { }
+#endif
+
+ static bool exists(boost::filesystem::path p) { return boost::filesystem::exists(p); }
+
+ virtual bool isMongoMMF() { return false; }
+
+ string filename() const { return _filename; }
+ void setFilename(string fn);
+
+ private:
+ string _filename;
+ static int _flushAll( bool sync ); // returns n flushed
+ protected:
+ virtual void close() = 0;
+ virtual void flush(bool sync) = 0;
+ /**
+ * returns a thread safe object that you can call flush on
+ * Flushable has to fail nicely if the underlying object gets killed
+ */
+ virtual Flushable * prepareFlush() = 0;
+
+ void created(); /* subclass must call after create */
+
+ /* subclass must call in destructor (or at close).
+ removes this from pathToFile and other maps
+ safe to call more than once, albeit might be wasted work
+ ideal to call close to the close, if the close is well before object destruction
+ */
+ void destroyed();
+
+ virtual unsigned long long length() const = 0;
+
+ // only supporting on posix mmap
+ virtual void _lock() {}
+ virtual void _unlock() {}
+
+ static set<MongoFile*> mmfiles;
+ public:
+ static map<string,MongoFile*> pathToFile;
+ };
+
+ /** look up a MMF by filename. scoped mutex locking convention.
+ example:
+ MMFFinderByName finder;
+ MongoMMF *a = finder.find("file_name_a");
+ MongoMMF *b = finder.find("file_name_b");
+ */
+ class MongoFileFinder : boost::noncopyable {
+ public:
+ MongoFileFinder() { }
+
+ /** @return The MongoFile object associated with the specified file name. If no file is open
+ with the specified name, returns null.
+ */
+ MongoFile* findByPath(string path) {
+ map<string,MongoFile*>::iterator i = MongoFile::pathToFile.find(path);
+ return i == MongoFile::pathToFile.end() ? NULL : i->second;
+ }
+
+ private:
+ LockMongoFilesShared _lk;
+ };
+
+ struct MongoFileAllowWrites {
+ MongoFileAllowWrites() {
+ MongoFile::markAllWritable();
+ }
+ ~MongoFileAllowWrites() {
+ MongoFile::unmarkAllWritable();
+ }
+ };
+
+ class MemoryMappedFile : public MongoFile {
+ protected:
+ virtual void* viewForFlushing() {
+ if( views.size() == 0 )
+ return 0;
+ assert( views.size() == 1 );
+ return views[0];
+ }
+ public:
+ MemoryMappedFile();
+
+ virtual ~MemoryMappedFile() {
+ LockMongoFilesExclusive lk;
+ close();
+ }
+
+ virtual void close();
+
+ // Throws exception if file doesn't exist. (dm may2010: not sure if this is always true?)
+ void* map(const char *filename);
+
+ /** @param options see MongoFile::Options
+ */
+ void* mapWithOptions(const char *filename, int options);
+
+ /* Creates with length if DNE, otherwise uses existing file length,
+ passed length.
+ @param options MongoFile::Options bits
+ */
+ void* map(const char *filename, unsigned long long &length, int options = 0 );
+
+ /* Create. Must not exist.
+ @param zero fill file with zeros when true
+ */
+ void* create(string filename, unsigned long long len, bool zero);
+
+ void flush(bool sync);
+ virtual Flushable * prepareFlush();
+
+ long shortLength() const { return (long) len; }
+ unsigned long long length() const { return len; }
+
+ /** create a new view with the specified properties.
+ automatically cleaned up upon close/destruction of the MemoryMappedFile object.
+ */
+ void* createReadOnlyMap();
+ void* createPrivateMap();
+
+ /** make the private map range writable (necessary for our windows implementation) */
+ static void makeWritable(void *, unsigned len)
+#if defined(_WIN32)
+ ;
+#else
+ { }
+#endif
+
+ private:
+ static void updateLength( const char *filename, unsigned long long &length );
+
+ HANDLE fd;
+ HANDLE maphandle;
+ vector<void *> views;
+ unsigned long long len;
+
+#ifdef _WIN32
+ boost::shared_ptr<mutex> _flushMutex;
+ void clearWritableBits(void *privateView);
+ public:
+ static const unsigned ChunkSize = 64 * 1024 * 1024;
+ static const unsigned NChunks = 1024 * 1024;
+#else
+ void clearWritableBits(void *privateView) { }
+#endif
+
+ protected:
+ // only posix mmap implementations will support this
+ virtual void _lock();
+ virtual void _unlock();
+
+ /** close the current private view and open a new replacement */
+ void* remapPrivateView(void *oldPrivateAddr);
+ };
+
+ typedef MemoryMappedFile MMF;
+
+ /** p is called from within a mutex that MongoFile uses. so be careful not to deadlock. */
+ template < class F >
+ inline void MongoFile::forEach( F p ) {
+ LockMongoFilesShared lklk;
+ for ( set<MongoFile*>::iterator i = mmfiles.begin(); i != mmfiles.end(); i++ )
+ p(*i);
+ }
+
+#if defined(_WIN32)
+ class ourbitset {
+ volatile unsigned bits[MemoryMappedFile::NChunks]; // volatile as we are doing double check locking
+ public:
+ ourbitset() {
+ memset((void*) bits, 0, sizeof(bits));
+ }
+ bool get(unsigned i) const {
+ unsigned x = i / 32;
+ assert( x < MemoryMappedFile::NChunks );
+ return (bits[x] & (1 << (i%32))) != 0;
+ }
+ void set(unsigned i) {
+ unsigned x = i / 32;
+ wassert( x < (MemoryMappedFile::NChunks*2/3) ); // warn if getting close to limit
+ assert( x < MemoryMappedFile::NChunks );
+ bits[x] |= (1 << (i%32));
+ }
+ void clear(unsigned i) {
+ unsigned x = i / 32;
+ assert( x < MemoryMappedFile::NChunks );
+ bits[x] &= ~(1 << (i%32));
+ }
+ };
+ extern ourbitset writable;
+ void makeChunkWritable(size_t chunkno);
+ inline void MemoryMappedFile::makeWritable(void *_p, unsigned len) {
+ size_t p = (size_t) _p;
+ unsigned a = p/ChunkSize;
+ unsigned b = (p+len)/ChunkSize;
+ for( unsigned i = a; i <= b; i++ ) {
+ if( !writable.get(i) ) {
+ makeChunkWritable(i);
+ }
+ }
+ }
+
+#endif
+
+} // namespace mongo
diff --git a/src/mongo/util/mmap_mm.cpp b/src/mongo/util/mmap_mm.cpp
new file mode 100644
index 00000000000..ec2400e02d3
--- /dev/null
+++ b/src/mongo/util/mmap_mm.cpp
@@ -0,0 +1,52 @@
+// mmap_mm.cpp - in memory (no file) version
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "mmap.h"
+
+/* in memory (no file) version */
+
+namespace mongo {
+
+ MemoryMappedFile::MemoryMappedFile() {
+ fd = 0;
+ maphandle = 0;
+ view = 0;
+ len = 0;
+ }
+
+ void MemoryMappedFile::close() {
+ if ( view )
+ free( view );
+ view = 0;
+ len = 0;
+ }
+
+ void* MemoryMappedFile::map(const char *filename, long& length , int options ) {
+ assert( length );
+ view = malloc( length );
+ return view;
+ }
+
+ void MemoryMappedFile::flush(bool sync) {
+ }
+
+ void MemoryMappedFile::_lock() {}
+ void MemoryMappedFile::_unlock() {}
+
+}
+
diff --git a/src/mongo/util/mmap_posix.cpp b/src/mongo/util/mmap_posix.cpp
new file mode 100644
index 00000000000..8097ef1b370
--- /dev/null
+++ b/src/mongo/util/mmap_posix.cpp
@@ -0,0 +1,214 @@
+// mmap_posix.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "mmap.h"
+#include "file_allocator.h"
+#include "../db/concurrency.h"
+#include <errno.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "../util/processinfo.h"
+#include "mongoutils/str.h"
+using namespace mongoutils;
+
+namespace mongo {
+
+ MemoryMappedFile::MemoryMappedFile() {
+ fd = 0;
+ maphandle = 0;
+ len = 0;
+ created();
+ }
+
+ void MemoryMappedFile::close() {
+ LockMongoFilesShared::assertExclusivelyLocked();
+ for( vector<void*>::iterator i = views.begin(); i != views.end(); i++ ) {
+ munmap(*i,len);
+ }
+ views.clear();
+
+ if ( fd )
+ ::close(fd);
+ fd = 0;
+ destroyed(); // cleans up from the master list of mmaps
+ }
+
+#ifndef O_NOATIME
+#define O_NOATIME (0)
+#endif
+
+#ifndef MAP_NORESERVE
+#define MAP_NORESERVE (0)
+#endif
+
+#if defined(__sunos__)
+ MAdvise::MAdvise(void *,unsigned, Advice) { }
+ MAdvise::~MAdvise() { }
+#else
+ MAdvise::MAdvise(void *p, unsigned len, Advice a) : _p(p), _len(len) {
+ assert( a == Sequential ); // more later
+ madvise(_p,_len,MADV_SEQUENTIAL);
+ }
+ MAdvise::~MAdvise() {
+ madvise(_p,_len,MADV_NORMAL);
+ }
+#endif
+
+ void* MemoryMappedFile::map(const char *filename, unsigned long long &length, int options) {
+ // length may be updated by callee.
+ setFilename(filename);
+ FileAllocator::get()->allocateAsap( filename, length );
+ len = length;
+
+ massert( 10446 , str::stream() << "mmap: can't map area of size 0 file: " << filename, length > 0 );
+
+ fd = open(filename, O_RDWR | O_NOATIME);
+ if ( fd <= 0 ) {
+ log() << "couldn't open " << filename << ' ' << errnoWithDescription() << endl;
+ fd = 0; // our sentinel for not opened
+ return 0;
+ }
+
+ unsigned long long filelen = lseek(fd, 0, SEEK_END);
+ uassert(10447, str::stream() << "map file alloc failed, wanted: " << length << " filelen: " << filelen << ' ' << sizeof(size_t), filelen == length );
+ lseek( fd, 0, SEEK_SET );
+
+ void * view = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if ( view == MAP_FAILED ) {
+ error() << " mmap() failed for " << filename << " len:" << length << " " << errnoWithDescription() << endl;
+ if ( errno == ENOMEM ) {
+ if( sizeof(void*) == 4 )
+ error() << "mmap failed with out of memory. You are using a 32-bit build and probably need to upgrade to 64" << endl;
+ else
+ error() << "mmap failed with out of memory. (64 bit build)" << endl;
+ }
+ return 0;
+ }
+
+
+#if defined(__sunos__)
+#warning madvise not supported on solaris yet
+#else
+ if ( options & SEQUENTIAL ) {
+ if ( madvise( view , length , MADV_SEQUENTIAL ) ) {
+ warning() << "map: madvise failed for " << filename << ' ' << errnoWithDescription() << endl;
+ }
+ }
+#endif
+
+ views.push_back( view );
+
+ DEV if (! d.dbMutex.info().isLocked()) {
+ _unlock();
+ }
+
+ return view;
+ }
+
+ void* MemoryMappedFile::createReadOnlyMap() {
+ void * x = mmap( /*start*/0 , len , PROT_READ , MAP_SHARED , fd , 0 );
+ if( x == MAP_FAILED ) {
+ if ( errno == ENOMEM ) {
+ if( sizeof(void*) == 4 )
+ error() << "mmap ro failed with out of memory. You are using a 32-bit build and probably need to upgrade to 64" << endl;
+ else
+ error() << "mmap ro failed with out of memory. (64 bit build)" << endl;
+ }
+ return 0;
+ }
+ return x;
+ }
+
+ void* MemoryMappedFile::createPrivateMap() {
+ void * x = mmap( /*start*/0 , len , PROT_READ|PROT_WRITE , MAP_PRIVATE|MAP_NORESERVE , fd , 0 );
+ if( x == MAP_FAILED ) {
+ if ( errno == ENOMEM ) {
+ if( sizeof(void*) == 4 ) {
+ error() << "mmap private failed with out of memory. You are using a 32-bit build and probably need to upgrade to 64" << endl;
+ }
+ else {
+ error() << "mmap private failed with out of memory. (64 bit build)" << endl;
+ }
+ }
+ else {
+ error() << "mmap private failed " << errnoWithDescription() << endl;
+ }
+ return 0;
+ }
+
+ views.push_back(x);
+ return x;
+ }
+
+ void* MemoryMappedFile::remapPrivateView(void *oldPrivateAddr) {
+ // don't unmap, just mmap over the old region
+ void * x = mmap( oldPrivateAddr, len , PROT_READ|PROT_WRITE , MAP_PRIVATE|MAP_NORESERVE|MAP_FIXED , fd , 0 );
+ if( x == MAP_FAILED ) {
+ int err = errno;
+ error() << "13601 Couldn't remap private view: " << errnoWithDescription(err) << endl;
+ log() << "aborting" << endl;
+ printMemInfo();
+ abort();
+ }
+ assert( x == oldPrivateAddr );
+ return x;
+ }
+
+ void MemoryMappedFile::flush(bool sync) {
+ if ( views.empty() || fd == 0 )
+ return;
+ if ( msync(viewForFlushing(), len, sync ? MS_SYNC : MS_ASYNC) )
+ problem() << "msync " << errnoWithDescription() << endl;
+ }
+
+ class PosixFlushable : public MemoryMappedFile::Flushable {
+ public:
+ PosixFlushable( void * view , HANDLE fd , long len )
+ : _view( view ) , _fd( fd ) , _len(len) {
+ }
+
+ void flush() {
+ if ( _view && _fd )
+ if ( msync(_view, _len, MS_SYNC ) )
+ problem() << "msync " << errnoWithDescription() << endl;
+
+ }
+
+ void * _view;
+ HANDLE _fd;
+ long _len;
+ };
+
+ MemoryMappedFile::Flushable * MemoryMappedFile::prepareFlush() {
+ return new PosixFlushable( viewForFlushing() , fd , len );
+ }
+
+ void MemoryMappedFile::_lock() {
+ if (! views.empty() && isMongoMMF() )
+ assert(mprotect(views[0], len, PROT_READ | PROT_WRITE) == 0);
+ }
+
+ void MemoryMappedFile::_unlock() {
+ if (! views.empty() && isMongoMMF() )
+ assert(mprotect(views[0], len, PROT_READ) == 0);
+ }
+
+} // namespace mongo
+
diff --git a/src/mongo/util/mmap_win.cpp b/src/mongo/util/mmap_win.cpp
new file mode 100644
index 00000000000..26115d096c1
--- /dev/null
+++ b/src/mongo/util/mmap_win.cpp
@@ -0,0 +1,202 @@
+// mmap_win.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "mmap.h"
+#include "text.h"
+#include "../db/mongommf.h"
+#include "../db/concurrency.h"
+
+namespace mongo {
+
+ mutex mapViewMutex("mapView");
+ ourbitset writable;
+
+ MAdvise::MAdvise(void *,unsigned, Advice) { }
+ MAdvise::~MAdvise() { }
+
+ /** notification on unmapping so we can clear writable bits */
+ void MemoryMappedFile::clearWritableBits(void *p) {
+ for( unsigned i = ((size_t)p)/ChunkSize; i <= (((size_t)p)+len)/ChunkSize; i++ ) {
+ writable.clear(i);
+ assert( !writable.get(i) );
+ }
+ }
+
+ MemoryMappedFile::MemoryMappedFile()
+ : _flushMutex(new mutex("flushMutex")) {
+ fd = 0;
+ maphandle = 0;
+ len = 0;
+ created();
+ }
+
+ void MemoryMappedFile::close() {
+ LockMongoFilesShared::assertExclusivelyLocked();
+ for( vector<void*>::iterator i = views.begin(); i != views.end(); i++ ) {
+ clearWritableBits(*i);
+ UnmapViewOfFile(*i);
+ }
+ views.clear();
+ if ( maphandle )
+ CloseHandle(maphandle);
+ maphandle = 0;
+ if ( fd )
+ CloseHandle(fd);
+ fd = 0;
+ destroyed(); // cleans up from the master list of mmaps
+ }
+
+ unsigned long long mapped = 0;
+
+ void* MemoryMappedFile::createReadOnlyMap() {
+ assert( maphandle );
+ scoped_lock lk(mapViewMutex);
+ void *p = MapViewOfFile(maphandle, FILE_MAP_READ, /*f ofs hi*/0, /*f ofs lo*/ 0, /*dwNumberOfBytesToMap 0 means to eof*/0);
+ if ( p == 0 ) {
+ DWORD e = GetLastError();
+ log() << "FILE_MAP_READ MapViewOfFile failed " << filename() << " " << errnoWithDescription(e) << endl;
+ }
+ else {
+ views.push_back(p);
+ }
+ return p;
+ }
+
+ void* MemoryMappedFile::map(const char *filenameIn, unsigned long long &length, int options) {
+ assert( fd == 0 && len == 0 ); // can't open more than once
+ setFilename(filenameIn);
+ /* big hack here: Babble uses db names with colons. doesn't seem to work on windows. temporary perhaps. */
+ char filename[256];
+ strncpy(filename, filenameIn, 255);
+ filename[255] = 0;
+ {
+ size_t len = strlen( filename );
+ for ( size_t i=len-1; i>=0; i-- ) {
+ if ( filename[i] == '/' ||
+ filename[i] == '\\' )
+ break;
+
+ if ( filename[i] == ':' )
+ filename[i] = '_';
+ }
+ }
+
+ updateLength( filename, length );
+
+ {
+ DWORD createOptions = FILE_ATTRIBUTE_NORMAL;
+ if ( options & SEQUENTIAL )
+ createOptions |= FILE_FLAG_SEQUENTIAL_SCAN;
+ DWORD rw = GENERIC_READ | GENERIC_WRITE;
+ fd = CreateFile(
+ toNativeString(filename).c_str(),
+ rw, // desired access
+ FILE_SHARE_WRITE | FILE_SHARE_READ, // share mode
+ NULL, // security
+ OPEN_ALWAYS, // create disposition
+ createOptions , // flags
+ NULL); // hTempl
+ if ( fd == INVALID_HANDLE_VALUE ) {
+ DWORD e = GetLastError();
+ log() << "Create/OpenFile failed " << filename << " errno:" << e << endl;
+ return 0;
+ }
+ }
+
+ mapped += length;
+
+ {
+ DWORD flProtect = PAGE_READWRITE; //(options & READONLY)?PAGE_READONLY:PAGE_READWRITE;
+ maphandle = CreateFileMapping(fd, NULL, flProtect,
+ length >> 32 /*maxsizehigh*/,
+ (unsigned) length /*maxsizelow*/,
+ NULL/*lpName*/);
+ if ( maphandle == NULL ) {
+ DWORD e = GetLastError(); // log() call was killing lasterror before we get to that point in the stream
+ log() << "CreateFileMapping failed " << filename << ' ' << errnoWithDescription(e) << endl;
+ close();
+ return 0;
+ }
+ }
+
+ void *view = 0;
+ {
+ scoped_lock lk(mapViewMutex);
+ DWORD access = (options&READONLY)? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;
+ view = MapViewOfFile(maphandle, access, /*f ofs hi*/0, /*f ofs lo*/ 0, /*dwNumberOfBytesToMap 0 means to eof*/0);
+ }
+ if ( view == 0 ) {
+ DWORD e = GetLastError();
+ log() << "MapViewOfFile failed " << filename << " " << errnoWithDescription(e) <<
+ ((sizeof(void*)==4)?" (32 bit build)":"") << endl;
+ close();
+ }
+ else {
+ views.push_back(view);
+ }
+ len = length;
+
+ return view;
+ }
+
+ class WindowsFlushable : public MemoryMappedFile::Flushable {
+ public:
+ WindowsFlushable( void * view , HANDLE fd , string filename , boost::shared_ptr<mutex> flushMutex )
+ : _view(view) , _fd(fd) , _filename(filename) , _flushMutex(flushMutex)
+ {}
+
+ void flush() {
+ if (!_view || !_fd)
+ return;
+
+ scoped_lock lk(*_flushMutex);
+
+ BOOL success = FlushViewOfFile(_view, 0); // 0 means whole mapping
+ if (!success) {
+ int err = GetLastError();
+ out() << "FlushViewOfFile failed " << err << " file: " << _filename << endl;
+ }
+
+ success = FlushFileBuffers(_fd);
+ if (!success) {
+ int err = GetLastError();
+ out() << "FlushFileBuffers failed " << err << " file: " << _filename << endl;
+ }
+ }
+
+ void * _view;
+ HANDLE _fd;
+ string _filename;
+ boost::shared_ptr<mutex> _flushMutex;
+ };
+
+ void MemoryMappedFile::flush(bool sync) {
+ uassert(13056, "Async flushing not supported on windows", sync);
+ if( !views.empty() ) {
+ WindowsFlushable f( viewForFlushing() , fd , filename() , _flushMutex);
+ f.flush();
+ }
+ }
+
+ MemoryMappedFile::Flushable * MemoryMappedFile::prepareFlush() {
+ return new WindowsFlushable( viewForFlushing() , fd , filename() , _flushMutex );
+ }
+ void MemoryMappedFile::_lock() {}
+ void MemoryMappedFile::_unlock() {}
+
+}
diff --git a/src/mongo/util/mongoutils/README b/src/mongo/util/mongoutils/README
new file mode 100755
index 00000000000..f61277c7409
--- /dev/null
+++ b/src/mongo/util/mongoutils/README
@@ -0,0 +1,15 @@
+ mongoutils namespace requirements:
+
+ (1) code is not database specific, rather, true utilities
+ (2) are cross platform
+ (3) may require boost headers, but not libs
+ (4) are clean and easy to use in any c++ project without pulling in lots of other stuff.
+ specifically, does not use anything in the mongo namespace!
+ (5) apache license
+ (6) must be documented! if you aren't going to bother (but don't do that), stick it in util.
+ (7) ideally header only (in the spirit of #3)
+
+ So basically, easy to use, general purpose stuff, with no arduous dependencies to drop into
+ any new project.
+
+ *** PLACE UNIT TESTS IN mongoutils/test.cpp ***
diff --git a/src/mongo/util/mongoutils/checksum.h b/src/mongo/util/mongoutils/checksum.h
new file mode 100644
index 00000000000..ea3d05131ce
--- /dev/null
+++ b/src/mongo/util/mongoutils/checksum.h
@@ -0,0 +1,32 @@
+/** @file checksum.h */
+
+/* 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
+
+namespace mongoutils {
+
+ /**
+ * this is a silly temporary implementation
+ */
+ inline int checksum( const char* x , int size ) {
+ int ck = 0;
+ for ( int i=0; i<size; i++ )
+ ck += ( (int)x[i] * ( i + 1 ) );
+ return ck;
+ }
+
+}
diff --git a/src/mongo/util/mongoutils/hash.h b/src/mongo/util/mongoutils/hash.h
new file mode 100644
index 00000000000..49f30b3242a
--- /dev/null
+++ b/src/mongo/util/mongoutils/hash.h
@@ -0,0 +1,41 @@
+/** @file hash.h */
+
+/* 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
+
+namespace mongoutils {
+
+ /** @return hash of a pointer to an unsigned. so you get a 32 bit hash out, regardless of whether
+ pointers are 32 or 64 bit on the particular platform.
+
+ is there a faster way to impl this that hashes just as well?
+ */
+ inline unsigned hashPointer(void *v) {
+ unsigned x = 0;
+ unsigned char *p = (unsigned char *) &v;
+ for( unsigned i = 0; i < sizeof(void*); i++ ) {
+ x = x * 131 + p[i];
+ }
+ return x;
+ }
+
+ inline unsigned hash(unsigned u) {
+ unsigned char *p = (unsigned char *) &u;
+ return (((((p[3] * 131) + p[2]) * 131) + p[1]) * 131) + p[0];
+ }
+
+}
diff --git a/src/mongo/util/mongoutils/html.h b/src/mongo/util/mongoutils/html.h
new file mode 100644
index 00000000000..f79e6ca514f
--- /dev/null
+++ b/src/mongo/util/mongoutils/html.h
@@ -0,0 +1,158 @@
+// @file html.h
+
+#pragma once
+
+/* Things in the mongoutils namespace
+ (1) are not database specific, rather, true utilities
+ (2) are cross platform
+ (3) may require boost headers, but not libs
+ (4) are clean and easy to use in any c++ project without pulling in lots of other stuff
+*/
+
+/* Copyright 2010 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.
+ */
+
+#include <sstream>
+
+namespace mongoutils {
+
+ namespace html {
+
+ using namespace std;
+
+ inline string _end() { return "</body></html>"; }
+ inline string _table() { return "</table>\n\n"; }
+ inline string _tr() { return "</tr>\n"; }
+
+ inline string tr() { return "<tr>"; }
+ inline string tr(string a, string b) {
+ stringstream ss;
+ ss << "<tr><td>" << a << "</td><td>" << b << "</td></tr>\n";
+ return ss.str();
+ }
+ template <class T>
+ inline string td(T x) {
+ stringstream ss;
+ ss << "<td>" << x << "</td>";
+ return ss.str();
+ }
+ inline string td(string x) {
+ return "<td>" + x + "</td>";
+ }
+ inline string th(string x) {
+ return "<th>" + x + "</th>";
+ }
+
+ inline void tablecell( stringstream& ss , bool b ) {
+ ss << "<td>" << (b ? "<b>X</b>" : "") << "</td>";
+ }
+
+ template< typename T>
+ inline void tablecell( stringstream& ss , const T& t ) {
+ ss << "<td>" << t << "</td>";
+ }
+
+ inline string table(const char *headers[] = 0, bool border = true) {
+ stringstream ss;
+ ss << "\n<table "
+ << (border?"border=1 ":"")
+ << "cellpadding=2 cellspacing=0>\n";
+ if( headers ) {
+ ss << "<tr>";
+ while( *headers ) {
+ ss << "<th>" << *headers << "</th>";
+ headers++;
+ }
+ ss << "</tr>\n";
+ }
+ return ss.str();
+ }
+
+ inline string start(string title) {
+ stringstream ss;
+ ss << "<html><head>\n<title>";
+ ss << title;
+ ss << "</title>\n";
+
+ ss << "<style type=\"text/css\" media=\"screen\">"
+ "body { font-family: helvetica, arial, san-serif }\n"
+ "table { border-collapse:collapse; border-color:#999; margin-top:.5em }\n"
+ "th { background-color:#bbb; color:#000 }\n"
+ "td,th { padding:.25em }\n"
+ "</style>\n";
+
+ ss << "</head>\n<body>\n";
+ return ss.str();
+ }
+
+ inline string red(string contentHtml, bool color=true) {
+ if( !color ) return contentHtml;
+ stringstream ss;
+ ss << "<span style=\"color:#A00;\">" << contentHtml << "</span>";
+ return ss.str();
+ }
+ inline string grey(string contentHtml, bool color=true) {
+ if( !color ) return contentHtml;
+ stringstream ss;
+ ss << "<span style=\"color:#888;\">" << contentHtml << "</span>";
+ return ss.str();
+ }
+ inline string blue(string contentHtml, bool color=true) {
+ if( !color ) return contentHtml;
+ stringstream ss;
+ ss << "<span style=\"color:#00A;\">" << contentHtml << "</span>";
+ return ss.str();
+ }
+ inline string yellow(string contentHtml, bool color=true) {
+ if( !color ) return contentHtml;
+ stringstream ss;
+ ss << "<span style=\"color:#A80;\">" << contentHtml << "</span>";
+ return ss.str();
+ }
+ inline string green(string contentHtml, bool color=true) {
+ if( !color ) return contentHtml;
+ stringstream ss;
+ ss << "<span style=\"color:#0A0;\">" << contentHtml << "</span>";
+ return ss.str();
+ }
+
+ inline string p(string contentHtml) {
+ stringstream ss;
+ ss << "<p>" << contentHtml << "</p>\n";
+ return ss.str();
+ }
+
+ inline string h2(string contentHtml) {
+ stringstream ss;
+ ss << "<h2>" << contentHtml << "</h2>\n";
+ return ss.str();
+ }
+
+ /* does NOT escape the strings. */
+ inline string a(string href, string title="", string contentHtml = "") {
+ stringstream ss;
+ ss << "<a";
+ if( !href.empty() ) ss << " href=\"" << href << '"';
+ if( !title.empty() ) ss << " title=\"" << title << '"';
+ ss << '>';
+ if( !contentHtml.empty() ) {
+ ss << contentHtml << "</a>";
+ }
+ return ss.str();
+ }
+
+ }
+
+}
diff --git a/src/mongo/util/mongoutils/mongoutils.vcxproj b/src/mongo/util/mongoutils/mongoutils.vcxproj
new file mode 100755
index 00000000000..f6ec0935ca9
--- /dev/null
+++ b/src/mongo/util/mongoutils/mongoutils.vcxproj
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{7B84584E-92BC-4DB9-971B-A1A8F93E5053}</ProjectGuid>
+ <RootNamespace>mongoutils</RootNamespace>
+ <ProjectName>mongoutils test program</ProjectName>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup />
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <AdditionalIncludeDirectories>c:\boost;\boost</AdditionalIncludeDirectories>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="test.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="html.h" />
+ <ClInclude Include="str.h" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/src/mongo/util/mongoutils/mongoutils.vcxproj.filters b/src/mongo/util/mongoutils/mongoutils.vcxproj.filters
new file mode 100755
index 00000000000..84ecbffede0
--- /dev/null
+++ b/src/mongo/util/mongoutils/mongoutils.vcxproj.filters
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="test.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="html.h" />
+ <ClInclude Include="str.h" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/src/mongo/util/mongoutils/str.h b/src/mongo/util/mongoutils/str.h
new file mode 100644
index 00000000000..97b121b0068
--- /dev/null
+++ b/src/mongo/util/mongoutils/str.h
@@ -0,0 +1,216 @@
+// @file str.h
+
+/* Copyright 2010 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
+
+/* Things in the mongoutils namespace
+ (1) are not database specific, rather, true utilities
+ (2) are cross platform
+ (3) may require boost headers, but not libs
+ (4) are clean and easy to use in any c++ project without pulling in lots of other stuff
+
+ Note: within this module, we use int for all offsets -- there are no unsigned offsets
+ and no size_t's. If you need 3 gigabyte long strings, don't use this module.
+*/
+
+#include <string>
+#include <sstream>
+
+// this violates the README rules for mongoutils:
+#include "../../bson/util/builder.h"
+
+namespace mongoutils {
+
+ namespace str {
+
+ typedef std::string string;
+
+ /** the idea here is to make one liners easy. e.g.:
+
+ return str::stream() << 1 << ' ' << 2;
+
+ since the following doesn't work:
+
+ (stringstream() << 1).str();
+ */
+ class stream {
+ public:
+ mongo::StringBuilder ss;
+ template<class T>
+ stream& operator<<(const T& v) {
+ ss << v;
+ return *this;
+ }
+ operator std::string () const { return ss.str(); }
+ };
+
+ inline bool startsWith(const char *str, const char *prefix) {
+ const char *s = str;
+ const char *p = prefix;
+ while( *p ) {
+ if( *p != *s ) return false;
+ p++; s++;
+ }
+ return true;
+ }
+ inline bool startsWith(string s, string p) { return startsWith(s.c_str(), p.c_str()); }
+
+ // while these are trivial today use in case we do different wide char things later
+ inline bool startsWith(const char *p, char ch) { return *p == ch; }
+ inline bool startsWith(string s, char ch) { return startsWith(s.c_str(), ch); }
+
+ inline bool endsWith(string s, string p) {
+ int l = p.size();
+ int x = s.size();
+ if( x < l ) return false;
+ return strncmp(s.c_str()+x-l, p.c_str(), l) == 0;
+ }
+ inline bool endsWith(const char *s, char p) {
+ size_t len = strlen(s);
+ return len && s[len-1] == p;
+ }
+
+ inline bool equals( const char * a , const char * b ) { return strcmp( a , b ) == 0; }
+
+ /** find char x, and return rest of string thereafter, or "" if not found */
+ inline const char * after(const char *s, char x) {
+ const char *p = strchr(s, x);
+ return (p != 0) ? p+1 : "";
+ }
+ inline string after(const string& s, char x) {
+ const char *p = strchr(s.c_str(), x);
+ return (p != 0) ? string(p+1) : "";
+ }
+
+ /** find string x, and return rest of string thereafter, or "" if not found */
+ inline const char * after(const char *s, const char *x) {
+ const char *p = strstr(s, x);
+ return (p != 0) ? p+strlen(x) : "";
+ }
+ inline string after(string s, string x) {
+ const char *p = strstr(s.c_str(), x.c_str());
+ return (p != 0) ? string(p+x.size()) : "";
+ }
+
+ /** @return true if s contains x */
+ inline bool contains(string s, string x) {
+ return strstr(s.c_str(), x.c_str()) != 0;
+ }
+ inline bool contains(string s, char x) {
+ return strchr(s.c_str(), x) != 0;
+ }
+
+ /** @return everything before the character x, else entire string */
+ inline string before(const string& s, char x) {
+ const char *p = strchr(s.c_str(), x);
+ return (p != 0) ? s.substr(0, p-s.c_str()) : s;
+ }
+
+ /** @return everything before the string x, else entire string */
+ inline string before(const string& s, const string& x) {
+ const char *p = strstr(s.c_str(), x.c_str());
+ return (p != 0) ? s.substr(0, p-s.c_str()) : s;
+ }
+
+ /** check if if strings share a common starting prefix
+ @return offset of divergence (or length if equal). 0=nothing in common. */
+ inline int shareCommonPrefix(const char *p, const char *q) {
+ int ofs = 0;
+ while( 1 ) {
+ if( *p == 0 || *q == 0 )
+ break;
+ if( *p != *q )
+ break;
+ p++; q++; ofs++;
+ }
+ return ofs;
+ }
+ inline int shareCommonPrefix(const string &a, const string &b)
+ { return shareCommonPrefix(a.c_str(), b.c_str()); }
+
+ /** string to unsigned. zero if not a number. can end with non-num chars */
+ inline unsigned toUnsigned(const string& a) {
+ unsigned x = 0;
+ const char *p = a.c_str();
+ while( 1 ) {
+ if( !isdigit(*p) )
+ break;
+ x = x * 10 + (*p - '0');
+ p++;
+ }
+ return x;
+ }
+
+ /** split a string on a specific char. We don't split N times, just once
+ on the first occurrence. If char not present entire string is in L
+ and R is empty.
+ @return true if char found
+ */
+ inline bool splitOn(const string &s, char c, string& L, string& R) {
+ const char *start = s.c_str();
+ const char *p = strchr(start, c);
+ if( p == 0 ) {
+ L = s; R.clear();
+ return false;
+ }
+ L = string(start, p-start);
+ R = string(p+1);
+ return true;
+ }
+ /** split scanning reverse direction. Splits ONCE ONLY. */
+ inline bool rSplitOn(const string &s, char c, string& L, string& R) {
+ const char *start = s.c_str();
+ const char *p = strrchr(start, c);
+ if( p == 0 ) {
+ L = s; R.clear();
+ return false;
+ }
+ L = string(start, p-start);
+ R = string(p+1);
+ return true;
+ }
+
+ /** @return number of occurrences of c in s */
+ inline unsigned count( const string& s , char c ) {
+ unsigned n=0;
+ for ( unsigned i=0; i<s.size(); i++ )
+ if ( s[i] == c )
+ n++;
+ return n;
+ }
+
+ /** trim leading spaces. spaces only, not tabs etc. */
+ inline string ltrim(const string& s) {
+ const char *p = s.c_str();
+ while( *p == ' ' ) p++;
+ return p;
+ }
+
+ /** remove trailing chars in place */
+ inline void stripTrailing(string& s, const char *chars) {
+ string::iterator i = s.end();
+ while( s.begin() != i ) {
+ i--;
+ if( contains(chars, *i) ) {
+ s.erase(i);
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/src/mongo/util/mongoutils/test.cpp b/src/mongo/util/mongoutils/test.cpp
new file mode 100644
index 00000000000..45268c5ca49
--- /dev/null
+++ b/src/mongo/util/mongoutils/test.cpp
@@ -0,0 +1,45 @@
+/* @file test.cpp
+ utils/mongoutils/test.cpp
+ unit tests for mongoutils
+*/
+
+/*
+ * Copyright 2010 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.
+ */
+
+#include <assert.h>
+#include "str.h"
+#include "html.h"
+
+using namespace std;
+using namespace mongoutils;
+
+int main() {
+ {
+ string s = "abcde";
+ str::stripTrailing(s, "ef");
+ assert( s == "abcd" );
+ str::stripTrailing(s, "abcd");
+ assert( s.empty() );
+ s = "abcddd";
+ str::stripTrailing(s, "d");
+ assert( s == "abc" );
+ }
+
+ string x = str::after("abcde", 'c');
+ assert( x == "de" );
+ assert( str::after("abcde", 'x') == "" );
+ return 0;
+}
diff --git a/src/mongo/util/moveablebuffer.h b/src/mongo/util/moveablebuffer.h
new file mode 100644
index 00000000000..e01f2d8d9a4
--- /dev/null
+++ b/src/mongo/util/moveablebuffer.h
@@ -0,0 +1,51 @@
+/* moveablebuffer.h
+*/
+
+/* 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
+
+namespace mongo {
+
+ /** this is a sort of smart pointer class where we can move where something is and all the pointers will adjust.
+ not threadsafe.
+ */
+ struct MoveableBuffer {
+ MoveableBuffer();
+ MoveableBuffer(void *);
+ MoveableBuffer& operator=(const MoveableBuffer&);
+ ~MoveableBuffer();
+
+ void *p;
+ };
+
+ /* implementation (inlines) below */
+
+ // this is a temp stub implementation...not really done yet - just having everything compile & such for checkpointing into git
+
+ inline MoveableBuffer::MoveableBuffer() : p(0) { }
+
+ inline MoveableBuffer::MoveableBuffer(void *_p) : p(_p) { }
+
+ inline MoveableBuffer& MoveableBuffer::operator=(const MoveableBuffer& r) {
+ p = r.p;
+ return *this;
+ }
+
+ inline MoveableBuffer::~MoveableBuffer() {
+ }
+
+}
diff --git a/src/mongo/util/net/hostandport.h b/src/mongo/util/net/hostandport.h
new file mode 100644
index 00000000000..3f4a64b79a9
--- /dev/null
+++ b/src/mongo/util/net/hostandport.h
@@ -0,0 +1,239 @@
+// hostandport.h
+
+/* 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 "sock.h"
+#include "../../db/cmdline.h"
+#include "../mongoutils/str.h"
+
+namespace mongo {
+
+ using namespace mongoutils;
+
+ void dynHostResolve(string& name, int& port);
+ string dynHostMyName();
+
+ /** helper for manipulating host:port connection endpoints.
+ */
+ struct HostAndPort {
+ HostAndPort() : _port(-1) { }
+
+ /** From a string hostname[:portnumber] or a #dynname
+ Throws user assertion if bad config string or bad port #.
+ */
+ HostAndPort(string s);
+
+ /** @param p port number. -1 is ok to use default. */
+ HostAndPort(string h, int p /*= -1*/) : _host(h), _port(p) {
+ assert( !str::startsWith(h, '#') );
+ }
+
+ HostAndPort(const SockAddr& sock ) : _host( sock.getAddr() ) , _port( sock.getPort() ) { }
+
+ static HostAndPort me() { return HostAndPort("localhost", cmdLine.port); }
+
+ /* uses real hostname instead of localhost */
+ static HostAndPort Me();
+
+ bool operator<(const HostAndPort& r) const {
+ string h = host();
+ string rh = r.host();
+ if( h < rh )
+ return true;
+ if( h == rh )
+ return port() < r.port();
+ return false;
+ }
+
+ bool operator==(const HostAndPort& r) const {
+ return host() == r.host() && port() == r.port();
+ }
+
+ bool operator!=(const HostAndPort& r) const { return !(*this == r); }
+
+ /* returns true if the host/port combo identifies this process instance. */
+ bool isSelf() const; // defined in isself.cpp
+
+ bool isLocalHost() const;
+
+ /**
+ * @param includePort host:port if true, host otherwise
+ */
+ string toString( bool includePort=true ) const;
+
+ // get the logical name if using a #dynhostname instead of resolving to current actual name
+ string dynString() const;
+ string toStringLong() const;
+
+ operator string() const { return toString(); }
+
+ bool empty() const {
+ return _dynName.empty() && _host.empty() && _port < 0;
+ }
+ string host() const {
+ if( !dyn() )
+ return _host;
+ string h = _dynName;
+ int p;
+ dynHostResolve(h, p);
+ return h;
+ }
+ int port() const {
+ int p = -2;
+ if( dyn() ) {
+ string h = _dynName;
+ dynHostResolve(h,p);
+ }
+ else {
+ p = _port;
+ }
+ return p >= 0 ? p : CmdLine::DefaultDBPort;
+ }
+ bool hasPort() const {
+ int p = -2;
+ if( dyn() ) {
+ string h = _dynName;
+ dynHostResolve(h,p);
+ }
+ else {
+ p = _port;
+ }
+ return p >= 0;
+ }
+ void setPort( int port ) {
+ if( dyn() ) {
+ log() << "INFO skipping setPort() HostAndPort dyn()=true" << endl;
+ return;
+ }
+ _port = port;
+ }
+
+ private:
+ bool dyn() const { return !_dynName.empty(); }
+ void init(const char *);
+ // invariant (except full obj assignment):
+ string _dynName; // when this is set, _host and _port aren't used, rather, we look up the dyn info every time.
+ string _host;
+ int _port; // -1 indicates unspecified
+ };
+
+ inline HostAndPort HostAndPort::Me() {
+ {
+ string s = dynHostMyName();
+ if( !s.empty() )
+ return HostAndPort(s);
+ }
+
+ const char* ips = cmdLine.bind_ip.c_str();
+ while(*ips) {
+ string ip;
+ const char * comma = strchr(ips, ',');
+ if (comma) {
+ ip = string(ips, comma - ips);
+ ips = comma + 1;
+ }
+ else {
+ ip = string(ips);
+ ips = "";
+ }
+ HostAndPort h = HostAndPort(ip, cmdLine.port);
+ if (!h.isLocalHost()) {
+ return h;
+ }
+ }
+
+ string h = getHostName();
+ assert( !h.empty() );
+ assert( h != "localhost" );
+ return HostAndPort(h, cmdLine.port);
+ }
+
+ inline string HostAndPort::dynString() const {
+ return dyn() ? _dynName : toString();
+ }
+
+ inline string HostAndPort::toStringLong() const {
+ return _dynName + ':' + toString();
+ }
+
+ inline string HostAndPort::toString( bool includePort ) const {
+ string h = host();
+ int p = port();
+
+ if ( ! includePort )
+ return h;
+
+ stringstream ss;
+ ss << h;
+ if ( p != -1 ) {
+ ss << ':';
+#if defined(_DEBUG)
+ if( p >= 44000 && p < 44100 ) {
+ log() << "warning: special debug port 44xxx used" << endl;
+ ss << p+1;
+ }
+ else
+ ss << p;
+#else
+ ss << p;
+#endif
+ }
+ return ss.str();
+ }
+
+ inline bool HostAndPort::isLocalHost() const {
+ string _host = host();
+ return ( _host == "localhost"
+ || startsWith(_host.c_str(), "127.")
+ || _host == "::1"
+ || _host == "anonymous unix socket"
+ || _host.c_str()[0] == '/' // unix socket
+ );
+ }
+
+ inline void HostAndPort::init(const char *p) {
+ uassert(13110, "HostAndPort: bad host:port config string", *p);
+ assert( *p != '#' );
+ assert( _dynName.empty() );
+ const char *colon = strrchr(p, ':');
+ if( colon ) {
+ int port = atoi(colon+1);
+ uassert(13095, "HostAndPort: bad port #", port > 0);
+ _host = string(p,colon-p);
+ _port = port;
+ }
+ else {
+ // no port specified.
+ _host = p;
+ _port = -1;
+ }
+ }
+
+ inline HostAndPort::HostAndPort(string s) {
+ const char *p = s.c_str();
+ if( *p == '#' ) {
+ _dynName = s;
+ _port = -2;
+ _host = "invalid_hostname_dyn_in_use";
+ }
+ else {
+ init(p);
+ }
+ }
+
+}
diff --git a/src/mongo/util/net/httpclient.cpp b/src/mongo/util/net/httpclient.cpp
new file mode 100644
index 00000000000..16eaa0ae80a
--- /dev/null
+++ b/src/mongo/util/net/httpclient.cpp
@@ -0,0 +1,177 @@
+// httpclient.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "httpclient.h"
+#include "sock.h"
+#include "message.h"
+#include "message_port.h"
+#include "../mongoutils/str.h"
+#include "../../bson/util/builder.h"
+
+namespace mongo {
+
+ //#define HD(x) cout << x << endl;
+#define HD(x)
+
+
+ int HttpClient::get( string url , Result * result ) {
+ return _go( "GET" , url , 0 , result );
+ }
+
+ int HttpClient::post( string url , string data , Result * result ) {
+ return _go( "POST" , url , data.c_str() , result );
+ }
+
+ int HttpClient::_go( const char * command , string url , const char * body , Result * result ) {
+ bool ssl = false;
+ if ( url.find( "https://" ) == 0 ) {
+ ssl = true;
+ url = url.substr( 8 );
+ }
+ else {
+ uassert( 10271 , "invalid url" , url.find( "http://" ) == 0 );
+ url = url.substr( 7 );
+ }
+
+ string host , path;
+ if ( url.find( "/" ) == string::npos ) {
+ host = url;
+ path = "/";
+ }
+ else {
+ host = url.substr( 0 , url.find( "/" ) );
+ path = url.substr( url.find( "/" ) );
+ }
+
+
+ HD( "host [" << host << "]" );
+ HD( "path [" << path << "]" );
+
+ string server = host;
+ int port = ssl ? 443 : 80;
+
+ string::size_type idx = host.find( ":" );
+ if ( idx != string::npos ) {
+ server = host.substr( 0 , idx );
+ string t = host.substr( idx + 1 );
+ port = atoi( t.c_str() );
+ }
+
+ HD( "server [" << server << "]" );
+ HD( "port [" << port << "]" );
+
+ string req;
+ {
+ stringstream ss;
+ ss << command << " " << path << " HTTP/1.1\r\n";
+ ss << "Host: " << host << "\r\n";
+ ss << "Connection: Close\r\n";
+ ss << "User-Agent: mongodb http client\r\n";
+ if ( body ) {
+ ss << "Content-Length: " << strlen( body ) << "\r\n";
+ }
+ ss << "\r\n";
+ if ( body ) {
+ ss << body;
+ }
+
+ req = ss.str();
+ }
+
+ SockAddr addr( server.c_str() , port );
+ HD( "addr: " << addr.toString() );
+
+ Socket sock;
+ if ( ! sock.connect( addr ) )
+ return -1;
+
+ if ( ssl ) {
+#ifdef MONGO_SSL
+ _checkSSLManager();
+ sock.secure( _sslManager.get() );
+#else
+ uasserted( 15862 , "no ssl support" );
+#endif
+ }
+
+ {
+ const char * out = req.c_str();
+ int toSend = req.size();
+ sock.send( out , toSend, "_go" );
+ }
+
+ char buf[4096];
+ int got = sock.unsafe_recv( buf , 4096 );
+ buf[got] = 0;
+
+ int rc;
+ char version[32];
+ assert( sscanf( buf , "%s %d" , version , &rc ) == 2 );
+ HD( "rc: " << rc );
+
+ StringBuilder sb;
+ if ( result )
+ sb << buf;
+
+ while ( ( got = sock.unsafe_recv( buf , 4096 ) ) > 0) {
+ if ( result )
+ sb << buf;
+ }
+
+ if ( result ) {
+ result->_init( rc , sb.str() );
+ }
+
+ return rc;
+ }
+
+ void HttpClient::Result::_init( int code , string entire ) {
+ _code = code;
+ _entireResponse = entire;
+
+ while ( true ) {
+ size_t i = entire.find( '\n' );
+ if ( i == string::npos ) {
+ // invalid
+ break;
+ }
+
+ string h = entire.substr( 0 , i );
+ entire = entire.substr( i + 1 );
+
+ if ( h.size() && h[h.size()-1] == '\r' )
+ h = h.substr( 0 , h.size() - 1 );
+
+ if ( h.size() == 0 )
+ break;
+
+ i = h.find( ':' );
+ if ( i != string::npos )
+ _headers[h.substr(0,i)] = str::ltrim(h.substr(i+1));
+ }
+
+ _body = entire;
+ }
+
+#ifdef MONGO_SSL
+ void HttpClient::_checkSSLManager() {
+ _sslManager.reset( new SSLManager( true ) );
+ }
+#endif
+
+}
diff --git a/src/mongo/util/net/httpclient.h b/src/mongo/util/net/httpclient.h
new file mode 100644
index 00000000000..126969f4f70
--- /dev/null
+++ b/src/mongo/util/net/httpclient.h
@@ -0,0 +1,78 @@
+// httpclient.h
+
+/* 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 "../../pch.h"
+#include "sock.h"
+
+namespace mongo {
+
+ class HttpClient : boost::noncopyable {
+ public:
+
+ typedef map<string,string> Headers;
+
+ class Result {
+ public:
+ Result() {}
+
+ const string& getEntireResponse() const {
+ return _entireResponse;
+ }
+
+ Headers getHeaders() const {
+ return _headers;
+ }
+
+ const string& getBody() const {
+ return _body;
+ }
+
+ private:
+
+ void _init( int code , string entire );
+
+ int _code;
+ string _entireResponse;
+
+ Headers _headers;
+ string _body;
+
+ friend class HttpClient;
+ };
+
+ /**
+ * @return response code
+ */
+ int get( string url , Result * result = 0 );
+
+ /**
+ * @return response code
+ */
+ int post( string url , string body , Result * result = 0 );
+
+ private:
+ int _go( const char * command , string url , const char * body , Result * result );
+
+#ifdef MONGO_SSL
+ void _checkSSLManager();
+
+ scoped_ptr<SSLManager> _sslManager;
+#endif
+ };
+}
diff --git a/src/mongo/util/net/listen.cpp b/src/mongo/util/net/listen.cpp
new file mode 100644
index 00000000000..ec3e4fa0ee8
--- /dev/null
+++ b/src/mongo/util/net/listen.cpp
@@ -0,0 +1,394 @@
+// listen.h
+
+/* 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.
+ */
+
+
+#include "pch.h"
+#include "listen.h"
+#include "message_port.h"
+
+#ifndef _WIN32
+
+# ifndef __sunos__
+# include <ifaddrs.h>
+# endif
+# include <sys/resource.h>
+# include <sys/stat.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <netdb.h>
+#ifdef __openbsd__
+# include <sys/uio.h>
+#endif
+
+#else
+
+// errno doesn't work for winsock.
+#undef errno
+#define errno WSAGetLastError()
+
+#endif
+
+namespace mongo {
+
+
+ void checkTicketNumbers();
+
+
+ // ----- Listener -------
+
+ const Listener* Listener::_timeTracker;
+
+ vector<SockAddr> ipToAddrs(const char* ips, int port, bool useUnixSockets) {
+ vector<SockAddr> out;
+ if (*ips == '\0') {
+ out.push_back(SockAddr("0.0.0.0", port)); // IPv4 all
+
+ if (IPv6Enabled())
+ out.push_back(SockAddr("::", port)); // IPv6 all
+#ifndef _WIN32
+ if (useUnixSockets)
+ out.push_back(SockAddr(makeUnixSockPath(port).c_str(), port)); // Unix socket
+#endif
+ return out;
+ }
+
+ while(*ips) {
+ string ip;
+ const char * comma = strchr(ips, ',');
+ if (comma) {
+ ip = string(ips, comma - ips);
+ ips = comma + 1;
+ }
+ else {
+ ip = string(ips);
+ ips = "";
+ }
+
+ SockAddr sa(ip.c_str(), port);
+ out.push_back(sa);
+
+#ifndef _WIN32
+ if (useUnixSockets && (sa.getAddr() == "127.0.0.1" || sa.getAddr() == "0.0.0.0")) // only IPv4
+ out.push_back(SockAddr(makeUnixSockPath(port).c_str(), port));
+#endif
+ }
+ return out;
+
+ }
+
+ Listener::Listener(const string& name, const string &ip, int port, bool logConnect )
+ : _port(port), _name(name), _ip(ip), _logConnect(logConnect), _elapsedTime(0) {
+#ifdef MONGO_SSL
+ _ssl = 0;
+ _sslPort = 0;
+
+ if ( cmdLine.sslOnNormalPorts && cmdLine.sslServerManager ) {
+ secure( cmdLine.sslServerManager );
+ }
+#endif
+ }
+
+ Listener::~Listener() {
+ if ( _timeTracker == this )
+ _timeTracker = 0;
+ }
+
+#ifdef MONGO_SSL
+ void Listener::secure( SSLManager* manager ) {
+ _ssl = manager;
+ }
+
+ void Listener::addSecurePort( SSLManager* manager , int additionalPort ) {
+ _ssl = manager;
+ _sslPort = additionalPort;
+ }
+
+#endif
+
+ bool Listener::_setupSockets( const vector<SockAddr>& mine , vector<SOCKET>& socks ) {
+ for (vector<SockAddr>::const_iterator it=mine.begin(), end=mine.end(); it != end; ++it) {
+ const SockAddr& me = *it;
+
+ SOCKET sock = ::socket(me.getType(), SOCK_STREAM, 0);
+ massert( 15863 , str::stream() << "listen(): invalid socket? " << errnoWithDescription() , sock >= 0 );
+
+ if (me.getType() == AF_UNIX) {
+#if !defined(_WIN32)
+ if (unlink(me.getAddr().c_str()) == -1) {
+ int x = errno;
+ if (x != ENOENT) {
+ log() << "couldn't unlink socket file " << me << errnoWithDescription(x) << " skipping" << endl;
+ continue;
+ }
+ }
+#endif
+ }
+ else if (me.getType() == AF_INET6) {
+ // IPv6 can also accept IPv4 connections as mapped addresses (::ffff:127.0.0.1)
+ // That causes a conflict if we don't do set it to IPV6_ONLY
+ const int one = 1;
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (const char*) &one, sizeof(one));
+ }
+
+#if !defined(_WIN32)
+ {
+ const int one = 1;
+ if ( setsockopt( sock , SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0 )
+ out() << "Failed to set socket opt, SO_REUSEADDR" << endl;
+ }
+#endif
+
+ if ( ::bind(sock, me.raw(), me.addressSize) != 0 ) {
+ int x = errno;
+ error() << "listen(): bind() failed " << errnoWithDescription(x) << " for socket: " << me.toString() << endl;
+ if ( x == EADDRINUSE )
+ error() << " addr already in use" << endl;
+ closesocket(sock);
+ return false;
+ }
+
+#if !defined(_WIN32)
+ if (me.getType() == AF_UNIX) {
+ if (chmod(me.getAddr().c_str(), 0777) == -1) {
+ error() << "couldn't chmod socket file " << me << errnoWithDescription() << endl;
+ }
+ ListeningSockets::get()->addPath( me.getAddr() );
+ }
+#endif
+
+ if ( ::listen(sock, 128) != 0 ) {
+ error() << "listen(): listen() failed " << errnoWithDescription() << endl;
+ closesocket(sock);
+ return false;
+ }
+
+ ListeningSockets::get()->add( sock );
+
+ socks.push_back(sock);
+ }
+
+ return true;
+ }
+
+ void Listener::initAndListen() {
+ checkTicketNumbers();
+ vector<SOCKET> socks;
+ set<int> sslSocks;
+
+ { // normal sockets
+ vector<SockAddr> mine = ipToAddrs(_ip.c_str(), _port, (!cmdLine.noUnixSocket && useUnixSockets()));
+ if ( ! _setupSockets( mine , socks ) )
+ return;
+ }
+
+#ifdef MONGO_SSL
+ if ( _ssl && _sslPort > 0 ) {
+ unsigned prev = socks.size();
+
+ vector<SockAddr> mine = ipToAddrs(_ip.c_str(), _sslPort, false );
+ if ( ! _setupSockets( mine , socks ) )
+ return;
+
+ for ( unsigned i=prev; i<socks.size(); i++ ) {
+ sslSocks.insert( socks[i] );
+ }
+
+ }
+#endif
+
+ SOCKET maxfd = 0; // needed for select()
+ for ( unsigned i=0; i<socks.size(); i++ ) {
+ if ( socks[i] > maxfd )
+ maxfd = socks[i];
+ }
+
+#ifdef MONGO_SSL
+ if ( _ssl == 0 ) {
+ _logListen( _port , false );
+ }
+ else if ( _sslPort == 0 ) {
+ _logListen( _port , true );
+ }
+ else {
+ // both
+ _logListen( _port , false );
+ _logListen( _sslPort , true );
+ }
+#else
+ _logListen( _port , false );
+#endif
+
+ static long connNumber = 0;
+ struct timeval maxSelectTime;
+ while ( ! inShutdown() ) {
+ fd_set fds[1];
+ FD_ZERO(fds);
+
+ for (vector<SOCKET>::iterator it=socks.begin(), end=socks.end(); it != end; ++it) {
+ FD_SET(*it, fds);
+ }
+
+ maxSelectTime.tv_sec = 0;
+ maxSelectTime.tv_usec = 10000;
+ const int ret = select(maxfd+1, fds, NULL, NULL, &maxSelectTime);
+
+ if (ret == 0) {
+#if defined(__linux__)
+ _elapsedTime += ( 10000 - maxSelectTime.tv_usec ) / 1000;
+#else
+ _elapsedTime += 10;
+#endif
+ continue;
+ }
+
+ if (ret < 0) {
+ int x = errno;
+#ifdef EINTR
+ if ( x == EINTR ) {
+ log() << "select() signal caught, continuing" << endl;
+ continue;
+ }
+#endif
+ if ( ! inShutdown() )
+ log() << "select() failure: ret=" << ret << " " << errnoWithDescription(x) << endl;
+ return;
+ }
+
+#if defined(__linux__)
+ _elapsedTime += max(ret, (int)(( 10000 - maxSelectTime.tv_usec ) / 1000));
+#else
+ _elapsedTime += ret; // assume 1ms to grab connection. very rough
+#endif
+
+ for (vector<SOCKET>::iterator it=socks.begin(), end=socks.end(); it != end; ++it) {
+ if (! (FD_ISSET(*it, fds)))
+ continue;
+
+ SockAddr from;
+ int s = accept(*it, from.raw(), &from.addressSize);
+ if ( s < 0 ) {
+ int x = errno; // so no global issues
+ if ( x == ECONNABORTED || x == EBADF ) {
+ log() << "Listener on port " << _port << " aborted" << endl;
+ return;
+ }
+ if ( x == 0 && inShutdown() ) {
+ return; // socket closed
+ }
+ if( !inShutdown() ) {
+ log() << "Listener: accept() returns " << s << " " << errnoWithDescription(x) << endl;
+ if (x == EMFILE || x == ENFILE) {
+ // Connection still in listen queue but we can't accept it yet
+ error() << "Out of file descriptors. Waiting one second before trying to accept more connections." << warnings;
+ sleepsecs(1);
+ }
+ }
+ continue;
+ }
+ if (from.getType() != AF_UNIX)
+ disableNagle(s);
+ if ( _logConnect && ! cmdLine.quiet ){
+ int conns = connTicketHolder.used()+1;
+ const char* word = (conns == 1 ? " connection" : " connections");
+ log() << "connection accepted from " << from.toString() << " #" << ++connNumber << " (" << conns << word << " now open)" << endl;
+ }
+
+ Socket newSock = Socket(s, from);
+#ifdef MONGO_SSL
+ if ( _ssl && ( _sslPort == 0 || sslSocks.count(*it) ) ) {
+ newSock.secureAccepted( _ssl );
+ }
+#endif
+ accepted( newSock );
+ }
+ }
+ }
+
+ void Listener::_logListen( int port , bool ssl ) {
+ log() << _name << ( _name.size() ? " " : "" ) << "waiting for connections on port " << port << ( ssl ? " ssl" : "" ) << endl;
+ }
+
+
+ void Listener::accepted(Socket socket) {
+ accepted( new MessagingPort(socket) );
+ }
+
+ void Listener::accepted(MessagingPort *mp) {
+ assert(!"You must overwrite one of the accepted methods");
+ }
+
+ // ----- ListeningSockets -------
+
+ ListeningSockets* ListeningSockets::_instance = new ListeningSockets();
+
+ ListeningSockets* ListeningSockets::get() {
+ return _instance;
+ }
+
+ // ------ connection ticket and control ------
+
+ const int DEFAULT_MAX_CONN = 20000;
+ const int MAX_MAX_CONN = 20000;
+
+ int getMaxConnections() {
+#ifdef _WIN32
+ return DEFAULT_MAX_CONN;
+#else
+ struct rlimit limit;
+ assert( getrlimit(RLIMIT_NOFILE,&limit) == 0 );
+
+ int max = (int)(limit.rlim_cur * .8);
+
+ log(1) << "fd limit"
+ << " hard:" << limit.rlim_max
+ << " soft:" << limit.rlim_cur
+ << " max conn: " << max
+ << endl;
+
+ if ( max > MAX_MAX_CONN )
+ max = MAX_MAX_CONN;
+
+ return max;
+#endif
+ }
+
+ void checkTicketNumbers() {
+ int want = getMaxConnections();
+ int current = connTicketHolder.outof();
+ if ( current != DEFAULT_MAX_CONN ) {
+ if ( current < want ) {
+ // they want fewer than they can handle
+ // which is fine
+ log(1) << " only allowing " << current << " connections" << endl;
+ return;
+ }
+ if ( current > want ) {
+ log() << " --maxConns too high, can only handle " << want << endl;
+ }
+ }
+ connTicketHolder.resize( want );
+ }
+
+ TicketHolder connTicketHolder(DEFAULT_MAX_CONN);
+
+}
diff --git a/src/mongo/util/net/listen.h b/src/mongo/util/net/listen.h
new file mode 100644
index 00000000000..ca90e835b97
--- /dev/null
+++ b/src/mongo/util/net/listen.h
@@ -0,0 +1,190 @@
+// listen.h
+
+/* 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 "sock.h"
+
+namespace mongo {
+
+ class MessagingPort;
+
+ class Listener : boost::noncopyable {
+ public:
+
+ Listener(const string& name, const string &ip, int port, bool logConnect=true );
+
+ virtual ~Listener();
+
+#ifdef MONGO_SSL
+ /**
+ * make this an ssl socket
+ * ownership of SSLManager remains with the caller
+ */
+ void secure( SSLManager* manager );
+
+ void addSecurePort( SSLManager* manager , int additionalPort );
+#endif
+
+ void initAndListen(); // never returns unless error (start a thread)
+
+ /* spawn a thread, etc., then return */
+ virtual void accepted(Socket socket);
+ virtual void accepted(MessagingPort *mp);
+
+ const int _port;
+
+ /**
+ * @return a rough estimate of elapsed time since the server started
+ */
+ long long getMyElapsedTimeMillis() const { return _elapsedTime; }
+
+ void setAsTimeTracker() {
+ _timeTracker = this;
+ }
+
+ static const Listener* getTimeTracker() {
+ return _timeTracker;
+ }
+
+ static long long getElapsedTimeMillis() {
+ if ( _timeTracker )
+ return _timeTracker->getMyElapsedTimeMillis();
+
+ // should this assert or throw? seems like callers may not expect to get zero back, certainly not forever.
+ return 0;
+ }
+
+ private:
+ string _name;
+ string _ip;
+ bool _logConnect;
+ long long _elapsedTime;
+
+#ifdef MONGO_SSL
+ SSLManager* _ssl;
+ int _sslPort;
+#endif
+
+ /**
+ * @return true iff everything went ok
+ */
+ bool _setupSockets( const vector<SockAddr>& mine , vector<SOCKET>& socks );
+
+ void _logListen( int port , bool ssl );
+
+ static const Listener* _timeTracker;
+
+ virtual bool useUnixSockets() const { return false; }
+ };
+
+ /**
+ * keep track of elapsed time
+ * after a set amount of time, tells you to do something
+ * only in this file because depends on Listener
+ */
+ class ElapsedTracker {
+ public:
+ ElapsedTracker( int hitsBetweenMarks , int msBetweenMarks )
+ : _h( hitsBetweenMarks ) , _ms( msBetweenMarks ) , _pings(0) {
+ _last = Listener::getElapsedTimeMillis();
+ }
+
+ /**
+ * call this for every iteration
+ * returns true if one of the triggers has gone off
+ */
+ bool intervalHasElapsed() {
+ if ( ( ++_pings % _h ) == 0 ) {
+ _last = Listener::getElapsedTimeMillis();
+ return true;
+ }
+
+ long long now = Listener::getElapsedTimeMillis();
+ if ( now - _last > _ms ) {
+ _last = now;
+ return true;
+ }
+
+ return false;
+ }
+
+ private:
+ const int _h;
+ const int _ms;
+
+ unsigned long long _pings;
+
+ long long _last;
+
+ };
+
+ class ListeningSockets {
+ public:
+ ListeningSockets()
+ : _mutex("ListeningSockets")
+ , _sockets( new set<int>() )
+ , _socketPaths( new set<string>() )
+ { }
+ void add( int sock ) {
+ scoped_lock lk( _mutex );
+ _sockets->insert( sock );
+ }
+ void addPath( string path ) {
+ scoped_lock lk( _mutex );
+ _socketPaths->insert( path );
+ }
+ void remove( int sock ) {
+ scoped_lock lk( _mutex );
+ _sockets->erase( sock );
+ }
+ void closeAll() {
+ set<int>* sockets;
+ set<string>* paths;
+
+ {
+ scoped_lock lk( _mutex );
+ sockets = _sockets;
+ _sockets = new set<int>();
+ paths = _socketPaths;
+ _socketPaths = new set<string>();
+ }
+
+ for ( set<int>::iterator i=sockets->begin(); i!=sockets->end(); i++ ) {
+ int sock = *i;
+ log() << "closing listening socket: " << sock << endl;
+ closesocket( sock );
+ }
+
+ for ( set<string>::iterator i=paths->begin(); i!=paths->end(); i++ ) {
+ string path = *i;
+ log() << "removing socket file: " << path << endl;
+ ::remove( path.c_str() );
+ }
+ }
+ static ListeningSockets* get();
+ private:
+ mongo::mutex _mutex;
+ set<int>* _sockets;
+ set<string>* _socketPaths; // for unix domain sockets
+ static ListeningSockets* _instance;
+ };
+
+
+ extern TicketHolder connTicketHolder;
+
+}
diff --git a/src/mongo/util/net/message.cpp b/src/mongo/util/net/message.cpp
new file mode 100644
index 00000000000..a84e5c48c5c
--- /dev/null
+++ b/src/mongo/util/net/message.cpp
@@ -0,0 +1,64 @@
+// message.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+
+#include "message.h"
+#include "message_port.h"
+#include "listen.h"
+
+#include "../goodies.h"
+#include "../../client/dbclient.h"
+
+namespace mongo {
+
+ void Message::send( MessagingPort &p, const char *context ) {
+ if ( empty() ) {
+ return;
+ }
+ if ( _buf != 0 ) {
+ p.send( (char*)_buf, _buf->len, context );
+ }
+ else {
+ p.send( _data, context );
+ }
+ }
+
+ MSGID NextMsgId;
+
+ /*struct MsgStart {
+ MsgStart() {
+ NextMsgId = (((unsigned) time(0)) << 16) ^ curTimeMillis();
+ assert(MsgDataHeaderSize == 16);
+ }
+ } msgstart;*/
+
+ MSGID nextMessageId() {
+ MSGID msgid = NextMsgId++;
+ return msgid;
+ }
+
+ bool doesOpGetAResponse( int op ) {
+ return op == dbQuery || op == dbGetMore;
+ }
+
+
+} // namespace mongo
diff --git a/src/mongo/util/net/message.h b/src/mongo/util/net/message.h
new file mode 100644
index 00000000000..14b0241f21d
--- /dev/null
+++ b/src/mongo/util/net/message.h
@@ -0,0 +1,312 @@
+// message.h
+
+/* 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 "sock.h"
+#include "../../bson/util/atomic_int.h"
+#include "hostandport.h"
+
+namespace mongo {
+
+ class Message;
+ class MessagingPort;
+ class PiggyBackData;
+
+ typedef AtomicUInt MSGID;
+
+ enum Operations {
+ opReply = 1, /* reply. responseTo is set. */
+ dbMsg = 1000, /* generic msg command followed by a string */
+ dbUpdate = 2001, /* update object */
+ dbInsert = 2002,
+ //dbGetByOID = 2003,
+ dbQuery = 2004,
+ dbGetMore = 2005,
+ dbDelete = 2006,
+ dbKillCursors = 2007
+ };
+
+ bool doesOpGetAResponse( int op );
+
+ inline const char * opToString( int op ) {
+ switch ( op ) {
+ case 0: return "none";
+ case opReply: return "reply";
+ case dbMsg: return "msg";
+ case dbUpdate: return "update";
+ case dbInsert: return "insert";
+ case dbQuery: return "query";
+ case dbGetMore: return "getmore";
+ case dbDelete: return "remove";
+ case dbKillCursors: return "killcursors";
+ default:
+ PRINT(op);
+ assert(0);
+ return "";
+ }
+ }
+
+ inline bool opIsWrite( int op ) {
+ switch ( op ) {
+
+ case 0:
+ case opReply:
+ case dbMsg:
+ case dbQuery:
+ case dbGetMore:
+ case dbKillCursors:
+ return false;
+
+ case dbUpdate:
+ case dbInsert:
+ case dbDelete:
+ return true;
+
+ default:
+ PRINT(op);
+ assert(0);
+ return "";
+ }
+
+ }
+
+#pragma pack(1)
+ /* see http://www.mongodb.org/display/DOCS/Mongo+Wire+Protocol
+ */
+ struct MSGHEADER {
+ int messageLength; // total message size, including this
+ int requestID; // identifier for this message
+ int responseTo; // requestID from the original request
+ // (used in reponses from db)
+ int opCode;
+ };
+ struct OP_GETMORE : public MSGHEADER {
+ MSGHEADER header; // standard message header
+ int ZERO_or_flags; // 0 - reserved for future use
+ //cstring fullCollectionName; // "dbname.collectionname"
+ //int32 numberToReturn; // number of documents to return
+ //int64 cursorID; // cursorID from the OP_REPLY
+ };
+#pragma pack()
+
+#pragma pack(1)
+ /* todo merge this with MSGHEADER (or inherit from it). */
+ struct MsgData {
+ int len; /* len of the msg, including this field */
+ MSGID id; /* request/reply id's match... */
+ MSGID responseTo; /* id of the message we are responding to */
+ short _operation;
+ char _flags;
+ char _version;
+ int operation() const {
+ return _operation;
+ }
+ void setOperation(int o) {
+ _flags = 0;
+ _version = 0;
+ _operation = o;
+ }
+ char _data[4];
+
+ int& dataAsInt() {
+ return *((int *) _data);
+ }
+
+ bool valid() {
+ if ( len <= 0 || len > ( 4 * BSONObjMaxInternalSize ) )
+ return false;
+ if ( _operation < 0 || _operation > 30000 )
+ return false;
+ return true;
+ }
+
+ long long getCursor() {
+ assert( responseTo > 0 );
+ assert( _operation == opReply );
+ long long * l = (long long *)(_data + 4);
+ return l[0];
+ }
+
+ int dataLen(); // len without header
+ };
+ const int MsgDataHeaderSize = sizeof(MsgData) - 4;
+ inline int MsgData::dataLen() {
+ return len - MsgDataHeaderSize;
+ }
+#pragma pack()
+
+ class Message {
+ public:
+ // we assume here that a vector with initial size 0 does no allocation (0 is the default, but wanted to make it explicit).
+ Message() : _buf( 0 ), _data( 0 ), _freeIt( false ) {}
+ Message( void * data , bool freeIt ) :
+ _buf( 0 ), _data( 0 ), _freeIt( false ) {
+ _setData( reinterpret_cast< MsgData* >( data ), freeIt );
+ };
+ Message(Message& r) : _buf( 0 ), _data( 0 ), _freeIt( false ) {
+ *this = r;
+ }
+ ~Message() {
+ reset();
+ }
+
+ SockAddr _from;
+
+ MsgData *header() const {
+ assert( !empty() );
+ return _buf ? _buf : reinterpret_cast< MsgData* > ( _data[ 0 ].first );
+ }
+ int operation() const { return header()->operation(); }
+
+ MsgData *singleData() const {
+ massert( 13273, "single data buffer expected", _buf );
+ return header();
+ }
+
+ bool empty() const { return !_buf && _data.empty(); }
+
+ int size() const {
+ int res = 0;
+ if ( _buf ) {
+ res = _buf->len;
+ }
+ else {
+ for (MsgVec::const_iterator it = _data.begin(); it != _data.end(); ++it) {
+ res += it->second;
+ }
+ }
+ return res;
+ }
+
+ int dataSize() const { return size() - sizeof(MSGHEADER); }
+
+ // concat multiple buffers - noop if <2 buffers already, otherwise can be expensive copy
+ // can get rid of this if we make response handling smarter
+ void concat() {
+ if ( _buf || empty() ) {
+ return;
+ }
+
+ assert( _freeIt );
+ int totalSize = 0;
+ for( vector< pair< char *, int > >::const_iterator i = _data.begin(); i != _data.end(); ++i ) {
+ totalSize += i->second;
+ }
+ char *buf = (char*)malloc( totalSize );
+ char *p = buf;
+ for( vector< pair< char *, int > >::const_iterator i = _data.begin(); i != _data.end(); ++i ) {
+ memcpy( p, i->first, i->second );
+ p += i->second;
+ }
+ reset();
+ _setData( (MsgData*)buf, true );
+ }
+
+ // vector swap() so this is fast
+ Message& operator=(Message& r) {
+ assert( empty() );
+ assert( r._freeIt );
+ _buf = r._buf;
+ r._buf = 0;
+ if ( r._data.size() > 0 ) {
+ _data.swap( r._data );
+ }
+ r._freeIt = false;
+ _freeIt = true;
+ return *this;
+ }
+
+ void reset() {
+ if ( _freeIt ) {
+ if ( _buf ) {
+ free( _buf );
+ }
+ for( vector< pair< char *, int > >::const_iterator i = _data.begin(); i != _data.end(); ++i ) {
+ free(i->first);
+ }
+ }
+ _buf = 0;
+ _data.clear();
+ _freeIt = false;
+ }
+
+ // use to add a buffer
+ // assumes message will free everything
+ void appendData(char *d, int size) {
+ if ( size <= 0 ) {
+ return;
+ }
+ if ( empty() ) {
+ MsgData *md = (MsgData*)d;
+ md->len = size; // can be updated later if more buffers added
+ _setData( md, true );
+ return;
+ }
+ assert( _freeIt );
+ if ( _buf ) {
+ _data.push_back( make_pair( (char*)_buf, _buf->len ) );
+ _buf = 0;
+ }
+ _data.push_back( make_pair( d, size ) );
+ header()->len += size;
+ }
+
+ // use to set first buffer if empty
+ void setData(MsgData *d, bool freeIt) {
+ assert( empty() );
+ _setData( d, freeIt );
+ }
+ void setData(int operation, const char *msgtxt) {
+ setData(operation, msgtxt, strlen(msgtxt)+1);
+ }
+ void setData(int operation, const char *msgdata, size_t len) {
+ assert( empty() );
+ size_t dataLen = len + sizeof(MsgData) - 4;
+ MsgData *d = (MsgData *) malloc(dataLen);
+ memcpy(d->_data, msgdata, len);
+ d->len = fixEndian(dataLen);
+ d->setOperation(operation);
+ _setData( d, true );
+ }
+
+ bool doIFreeIt() {
+ return _freeIt;
+ }
+
+ void send( MessagingPort &p, const char *context );
+
+ string toString() const;
+
+ private:
+ void _setData( MsgData *d, bool freeIt ) {
+ _freeIt = freeIt;
+ _buf = d;
+ }
+ // if just one buffer, keep it in _buf, otherwise keep a sequence of buffers in _data
+ MsgData * _buf;
+ // byte buffer(s) - the first must contain at least a full MsgData unless using _buf for storage instead
+ typedef vector< pair< char*, int > > MsgVec;
+ MsgVec _data;
+ bool _freeIt;
+ };
+
+
+ MSGID nextMessageId();
+
+
+} // namespace mongo
diff --git a/src/mongo/util/net/message_port.cpp b/src/mongo/util/net/message_port.cpp
new file mode 100644
index 00000000000..c342ed3c8b7
--- /dev/null
+++ b/src/mongo/util/net/message_port.cpp
@@ -0,0 +1,303 @@
+// message_port.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+
+#include "message.h"
+#include "message_port.h"
+#include "listen.h"
+
+#include "../goodies.h"
+#include "../background.h"
+#include "../time_support.h"
+#include "../../db/cmdline.h"
+#include "../../client/dbclient.h"
+
+
+#ifndef _WIN32
+# ifndef __sunos__
+# include <ifaddrs.h>
+# endif
+# include <sys/resource.h>
+# include <sys/stat.h>
+#else
+
+// errno doesn't work for winsock.
+#undef errno
+#define errno WSAGetLastError()
+
+#endif
+
+namespace mongo {
+
+
+// if you want trace output:
+#define mmm(x)
+
+ /* messagingport -------------------------------------------------------------- */
+
+ class PiggyBackData {
+ public:
+ PiggyBackData( MessagingPort * port ) {
+ _port = port;
+ _buf = new char[1300];
+ _cur = _buf;
+ }
+
+ ~PiggyBackData() {
+ DESTRUCTOR_GUARD (
+ flush();
+ delete[]( _cur );
+ );
+ }
+
+ void append( Message& m ) {
+ assert( m.header()->len <= 1300 );
+
+ if ( len() + m.header()->len > 1300 )
+ flush();
+
+ memcpy( _cur , m.singleData() , m.header()->len );
+ _cur += m.header()->len;
+ }
+
+ void flush() {
+ if ( _buf == _cur )
+ return;
+
+ _port->send( _buf , len(), "flush" );
+ _cur = _buf;
+ }
+
+ int len() const { return _cur - _buf; }
+
+ private:
+ MessagingPort* _port;
+ char * _buf;
+ char * _cur;
+ };
+
+ class Ports {
+ set<MessagingPort*> ports;
+ mongo::mutex m;
+ public:
+ Ports() : ports(), m("Ports") {}
+ void closeAll(unsigned skip_mask) {
+ scoped_lock bl(m);
+ for ( set<MessagingPort*>::iterator i = ports.begin(); i != ports.end(); i++ ) {
+ if( (*i)->tag & skip_mask )
+ continue;
+ (*i)->shutdown();
+ }
+ }
+ void insert(MessagingPort* p) {
+ scoped_lock bl(m);
+ ports.insert(p);
+ }
+ void erase(MessagingPort* p) {
+ scoped_lock bl(m);
+ ports.erase(p);
+ }
+ };
+
+ // we "new" this so it is still be around when other automatic global vars
+ // are being destructed during termination.
+ Ports& ports = *(new Ports());
+
+ void MessagingPort::closeAllSockets(unsigned mask) {
+ ports.closeAll(mask);
+ }
+
+ MessagingPort::MessagingPort(int fd, const SockAddr& remote)
+ : Socket( fd , remote ) , piggyBackData(0) {
+ ports.insert(this);
+ }
+
+ MessagingPort::MessagingPort( double timeout, int ll )
+ : Socket( timeout, ll ) {
+ ports.insert(this);
+ piggyBackData = 0;
+ }
+
+ MessagingPort::MessagingPort( Socket& sock )
+ : Socket( sock ) , piggyBackData( 0 ) {
+ ports.insert(this);
+ }
+
+ void MessagingPort::shutdown() {
+ close();
+ }
+
+ MessagingPort::~MessagingPort() {
+ if ( piggyBackData )
+ delete( piggyBackData );
+ shutdown();
+ ports.erase(this);
+ }
+
+ bool MessagingPort::recv(Message& m) {
+ try {
+again:
+ mmm( log() << "* recv() sock:" << this->sock << endl; )
+ int len = -1;
+
+ char *lenbuf = (char *) &len;
+ int lft = 4;
+ Socket::recv( lenbuf, lft );
+
+ if ( len < 16 || len > 48000000 ) { // messages must be large enough for headers
+ if ( len == -1 ) {
+ // Endian check from the client, after connecting, to see what mode server is running in.
+ unsigned foo = 0x10203040;
+ send( (char *) &foo, 4, "endian" );
+ goto again;
+ }
+
+ if ( len == 542393671 ) {
+ // an http GET
+ log(_logLevel) << "looks like you're trying to access db over http on native driver port. please add 1000 for webserver" << endl;
+ string msg = "You are trying to access MongoDB on the native driver port. For http diagnostic access, add 1000 to the port number\n";
+ stringstream ss;
+ ss << "HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Type: text/plain\r\nContent-Length: " << msg.size() << "\r\n\r\n" << msg;
+ string s = ss.str();
+ send( s.c_str(), s.size(), "http" );
+ return false;
+ }
+ log(0) << "recv(): message len " << len << " is too large" << len << endl;
+ return false;
+ }
+
+ int z = (len+1023)&0xfffffc00;
+ assert(z>=len);
+ MsgData *md = (MsgData *) malloc(z);
+ assert(md);
+ md->len = len;
+
+ char *p = (char *) &md->id;
+ int left = len -4;
+
+ try {
+ Socket::recv( p, left );
+ }
+ catch (...) {
+ free(md);
+ throw;
+ }
+
+ m.setData(md, true);
+ return true;
+
+ }
+ catch ( const SocketException & e ) {
+ log(_logLevel + (e.shouldPrint() ? 0 : 1) ) << "SocketException: remote: " << remote() << " error: " << e << endl;
+ m.reset();
+ return false;
+ }
+ }
+
+ void MessagingPort::reply(Message& received, Message& response) {
+ say(/*received.from, */response, received.header()->id);
+ }
+
+ void MessagingPort::reply(Message& received, Message& response, MSGID responseTo) {
+ say(/*received.from, */response, responseTo);
+ }
+
+ bool MessagingPort::call(Message& toSend, Message& response) {
+ mmm( log() << "*call()" << endl; )
+ say(toSend);
+ return recv( toSend , response );
+ }
+
+ bool MessagingPort::recv( const Message& toSend , Message& response ) {
+ while ( 1 ) {
+ bool ok = recv(response);
+ if ( !ok )
+ return false;
+ //log() << "got response: " << response.data->responseTo << endl;
+ if ( response.header()->responseTo == toSend.header()->id )
+ break;
+ error() << "MessagingPort::call() wrong id got:" << hex << (unsigned)response.header()->responseTo << " expect:" << (unsigned)toSend.header()->id << '\n'
+ << dec
+ << " toSend op: " << (unsigned)toSend.operation() << '\n'
+ << " response msgid:" << (unsigned)response.header()->id << '\n'
+ << " response len: " << (unsigned)response.header()->len << '\n'
+ << " response op: " << response.operation() << '\n'
+ << " remote: " << remoteString() << endl;
+ assert(false);
+ response.reset();
+ }
+ mmm( log() << "*call() end" << endl; )
+ return true;
+ }
+
+ void MessagingPort::assertStillConnected() {
+ uassert(15901, "client disconnected during operation", Socket::stillConnected());
+ }
+
+ void MessagingPort::say(Message& toSend, int responseTo) {
+ assert( !toSend.empty() );
+ mmm( log() << "* say() sock:" << this->sock << " thr:" << GetCurrentThreadId() << endl; )
+ toSend.header()->id = nextMessageId();
+ toSend.header()->responseTo = responseTo;
+
+ if ( piggyBackData && piggyBackData->len() ) {
+ mmm( log() << "* have piggy back" << endl; )
+ if ( ( piggyBackData->len() + toSend.header()->len ) > 1300 ) {
+ // won't fit in a packet - so just send it off
+ piggyBackData->flush();
+ }
+ else {
+ piggyBackData->append( toSend );
+ piggyBackData->flush();
+ return;
+ }
+ }
+
+ toSend.send( *this, "say" );
+ }
+
+ void MessagingPort::piggyBack( Message& toSend , int responseTo ) {
+
+ if ( toSend.header()->len > 1300 ) {
+ // not worth saving because its almost an entire packet
+ say( toSend );
+ return;
+ }
+
+ // we're going to be storing this, so need to set it up
+ toSend.header()->id = nextMessageId();
+ toSend.header()->responseTo = responseTo;
+
+ if ( ! piggyBackData )
+ piggyBackData = new PiggyBackData( this );
+
+ piggyBackData->append( toSend );
+ }
+
+ HostAndPort MessagingPort::remote() const {
+ if ( ! _remoteParsed.hasPort() )
+ _remoteParsed = HostAndPort( remoteAddr() );
+ return _remoteParsed;
+ }
+
+
+} // namespace mongo
diff --git a/src/mongo/util/net/message_port.h b/src/mongo/util/net/message_port.h
new file mode 100644
index 00000000000..5d404d84f8a
--- /dev/null
+++ b/src/mongo/util/net/message_port.h
@@ -0,0 +1,108 @@
+// message_port.h
+
+/* 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 "sock.h"
+#include "message.h"
+
+namespace mongo {
+
+ class MessagingPort;
+ class PiggyBackData;
+
+ typedef AtomicUInt MSGID;
+
+ class AbstractMessagingPort : boost::noncopyable {
+ public:
+ AbstractMessagingPort() : tag(0) {}
+ virtual ~AbstractMessagingPort() { }
+ virtual void reply(Message& received, Message& response, MSGID responseTo) = 0; // like the reply below, but doesn't rely on received.data still being available
+ virtual void reply(Message& received, Message& response) = 0;
+
+ virtual HostAndPort remote() const = 0;
+ virtual unsigned remotePort() const = 0;
+
+ virtual void assertStillConnected() = 0;
+
+ public:
+ // TODO make this private with some helpers
+
+ /* ports can be tagged with various classes. see closeAllSockets(tag). defaults to 0. */
+ unsigned tag;
+
+ };
+
+ class MessagingPort : public AbstractMessagingPort , public Socket {
+ public:
+ MessagingPort(int fd, const SockAddr& remote);
+
+ // in some cases the timeout will actually be 2x this value - eg we do a partial send,
+ // then the timeout fires, then we try to send again, then the timeout fires again with
+ // no data sent, then we detect that the other side is down
+ MessagingPort(double so_timeout = 0, int logLevel = 0 );
+
+ MessagingPort(Socket& socket);
+
+ virtual ~MessagingPort();
+
+ void shutdown();
+
+ /* it's assumed if you reuse a message object, that it doesn't cross MessagingPort's.
+ also, the Message data will go out of scope on the subsequent recv call.
+ */
+ bool recv(Message& m);
+ void reply(Message& received, Message& response, MSGID responseTo);
+ void reply(Message& received, Message& response);
+ bool call(Message& toSend, Message& response);
+
+ void say(Message& toSend, int responseTo = -1);
+
+ /**
+ * this is used for doing 'async' queries
+ * instead of doing call( to , from )
+ * you would do
+ * say( to )
+ * recv( from )
+ * Note: if you fail to call recv and someone else uses this port,
+ * horrible things will happend
+ */
+ bool recv( const Message& sent , Message& response );
+
+ void piggyBack( Message& toSend , int responseTo = -1 );
+
+ unsigned remotePort() const { return Socket::remotePort(); }
+ virtual HostAndPort remote() const;
+
+ void assertStillConnected();
+
+ private:
+
+ PiggyBackData * piggyBackData;
+
+ // this is the parsed version of remote
+ // mutable because its initialized only on call to remote()
+ mutable HostAndPort _remoteParsed;
+
+ public:
+ static void closeAllSockets(unsigned tagMask = 0xffffffff);
+
+ friend class PiggyBackData;
+ };
+
+
+} // namespace mongo
diff --git a/src/mongo/util/net/message_server.h b/src/mongo/util/net/message_server.h
new file mode 100644
index 00000000000..ae77b97bb0f
--- /dev/null
+++ b/src/mongo/util/net/message_server.h
@@ -0,0 +1,66 @@
+// message_server.h
+
+/* 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.
+ */
+
+/*
+ abstract database server
+ async io core, worker thread system
+ */
+
+#pragma once
+
+#include "../../pch.h"
+
+namespace mongo {
+
+ class MessageHandler {
+ public:
+ virtual ~MessageHandler() {}
+
+ /**
+ * called once when a socket is connected
+ */
+ virtual void connected( AbstractMessagingPort* p ) = 0;
+
+ /**
+ * called every time a message comes in
+ * handler is responsible for responding to client
+ */
+ virtual void process( Message& m , AbstractMessagingPort* p , LastError * err ) = 0;
+
+ /**
+ * called once when a socket is disconnected
+ */
+ virtual void disconnected( AbstractMessagingPort* p ) = 0;
+ };
+
+ class MessageServer {
+ public:
+ struct Options {
+ int port; // port to bind to
+ string ipList; // addresses to bind to
+
+ Options() : port(0), ipList("") {}
+ };
+
+ virtual ~MessageServer() {}
+ virtual void run() = 0;
+ virtual void setAsTimeTracker() = 0;
+ };
+
+ // TODO use a factory here to decide between port and asio variations
+ MessageServer * createServer( const MessageServer::Options& opts , MessageHandler * handler );
+}
diff --git a/src/mongo/util/net/message_server_asio.cpp b/src/mongo/util/net/message_server_asio.cpp
new file mode 100644
index 00000000000..0c6a7d925da
--- /dev/null
+++ b/src/mongo/util/net/message_server_asio.cpp
@@ -0,0 +1,261 @@
+// message_server_asio.cpp
+
+/* 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.
+ */
+
+#ifdef USE_ASIO
+
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <iostream>
+#include <vector>
+
+#include "message.h"
+#include "message_server.h"
+#include "../util/concurrency/mvar.h"
+
+using namespace boost;
+using namespace boost::asio;
+using namespace boost::asio::ip;
+
+namespace mongo {
+ class MessageServerSession;
+
+ namespace {
+ class StickyThread {
+ public:
+ StickyThread()
+ : _thread(boost::ref(*this))
+ {}
+
+ ~StickyThread() {
+ _mss.put(boost::shared_ptr<MessageServerSession>());
+ _thread.join();
+ }
+
+ void ready(boost::shared_ptr<MessageServerSession> mss) {
+ _mss.put(mss);
+ }
+
+ void operator() () {
+ boost::shared_ptr<MessageServerSession> mss;
+ while((mss = _mss.take())) { // intentionally not using ==
+ task(mss.get());
+ mss.reset();
+ }
+ }
+
+ private:
+ boost::thread _thread;
+ inline void task(MessageServerSession* mss); // must be defined after MessageServerSession
+
+ MVar<boost::shared_ptr<MessageServerSession> > _mss; // populated when given a task
+ };
+
+ vector<boost::shared_ptr<StickyThread> > thread_pool;
+ mongo::mutex tp_mutex; // this is only needed if io_service::run() is called from multiple threads
+ }
+
+ class MessageServerSession : public boost::enable_shared_from_this<MessageServerSession> , public AbstractMessagingPort {
+ public:
+ MessageServerSession( MessageHandler * handler , io_service& ioservice )
+ : _handler( handler )
+ , _socket( ioservice )
+ , _portCache(0)
+ { }
+
+ ~MessageServerSession() {
+ cout << "disconnect from: " << _socket.remote_endpoint() << endl;
+ }
+
+ tcp::socket& socket() {
+ return _socket;
+ }
+
+ void start() {
+ cout << "MessageServerSession start from:" << _socket.remote_endpoint() << endl;
+ _startHeaderRead();
+ }
+
+ void handleReadHeader( const boost::system::error_code& error ) {
+ if ( _inHeader.len == 0 )
+ return;
+
+ if ( ! _inHeader.valid() ) {
+ cout << " got invalid header from: " << _socket.remote_endpoint() << " closing connected" << endl;
+ return;
+ }
+
+ char * raw = (char*)malloc( _inHeader.len );
+
+ MsgData * data = (MsgData*)raw;
+ memcpy( data , &_inHeader , sizeof( _inHeader ) );
+ assert( data->len == _inHeader.len );
+
+ uassert( 10273 , "_cur not empty! pipelining requests not supported" , ! _cur.data );
+
+ _cur.setData( data , true );
+ async_read( _socket ,
+ buffer( raw + sizeof( _inHeader ) , _inHeader.len - sizeof( _inHeader ) ) ,
+ boost::bind( &MessageServerSession::handleReadBody , shared_from_this() , boost::asio::placeholders::error ) );
+ }
+
+ void handleReadBody( const boost::system::error_code& error ) {
+ if (!_myThread) {
+ mongo::mutex::scoped_lock(tp_mutex);
+ if (!thread_pool.empty()) {
+ _myThread = thread_pool.back();
+ thread_pool.pop_back();
+ }
+ }
+
+ if (!_myThread) // pool is empty
+ _myThread.reset(new StickyThread());
+
+ assert(_myThread);
+
+ _myThread->ready(shared_from_this());
+ }
+
+ void process() {
+ _handler->process( _cur , this );
+
+ if (_reply.data) {
+ async_write( _socket ,
+ buffer( (char*)_reply.data , _reply.data->len ) ,
+ boost::bind( &MessageServerSession::handleWriteDone , shared_from_this() , boost::asio::placeholders::error ) );
+ }
+ else {
+ _cur.reset();
+ _startHeaderRead();
+ }
+ }
+
+ void handleWriteDone( const boost::system::error_code& error ) {
+ {
+ // return thread to pool after we have sent data to the client
+ mongo::mutex::scoped_lock(tp_mutex);
+ assert(_myThread);
+ thread_pool.push_back(_myThread);
+ _myThread.reset();
+ }
+ _cur.reset();
+ _reply.reset();
+ _startHeaderRead();
+ }
+
+ virtual void reply( Message& received, Message& response ) {
+ reply( received , response , received.data->id );
+ }
+
+ virtual void reply( Message& query , Message& toSend, MSGID responseTo ) {
+ _reply = toSend;
+
+ _reply.data->id = nextMessageId();
+ _reply.data->responseTo = responseTo;
+ uassert( 10274 , "pipelining requests doesn't work yet" , query.data->id == _cur.data->id );
+ }
+
+
+ virtual unsigned remotePort() {
+ if (!_portCache)
+ _portCache = _socket.remote_endpoint().port(); //this is expensive
+ return _portCache;
+ }
+
+ private:
+
+ void _startHeaderRead() {
+ _inHeader.len = 0;
+ async_read( _socket ,
+ buffer( &_inHeader , sizeof( _inHeader ) ) ,
+ boost::bind( &MessageServerSession::handleReadHeader , shared_from_this() , boost::asio::placeholders::error ) );
+ }
+
+ MessageHandler * _handler;
+ tcp::socket _socket;
+ MsgData _inHeader;
+ Message _cur;
+ Message _reply;
+
+ unsigned _portCache;
+
+ boost::shared_ptr<StickyThread> _myThread;
+ };
+
+ void StickyThread::task(MessageServerSession* mss) {
+ mss->process();
+ }
+
+
+ class AsyncMessageServer : public MessageServer {
+ public:
+ // TODO accept an IP address to bind to
+ AsyncMessageServer( const MessageServer::Options& opts , MessageHandler * handler )
+ : _port( opts.port )
+ , _handler(handler)
+ , _endpoint( tcp::v4() , opts.port )
+ , _acceptor( _ioservice , _endpoint ) {
+ _accept();
+ }
+ virtual ~AsyncMessageServer() {
+
+ }
+
+ void run() {
+ cout << "AsyncMessageServer starting to listen on: " << _port << endl;
+ boost::thread other(boost::bind(&io_service::run, &_ioservice));
+ _ioservice.run();
+ cout << "AsyncMessageServer done listening on: " << _port << endl;
+ }
+
+ void handleAccept( shared_ptr<MessageServerSession> session ,
+ const boost::system::error_code& error ) {
+ if ( error ) {
+ cout << "handleAccept error!" << endl;
+ return;
+ }
+ session->start();
+ _accept();
+ }
+
+ void _accept( ) {
+ shared_ptr<MessageServerSession> session( new MessageServerSession( _handler , _ioservice ) );
+ _acceptor.async_accept( session->socket() ,
+ boost::bind( &AsyncMessageServer::handleAccept,
+ this,
+ session,
+ boost::asio::placeholders::error )
+ );
+ }
+
+ private:
+ int _port;
+ MessageHandler * _handler;
+ io_service _ioservice;
+ tcp::endpoint _endpoint;
+ tcp::acceptor _acceptor;
+ };
+
+ MessageServer * createServer( const MessageServer::Options& opts , MessageHandler * handler ) {
+ return new AsyncMessageServer( opts , handler );
+ }
+
+}
+
+#endif
diff --git a/src/mongo/util/net/message_server_port.cpp b/src/mongo/util/net/message_server_port.cpp
new file mode 100644
index 00000000000..7e6a731529b
--- /dev/null
+++ b/src/mongo/util/net/message_server_port.cpp
@@ -0,0 +1,204 @@
+// message_server_port.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+
+#ifndef USE_ASIO
+
+#include "message.h"
+#include "message_port.h"
+#include "message_server.h"
+#include "listen.h"
+
+#include "../../db/cmdline.h"
+#include "../../db/lasterror.h"
+#include "../../db/stats/counters.h"
+
+#ifdef __linux__ // TODO: consider making this ifndef _WIN32
+# include <sys/resource.h>
+#endif
+
+namespace mongo {
+
+ namespace pms {
+
+ MessageHandler * handler;
+
+ void threadRun( MessagingPort * inPort) {
+ TicketHolderReleaser connTicketReleaser( &connTicketHolder );
+
+ setThreadName( "conn" );
+
+ assert( inPort );
+ inPort->setLogLevel(1);
+ scoped_ptr<MessagingPort> p( inPort );
+
+ p->postFork();
+
+ string otherSide;
+
+ Message m;
+ try {
+ LastError * le = new LastError();
+ lastError.reset( le ); // lastError now has ownership
+
+ otherSide = p->remoteString();
+
+ handler->connected( p.get() );
+
+ while ( ! inShutdown() ) {
+ m.reset();
+ p->clearCounters();
+
+ if ( ! p->recv(m) ) {
+ if( !cmdLine.quiet ){
+ int conns = connTicketHolder.used()-1;
+ const char* word = (conns == 1 ? " connection" : " connections");
+ log() << "end connection " << otherSide << " (" << conns << word << " now open)" << endl;
+ }
+ p->shutdown();
+ break;
+ }
+
+ handler->process( m , p.get() , le );
+ networkCounter.hit( p->getBytesIn() , p->getBytesOut() );
+ }
+ }
+ catch ( AssertionException& e ) {
+ log() << "AssertionException handling request, closing client connection: " << e << endl;
+ p->shutdown();
+ }
+ catch ( SocketException& e ) {
+ log() << "SocketException handling request, closing client connection: " << e << endl;
+ p->shutdown();
+ }
+ catch ( const ClockSkewException & ) {
+ log() << "ClockSkewException - shutting down" << endl;
+ exitCleanly( EXIT_CLOCK_SKEW );
+ }
+ catch ( const DBException& e ) { // must be right above std::exception to avoid catching subclasses
+ log() << "DBException handling request, closing client connection: " << e << endl;
+ p->shutdown();
+ }
+ catch ( std::exception &e ) {
+ error() << "Uncaught std::exception: " << e.what() << ", terminating" << endl;
+ dbexit( EXIT_UNCAUGHT );
+ }
+ catch ( ... ) {
+ error() << "Uncaught exception, terminating" << endl;
+ dbexit( EXIT_UNCAUGHT );
+ }
+
+ handler->disconnected( p.get() );
+ }
+
+ }
+
+ class PortMessageServer : public MessageServer , public Listener {
+ public:
+ PortMessageServer( const MessageServer::Options& opts, MessageHandler * handler ) :
+ Listener( "" , opts.ipList, opts.port ) {
+
+ uassert( 10275 , "multiple PortMessageServer not supported" , ! pms::handler );
+ pms::handler = handler;
+ }
+
+ virtual void accepted(MessagingPort * p) {
+
+ if ( ! connTicketHolder.tryAcquire() ) {
+ log() << "connection refused because too many open connections: " << connTicketHolder.used() << endl;
+
+ // TODO: would be nice if we notified them...
+ p->shutdown();
+ delete p;
+
+ sleepmillis(2); // otherwise we'll hard loop
+ return;
+ }
+
+ try {
+#ifndef __linux__ // TODO: consider making this ifdef _WIN32
+ boost::thread thr( boost::bind( &pms::threadRun , p ) );
+#else
+ pthread_attr_t attrs;
+ pthread_attr_init(&attrs);
+ pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED);
+
+ static const size_t STACK_SIZE = 1024*1024; // if we change this we need to update the warning
+
+ struct rlimit limits;
+ verify(15887, getrlimit(RLIMIT_STACK, &limits) == 0);
+ if (limits.rlim_cur > STACK_SIZE) {
+ pthread_attr_setstacksize(&attrs, (DEBUG_BUILD
+ ? (STACK_SIZE / 2)
+ : STACK_SIZE));
+ } else if (limits.rlim_cur < 1024*1024) {
+ warning() << "Stack size set to " << (limits.rlim_cur/1024) << "KB. We suggest 1MB" << endl;
+ }
+
+
+ pthread_t thread;
+ int failed = pthread_create(&thread, &attrs, (void*(*)(void*)) &pms::threadRun, p);
+
+ pthread_attr_destroy(&attrs);
+
+ if (failed) {
+ log() << "pthread_create failed: " << errnoWithDescription(failed) << endl;
+ throw boost::thread_resource_error(); // for consistency with boost::thread
+ }
+#endif
+ }
+ catch ( boost::thread_resource_error& ) {
+ connTicketHolder.release();
+ log() << "can't create new thread, closing connection" << endl;
+
+ p->shutdown();
+ delete p;
+
+ sleepmillis(2);
+ }
+ catch ( ... ) {
+ connTicketHolder.release();
+ log() << "unknown error accepting new socket" << endl;
+
+ p->shutdown();
+ delete p;
+
+ sleepmillis(2);
+ }
+
+ }
+
+ virtual void setAsTimeTracker() {
+ Listener::setAsTimeTracker();
+ }
+
+ void run() {
+ initAndListen();
+ }
+
+ virtual bool useUnixSockets() const { return true; }
+ };
+
+
+ MessageServer * createServer( const MessageServer::Options& opts , MessageHandler * handler ) {
+ return new PortMessageServer( opts , handler );
+ }
+
+}
+
+#endif
diff --git a/src/mongo/util/net/miniwebserver.cpp b/src/mongo/util/net/miniwebserver.cpp
new file mode 100644
index 00000000000..f0b58569d22
--- /dev/null
+++ b/src/mongo/util/net/miniwebserver.cpp
@@ -0,0 +1,212 @@
+// miniwebserver.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "miniwebserver.h"
+#include "../hex.h"
+
+#include "pcrecpp.h"
+
+namespace mongo {
+
+ MiniWebServer::MiniWebServer(const string& name, const string &ip, int port)
+ : Listener(name, ip, port, false)
+ {}
+
+ string MiniWebServer::parseURL( const char * buf ) {
+ const char * urlStart = strchr( buf , ' ' );
+ if ( ! urlStart )
+ return "/";
+
+ urlStart++;
+
+ const char * end = strchr( urlStart , ' ' );
+ if ( ! end ) {
+ end = strchr( urlStart , '\r' );
+ if ( ! end ) {
+ end = strchr( urlStart , '\n' );
+ }
+ }
+
+ if ( ! end )
+ return "/";
+
+ int diff = (int)(end-urlStart);
+ if ( diff < 0 || diff > 255 )
+ return "/";
+
+ return string( urlStart , (int)(end-urlStart) );
+ }
+
+ void MiniWebServer::parseParams( BSONObj & params , string query ) {
+ if ( query.size() == 0 )
+ return;
+
+ BSONObjBuilder b;
+ while ( query.size() ) {
+
+ string::size_type amp = query.find( "&" );
+
+ string cur;
+ if ( amp == string::npos ) {
+ cur = query;
+ query = "";
+ }
+ else {
+ cur = query.substr( 0 , amp );
+ query = query.substr( amp + 1 );
+ }
+
+ string::size_type eq = cur.find( "=" );
+ if ( eq == string::npos )
+ continue;
+
+ b.append( urlDecode(cur.substr(0,eq)) , urlDecode(cur.substr(eq+1) ) );
+ }
+
+ params = b.obj();
+ }
+
+ string MiniWebServer::parseMethod( const char * headers ) {
+ const char * end = strchr( headers , ' ' );
+ if ( ! end )
+ return "GET";
+ return string( headers , (int)(end-headers) );
+ }
+
+ const char *MiniWebServer::body( const char *buf ) {
+ const char *ret = strstr( buf, "\r\n\r\n" );
+ return ret ? ret + 4 : ret;
+ }
+
+ bool MiniWebServer::fullReceive( const char *buf ) {
+ const char *bod = body( buf );
+ if ( !bod )
+ return false;
+ const char *lenString = "Content-Length:";
+ const char *lengthLoc = strstr( buf, lenString );
+ if ( !lengthLoc )
+ return true;
+ lengthLoc += strlen( lenString );
+ long len = strtol( lengthLoc, 0, 10 );
+ if ( long( strlen( bod ) ) == len )
+ return true;
+ return false;
+ }
+
+ void MiniWebServer::accepted(Socket sock) {
+ sock.postFork();
+ sock.setTimeout(8);
+ char buf[4096];
+ int len = 0;
+ while ( 1 ) {
+ int left = sizeof(buf) - 1 - len;
+ if( left == 0 )
+ break;
+ int x = sock.unsafe_recv( buf + len , left );
+ if ( x <= 0 ) {
+ sock.close();
+ return;
+ }
+ len += x;
+ buf[ len ] = 0;
+ if ( fullReceive( buf ) ) {
+ break;
+ }
+ }
+ buf[len] = 0;
+
+ string responseMsg;
+ int responseCode = 599;
+ vector<string> headers;
+
+ try {
+ doRequest(buf, parseURL( buf ), responseMsg, responseCode, headers, sock.remoteAddr() );
+ }
+ catch ( std::exception& e ) {
+ responseCode = 500;
+ responseMsg = "error loading page: ";
+ responseMsg += e.what();
+ }
+ catch ( ... ) {
+ responseCode = 500;
+ responseMsg = "unknown error loading page";
+ }
+
+ stringstream ss;
+ ss << "HTTP/1.0 " << responseCode;
+ if ( responseCode == 200 ) ss << " OK";
+ ss << "\r\n";
+ if ( headers.empty() ) {
+ ss << "Content-Type: text/html\r\n";
+ }
+ else {
+ for ( vector<string>::iterator i = headers.begin(); i != headers.end(); i++ ) {
+ assert( strncmp("Content-Length", i->c_str(), 14) );
+ ss << *i << "\r\n";
+ }
+ }
+ ss << "Connection: close\r\n";
+ ss << "Content-Length: " << responseMsg.size() << "\r\n";
+ ss << "\r\n";
+ ss << responseMsg;
+ string response = ss.str();
+
+ try {
+ sock.send( response.c_str(), response.size() , "http response" );
+ sock.close();
+ }
+ catch ( SocketException& e ) {
+ log(1) << "couldn't send data to http client: " << e << endl;
+ }
+ }
+
+ string MiniWebServer::getHeader( const char * req , string wanted ) {
+ const char * headers = strchr( req , '\n' );
+ if ( ! headers )
+ return "";
+ pcrecpp::StringPiece input( headers + 1 );
+
+ string name;
+ string val;
+ pcrecpp::RE re("([\\w\\-]+): (.*?)\r?\n");
+ while ( re.Consume( &input, &name, &val) ) {
+ if ( name == wanted )
+ return val;
+ }
+ return "";
+ }
+
+ string MiniWebServer::urlDecode(const char* s) {
+ stringstream out;
+ while(*s) {
+ if (*s == '+') {
+ out << ' ';
+ }
+ else if (*s == '%') {
+ out << fromHex(s+1);
+ s+=2;
+ }
+ else {
+ out << *s;
+ }
+ s++;
+ }
+ return out.str();
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/net/miniwebserver.h b/src/mongo/util/net/miniwebserver.h
new file mode 100644
index 00000000000..1fb6b3f2e65
--- /dev/null
+++ b/src/mongo/util/net/miniwebserver.h
@@ -0,0 +1,60 @@
+// miniwebserver.h
+
+/* 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 "../../pch.h"
+#include "message.h"
+#include "message_port.h"
+#include "listen.h"
+#include "../../db/jsobj.h"
+
+namespace mongo {
+
+ class MiniWebServer : public Listener {
+ public:
+ MiniWebServer(const string& name, const string &ip, int _port);
+ virtual ~MiniWebServer() {}
+
+ virtual void doRequest(
+ const char *rq, // the full request
+ string url,
+ // set these and return them:
+ string& responseMsg,
+ int& responseCode,
+ vector<string>& headers, // if completely empty, content-type: text/html will be added
+ const SockAddr &from
+ ) = 0;
+
+ // --- static helpers ----
+
+ static void parseParams( BSONObj & params , string query );
+
+ static string parseURL( const char * buf );
+ static string parseMethod( const char * headers );
+ static string getHeader( const char * headers , string name );
+ static const char *body( const char *buf );
+
+ static string urlDecode(const char* s);
+ static string urlDecode(string s) {return urlDecode(s.c_str());}
+
+ private:
+ void accepted(Socket socket);
+ static bool fullReceive( const char *buf );
+ };
+
+} // namespace mongo
diff --git a/src/mongo/util/net/sock.cpp b/src/mongo/util/net/sock.cpp
new file mode 100644
index 00000000000..bd08e6c64b9
--- /dev/null
+++ b/src/mongo/util/net/sock.cpp
@@ -0,0 +1,763 @@
+// @file sock.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "sock.h"
+#include "../background.h"
+#include "../concurrency/value.h"
+#include "../mongoutils/str.h"
+
+#if !defined(_WIN32)
+# include <sys/socket.h>
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/un.h>
+# include <netinet/in.h>
+# include <netinet/tcp.h>
+# include <arpa/inet.h>
+# include <errno.h>
+# include <netdb.h>
+# if defined(__openbsd__)
+# include <sys/uio.h>
+# endif
+#endif
+
+#ifdef MONGO_SSL
+#include <openssl/err.h>
+#include <openssl/ssl.h>
+#endif
+
+using namespace mongoutils;
+
+namespace mongo {
+
+ void dynHostResolve(string& name, int& port);
+ string dynHostMyName();
+
+ static bool ipv6 = false;
+ void enableIPv6(bool state) { ipv6 = state; }
+ bool IPv6Enabled() { return ipv6; }
+
+ void setSockTimeouts(int sock, double secs) {
+ struct timeval tv;
+ tv.tv_sec = (int)secs;
+ tv.tv_usec = (int)((long long)(secs*1000*1000) % (1000*1000));
+ bool report = logLevel > 3; // solaris doesn't provide these
+ DEV report = true;
+#if defined(_WIN32)
+ tv.tv_sec *= 1000; // Windows timeout is a DWORD, in milliseconds.
+ int status = setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv.tv_sec, sizeof(DWORD) ) == 0;
+ if( report && (status == SOCKET_ERROR) ) log() << "unable to set SO_RCVTIMEO" << endl;
+ status = setsockopt( sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &tv.tv_sec, sizeof(DWORD) ) == 0;
+ DEV if( report && (status == SOCKET_ERROR) ) log() << "unable to set SO_SNDTIMEO" << endl;
+#else
+ bool ok = setsockopt( sock, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof(tv) ) == 0;
+ if( report && !ok ) log() << "unable to set SO_RCVTIMEO" << endl;
+ ok = setsockopt( sock, SOL_SOCKET, SO_SNDTIMEO, (char *) &tv, sizeof(tv) ) == 0;
+ DEV if( report && !ok ) log() << "unable to set SO_SNDTIMEO" << endl;
+#endif
+ }
+
+#if defined(_WIN32)
+ void disableNagle(int sock) {
+ int x = 1;
+ if ( setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &x, sizeof(x)) )
+ error() << "disableNagle failed" << endl;
+ if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &x, sizeof(x)) )
+ error() << "SO_KEEPALIVE failed" << endl;
+ }
+#else
+
+ void disableNagle(int sock) {
+ int x = 1;
+
+#ifdef SOL_TCP
+ int level = SOL_TCP;
+#else
+ int level = SOL_SOCKET;
+#endif
+
+ if ( setsockopt(sock, level, TCP_NODELAY, (char *) &x, sizeof(x)) )
+ error() << "disableNagle failed: " << errnoWithDescription() << endl;
+
+#ifdef SO_KEEPALIVE
+ if ( setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &x, sizeof(x)) )
+ error() << "SO_KEEPALIVE failed: " << errnoWithDescription() << endl;
+
+# ifdef __linux__
+ socklen_t len = sizeof(x);
+ if ( getsockopt(sock, level, TCP_KEEPIDLE, (char *) &x, &len) )
+ error() << "can't get TCP_KEEPIDLE: " << errnoWithDescription() << endl;
+
+ if (x > 300) {
+ x = 300;
+ if ( setsockopt(sock, level, TCP_KEEPIDLE, (char *) &x, sizeof(x)) ) {
+ error() << "can't set TCP_KEEPIDLE: " << errnoWithDescription() << endl;
+ }
+ }
+
+ len = sizeof(x); // just in case it changed
+ if ( getsockopt(sock, level, TCP_KEEPINTVL, (char *) &x, &len) )
+ error() << "can't get TCP_KEEPINTVL: " << errnoWithDescription() << endl;
+
+ if (x > 300) {
+ x = 300;
+ if ( setsockopt(sock, level, TCP_KEEPINTVL, (char *) &x, sizeof(x)) ) {
+ error() << "can't set TCP_KEEPINTVL: " << errnoWithDescription() << endl;
+ }
+ }
+# endif
+#endif
+
+ }
+
+#endif
+
+ string getAddrInfoStrError(int code) {
+#if !defined(_WIN32)
+ return gai_strerror(code);
+#else
+ /* gai_strerrorA is not threadsafe on windows. don't use it. */
+ return errnoWithDescription(code);
+#endif
+ }
+
+
+ // --- SockAddr
+
+ SockAddr::SockAddr(int sourcePort) {
+ memset(as<sockaddr_in>().sin_zero, 0, sizeof(as<sockaddr_in>().sin_zero));
+ as<sockaddr_in>().sin_family = AF_INET;
+ as<sockaddr_in>().sin_port = htons(sourcePort);
+ as<sockaddr_in>().sin_addr.s_addr = htonl(INADDR_ANY);
+ addressSize = sizeof(sockaddr_in);
+ }
+
+ SockAddr::SockAddr(const char * _iporhost , int port) {
+ string target = _iporhost;
+ bool cloudName = *_iporhost == '#';
+ if( target == "localhost" ) {
+ target = "127.0.0.1";
+ }
+ else if( cloudName ) {
+ dynHostResolve(target, port);
+ }
+
+ if( str::contains(target, '/') ) {
+#ifdef _WIN32
+ uassert(13080, "no unix socket support on windows", false);
+#endif
+ uassert(13079, "path to unix socket too long", target.size() < sizeof(as<sockaddr_un>().sun_path));
+ as<sockaddr_un>().sun_family = AF_UNIX;
+ strcpy(as<sockaddr_un>().sun_path, target.c_str());
+ addressSize = sizeof(sockaddr_un);
+ }
+ else {
+ addrinfo* addrs = NULL;
+ addrinfo hints;
+ memset(&hints, 0, sizeof(addrinfo));
+ hints.ai_socktype = SOCK_STREAM;
+ //hints.ai_flags = AI_ADDRCONFIG; // This is often recommended but don't do it. SERVER-1579
+ hints.ai_flags |= AI_NUMERICHOST; // first pass tries w/o DNS lookup
+ hints.ai_family = (IPv6Enabled() ? AF_UNSPEC : AF_INET);
+
+ StringBuilder ss;
+ ss << port;
+ int ret = getaddrinfo(target.c_str(), ss.str().c_str(), &hints, &addrs);
+
+ // old C compilers on IPv6-capable hosts return EAI_NODATA error
+#ifdef EAI_NODATA
+ int nodata = (ret == EAI_NODATA);
+#else
+ int nodata = false;
+#endif
+ if ( (ret == EAI_NONAME || nodata) && !cloudName ) {
+ // iporhost isn't an IP address, allow DNS lookup
+ hints.ai_flags &= ~AI_NUMERICHOST;
+ ret = getaddrinfo(target.c_str(), ss.str().c_str(), &hints, &addrs);
+ }
+
+ if (ret) {
+ // we were unsuccessful
+ if( target != "0.0.0.0" ) { // don't log if this as it is a CRT construction and log() may not work yet.
+ log() << "getaddrinfo(\"" << target << "\") failed: " << gai_strerror(ret) << endl;
+ }
+ *this = SockAddr(port);
+ }
+ else {
+ //TODO: handle other addresses in linked list;
+ assert(addrs->ai_addrlen <= sizeof(sa));
+ memcpy(&sa, addrs->ai_addr, addrs->ai_addrlen);
+ addressSize = addrs->ai_addrlen;
+ freeaddrinfo(addrs);
+ }
+ }
+ }
+
+ bool SockAddr::isLocalHost() const {
+ switch (getType()) {
+ case AF_INET: return getAddr() == "127.0.0.1";
+ case AF_INET6: return getAddr() == "::1";
+ case AF_UNIX: return true;
+ default: return false;
+ }
+ assert(false);
+ return false;
+ }
+
+ string SockAddr::toString(bool includePort) const {
+ string out = getAddr();
+ if (includePort && getType() != AF_UNIX && getType() != AF_UNSPEC)
+ out += mongoutils::str::stream() << ':' << getPort();
+ return out;
+ }
+
+ sa_family_t SockAddr::getType() const {
+ return sa.ss_family;
+ }
+
+ unsigned SockAddr::getPort() const {
+ switch (getType()) {
+ case AF_INET: return ntohs(as<sockaddr_in>().sin_port);
+ case AF_INET6: return ntohs(as<sockaddr_in6>().sin6_port);
+ case AF_UNIX: return 0;
+ case AF_UNSPEC: return 0;
+ default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported address family", false); return 0;
+ }
+ }
+
+ string SockAddr::getAddr() const {
+ switch (getType()) {
+ case AF_INET:
+ case AF_INET6: {
+ const int buflen=128;
+ char buffer[buflen];
+ int ret = getnameinfo(raw(), addressSize, buffer, buflen, NULL, 0, NI_NUMERICHOST);
+ massert(13082, str::stream() << "getnameinfo error " << getAddrInfoStrError(ret), ret == 0);
+ return buffer;
+ }
+
+ case AF_UNIX: return (addressSize > 2 ? as<sockaddr_un>().sun_path : "anonymous unix socket");
+ case AF_UNSPEC: return "(NONE)";
+ default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported address family", false); return "";
+ }
+ }
+
+ bool SockAddr::operator==(const SockAddr& r) const {
+ if (getType() != r.getType())
+ return false;
+
+ if (getPort() != r.getPort())
+ return false;
+
+ switch (getType()) {
+ case AF_INET: return as<sockaddr_in>().sin_addr.s_addr == r.as<sockaddr_in>().sin_addr.s_addr;
+ case AF_INET6: return memcmp(as<sockaddr_in6>().sin6_addr.s6_addr, r.as<sockaddr_in6>().sin6_addr.s6_addr, sizeof(in6_addr)) == 0;
+ case AF_UNIX: return strcmp(as<sockaddr_un>().sun_path, r.as<sockaddr_un>().sun_path) == 0;
+ case AF_UNSPEC: return true; // assume all unspecified addresses are the same
+ default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported address family", false);
+ }
+ return false;
+ }
+
+ bool SockAddr::operator!=(const SockAddr& r) const {
+ return !(*this == r);
+ }
+
+ bool SockAddr::operator<(const SockAddr& r) const {
+ if (getType() < r.getType())
+ return true;
+ else if (getType() > r.getType())
+ return false;
+
+ if (getPort() < r.getPort())
+ return true;
+ else if (getPort() > r.getPort())
+ return false;
+
+ switch (getType()) {
+ case AF_INET: return as<sockaddr_in>().sin_addr.s_addr < r.as<sockaddr_in>().sin_addr.s_addr;
+ case AF_INET6: return memcmp(as<sockaddr_in6>().sin6_addr.s6_addr, r.as<sockaddr_in6>().sin6_addr.s6_addr, sizeof(in6_addr)) < 0;
+ case AF_UNIX: return strcmp(as<sockaddr_un>().sun_path, r.as<sockaddr_un>().sun_path) < 0;
+ case AF_UNSPEC: return false;
+ default: massert(SOCK_FAMILY_UNKNOWN_ERROR, "unsupported address family", false);
+ }
+ return false;
+ }
+
+ SockAddr unknownAddress( "0.0.0.0", 0 );
+
+ // If an ip address is passed in, just return that. If a hostname is passed
+ // in, look up its ip and return that. Returns "" on failure.
+ string hostbyname(const char *hostname) {
+ if( *hostname == '#' ) {
+ string s = hostname;
+ int port;
+ dynHostResolve(s, port);
+ return s;
+ }
+
+ string addr = SockAddr(hostname, 0).getAddr();
+ if (addr == "0.0.0.0")
+ return "";
+ else
+ return addr;
+ }
+
+ // --- my --
+
+ DiagStr _hostNameCached;
+
+ string getHostName() {
+ {
+ string s = dynHostMyName();
+ if( !s.empty() )
+ return s;
+ }
+
+ char buf[256];
+ int ec = gethostname(buf, 127);
+ if ( ec || *buf == 0 ) {
+ log() << "can't get this server's hostname " << errnoWithDescription() << endl;
+ return "";
+ }
+ return buf;
+ }
+
+ static void _hostNameCachedInit() {
+ _hostNameCached = getHostName();
+ }
+ boost::once_flag _hostNameCachedInitFlags = BOOST_ONCE_INIT;
+
+ /** we store our host name once */
+ // ok w dynhosts map?
+ string getHostNameCached() {
+ boost::call_once( _hostNameCachedInit , _hostNameCachedInitFlags );
+ return _hostNameCached;
+ }
+
+ // --------- SocketException ----------
+
+#ifdef MSG_NOSIGNAL
+ const int portSendFlags = MSG_NOSIGNAL;
+ const int portRecvFlags = MSG_NOSIGNAL;
+#else
+ const int portSendFlags = 0;
+ const int portRecvFlags = 0;
+#endif
+
+ string SocketException::toString() const {
+ stringstream ss;
+ ss << _ei.code << " socket exception [" << _type << "] ";
+
+ if ( _server.size() )
+ ss << "server [" << _server << "] ";
+
+ if ( _extra.size() )
+ ss << _extra;
+
+ return ss.str();
+ }
+
+
+ // ------------ SSLManager -----------------
+
+#ifdef MONGO_SSL
+ SSLManager::SSLManager( bool client ) {
+ _client = client;
+ SSL_library_init();
+ SSL_load_error_strings();
+ ERR_load_crypto_strings();
+
+ _context = SSL_CTX_new( client ? SSLv23_client_method() : SSLv23_server_method() );
+ massert( 15864 , mongoutils::str::stream() << "can't create SSL Context: " << ERR_error_string(ERR_get_error(), NULL) , _context );
+
+ SSL_CTX_set_options( _context, SSL_OP_ALL);
+ }
+
+ void SSLManager::setupPubPriv( const string& privateKeyFile , const string& publicKeyFile ) {
+ massert( 15865 ,
+ mongoutils::str::stream() << "Can't read SSL certificate from file "
+ << publicKeyFile << ":" << ERR_error_string(ERR_get_error(), NULL) ,
+ SSL_CTX_use_certificate_file(_context, publicKeyFile.c_str(), SSL_FILETYPE_PEM) );
+
+
+ massert( 15866 ,
+ mongoutils::str::stream() << "Can't read SSL private key from file "
+ << privateKeyFile << " : " << ERR_error_string(ERR_get_error(), NULL) ,
+ SSL_CTX_use_PrivateKey_file(_context, privateKeyFile.c_str(), SSL_FILETYPE_PEM) );
+ }
+
+
+ int SSLManager::password_cb(char *buf,int num, int rwflag,void *userdata){
+ SSLManager* sm = (SSLManager*)userdata;
+ string pass = sm->_password;
+ strcpy(buf,pass.c_str());
+ return(pass.size());
+ }
+
+ void SSLManager::setupPEM( const string& keyFile , const string& password ) {
+ _password = password;
+
+ massert( 15867 , "Can't read certificate file" , SSL_CTX_use_certificate_chain_file( _context , keyFile.c_str() ) );
+
+ SSL_CTX_set_default_passwd_cb_userdata( _context , this );
+ SSL_CTX_set_default_passwd_cb( _context, &SSLManager::password_cb );
+
+ massert( 15868 , "Can't read key file" , SSL_CTX_use_PrivateKey_file( _context , keyFile.c_str() , SSL_FILETYPE_PEM ) );
+ }
+
+ SSL * SSLManager::secure( int fd ) {
+ SSL * ssl = SSL_new( _context );
+ massert( 15861 , "can't create SSL" , ssl );
+ SSL_set_fd( ssl , fd );
+ return ssl;
+ }
+
+
+#endif
+
+ // ------------ Socket -----------------
+
+ Socket::Socket(int fd , const SockAddr& remote) :
+ _fd(fd), _remote(remote), _timeout(0) {
+ _logLevel = 0;
+ _init();
+ }
+
+ Socket::Socket( double timeout, int ll ) {
+ _logLevel = ll;
+ _fd = -1;
+ _timeout = timeout;
+ _init();
+ }
+
+ void Socket::_init() {
+ _bytesOut = 0;
+ _bytesIn = 0;
+#ifdef MONGO_SSL
+ _sslAccepted = 0;
+#endif
+ }
+
+ void Socket::close() {
+#ifdef MONGO_SSL
+ _ssl.reset();
+#endif
+ if ( _fd >= 0 ) {
+ closesocket( _fd );
+ _fd = -1;
+ }
+ }
+
+#ifdef MONGO_SSL
+ void Socket::secure( SSLManager * ssl ) {
+ assert( ssl );
+ assert( _fd >= 0 );
+ _ssl.reset( ssl->secure( _fd ) );
+ SSL_connect( _ssl.get() );
+ }
+
+ void Socket::secureAccepted( SSLManager * ssl ) {
+ _sslAccepted = ssl;
+ }
+#endif
+
+ void Socket::postFork() {
+#ifdef MONGO_SSL
+ if ( _sslAccepted ) {
+ assert( _fd );
+ _ssl.reset( _sslAccepted->secure( _fd ) );
+ SSL_accept( _ssl.get() );
+ _sslAccepted = 0;
+ }
+#endif
+ }
+
+ class ConnectBG : public BackgroundJob {
+ public:
+ ConnectBG(int sock, SockAddr remote) : _sock(sock), _remote(remote) { }
+
+ void run() { _res = ::connect(_sock, _remote.raw(), _remote.addressSize); }
+ string name() const { return "ConnectBG"; }
+ int inError() const { return _res; }
+
+ private:
+ int _sock;
+ int _res;
+ SockAddr _remote;
+ };
+
+ bool Socket::connect(SockAddr& remote) {
+ _remote = remote;
+
+ _fd = socket(remote.getType(), SOCK_STREAM, 0);
+ if ( _fd == INVALID_SOCKET ) {
+ log(_logLevel) << "ERROR: connect invalid socket " << errnoWithDescription() << endl;
+ return false;
+ }
+
+ if ( _timeout > 0 ) {
+ setTimeout( _timeout );
+ }
+
+ ConnectBG bg(_fd, remote);
+ bg.go();
+ if ( bg.wait(5000) ) {
+ if ( bg.inError() ) {
+ close();
+ return false;
+ }
+ }
+ else {
+ // time out the connect
+ close();
+ bg.wait(); // so bg stays in scope until bg thread terminates
+ return false;
+ }
+
+ if (remote.getType() != AF_UNIX)
+ disableNagle(_fd);
+
+#ifdef SO_NOSIGPIPE
+ // osx
+ const int one = 1;
+ setsockopt( _fd , SOL_SOCKET, SO_NOSIGPIPE, &one, sizeof(int));
+#endif
+
+ return true;
+ }
+
+ int Socket::_send( const char * data , int len ) {
+#ifdef MONGO_SSL
+ if ( _ssl ) {
+ return SSL_write( _ssl.get() , data , len );
+ }
+#endif
+ return ::send( _fd , data , len , portSendFlags );
+ }
+
+ bool Socket::stillConnected() {
+#ifdef MONGO_SSL
+ DEV log() << "TODO stillConnected() w/SSL" << endl;
+#else
+ int r = _send("", 0);
+ if( r < 0 ) {
+#if defined(_WIN32)
+ if ( WSAGetLastError() == WSAETIMEDOUT ) {
+#else
+ if ( ( errno == EAGAIN || errno == EWOULDBLOCK ) ) {
+#endif
+ ;
+ }
+ else {
+ return false;
+ }
+ }
+#endif
+ return true;
+ }
+
+ // sends all data or throws an exception
+ void Socket::send( const char * data , int len, const char *context ) {
+ while( len > 0 ) {
+ int ret = _send( data , len );
+ if ( ret == -1 ) {
+
+#ifdef MONGO_SSL
+ if ( _ssl ) {
+ log() << "SSL Error ret: " << ret << " err: " << SSL_get_error( _ssl.get() , ret )
+ << " " << ERR_error_string(ERR_get_error(), NULL)
+ << endl;
+ }
+#endif
+
+#if defined(_WIN32)
+ if ( WSAGetLastError() == WSAETIMEDOUT && _timeout != 0 ) {
+#else
+ if ( ( errno == EAGAIN || errno == EWOULDBLOCK ) && _timeout != 0 ) {
+#endif
+ log(_logLevel) << "Socket " << context << " send() timed out " << _remote.toString() << endl;
+ throw SocketException( SocketException::SEND_TIMEOUT , remoteString() );
+ }
+ else {
+ SocketException::Type t = SocketException::SEND_ERROR;
+ log(_logLevel) << "Socket " << context << " send() "
+ << errnoWithDescription() << ' ' << remoteString() << endl;
+ throw SocketException( t , remoteString() );
+ }
+ }
+ else {
+ _bytesOut += ret;
+
+ assert( ret <= len );
+ len -= ret;
+ data += ret;
+ }
+ }
+ }
+
+ void Socket::_send( const vector< pair< char *, int > > &data, const char *context ) {
+ for( vector< pair< char *, int > >::const_iterator i = data.begin(); i != data.end(); ++i ) {
+ char * data = i->first;
+ int len = i->second;
+ send( data, len, context );
+ }
+ }
+
+ /** sends all data or throws an exception
+ * @param context descriptive for logging
+ */
+ void Socket::send( const vector< pair< char *, int > > &data, const char *context ) {
+
+#ifdef MONGO_SSL
+ if ( _ssl ) {
+ _send( data , context );
+ return;
+ }
+#endif
+
+#if defined(_WIN32)
+ // TODO use scatter/gather api
+ _send( data , context );
+#else
+ vector< struct iovec > d( data.size() );
+ int i = 0;
+ for( vector< pair< char *, int > >::const_iterator j = data.begin(); j != data.end(); ++j ) {
+ if ( j->second > 0 ) {
+ d[ i ].iov_base = j->first;
+ d[ i ].iov_len = j->second;
+ ++i;
+ _bytesOut += j->second;
+ }
+ }
+ struct msghdr meta;
+ memset( &meta, 0, sizeof( meta ) );
+ meta.msg_iov = &d[ 0 ];
+ meta.msg_iovlen = d.size();
+
+ while( meta.msg_iovlen > 0 ) {
+ int ret = ::sendmsg( _fd , &meta , portSendFlags );
+ if ( ret == -1 ) {
+ if ( errno != EAGAIN || _timeout == 0 ) {
+ log(_logLevel) << "Socket " << context << " send() " << errnoWithDescription() << ' ' << remoteString() << endl;
+ throw SocketException( SocketException::SEND_ERROR , remoteString() );
+ }
+ else {
+ log(_logLevel) << "Socket " << context << " send() remote timeout " << remoteString() << endl;
+ throw SocketException( SocketException::SEND_TIMEOUT , remoteString() );
+ }
+ }
+ else {
+ struct iovec *& i = meta.msg_iov;
+ while( ret > 0 ) {
+ if ( i->iov_len > unsigned( ret ) ) {
+ i->iov_len -= ret;
+ i->iov_base = (char*)(i->iov_base) + ret;
+ ret = 0;
+ }
+ else {
+ ret -= i->iov_len;
+ ++i;
+ --(meta.msg_iovlen);
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ void Socket::recv( char * buf , int len ) {
+ unsigned retries = 0;
+ while( len > 0 ) {
+ int ret = unsafe_recv( buf , len );
+ if ( ret > 0 ) {
+ if ( len <= 4 && ret != len )
+ log(_logLevel) << "Socket recv() got " << ret << " bytes wanted len=" << len << endl;
+ assert( ret <= len );
+ len -= ret;
+ buf += ret;
+ }
+ else if ( ret == 0 ) {
+ log(3) << "Socket recv() conn closed? " << remoteString() << endl;
+ throw SocketException( SocketException::CLOSED , remoteString() );
+ }
+ else { /* ret < 0 */
+#if defined(_WIN32)
+ int e = WSAGetLastError();
+#else
+ int e = errno;
+# if defined(EINTR)
+ if( e == EINTR ) {
+ if( ++retries == 1 ) {
+ log() << "EINTR retry" << endl;
+ continue;
+ }
+ }
+# endif
+#endif
+ if ( ( e == EAGAIN
+#if defined(_WIN32)
+ || e == WSAETIMEDOUT
+#endif
+ ) && _timeout > 0 )
+ {
+ // this is a timeout
+ log(_logLevel) << "Socket recv() timeout " << remoteString() <<endl;
+ throw SocketException( SocketException::RECV_TIMEOUT, remoteString() );
+ }
+
+ log(_logLevel) << "Socket recv() " << errnoWithDescription(e) << " " << remoteString() <<endl;
+ throw SocketException( SocketException::RECV_ERROR , remoteString() );
+ }
+ }
+ }
+
+ int Socket::unsafe_recv( char *buf, int max ) {
+ int x = _recv( buf , max );
+ _bytesIn += x;
+ return x;
+ }
+
+
+ int Socket::_recv( char *buf, int max ) {
+#ifdef MONGO_SSL
+ if ( _ssl ){
+ return SSL_read( _ssl.get() , buf , max );
+ }
+#endif
+ return ::recv( _fd , buf , max , portRecvFlags );
+ }
+
+ void Socket::setTimeout( double secs ) {
+ setSockTimeouts( _fd, secs );
+ }
+
+#if defined(_WIN32)
+ struct WinsockInit {
+ WinsockInit() {
+ WSADATA d;
+ if ( WSAStartup(MAKEWORD(2,2), &d) != 0 ) {
+ out() << "ERROR: wsastartup failed " << errnoWithDescription() << endl;
+ problem() << "ERROR: wsastartup failed " << errnoWithDescription() << endl;
+ dbexit( EXIT_NTSERVICE_ERROR );
+ }
+ }
+ } winsock_init;
+#endif
+
+} // namespace mongo
diff --git a/src/mongo/util/net/sock.h b/src/mongo/util/net/sock.h
new file mode 100644
index 00000000000..2053768cbd5
--- /dev/null
+++ b/src/mongo/util/net/sock.h
@@ -0,0 +1,261 @@
+// @file sock.h
+
+/* 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 "../../pch.h"
+
+#include <stdio.h>
+#include <sstream>
+#include "../goodies.h"
+#include "../../db/cmdline.h"
+#include "../mongoutils/str.h"
+
+#ifndef _WIN32
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+
+#ifdef __openbsd__
+# include <sys/uio.h>
+#endif
+
+#endif // _WIN32
+
+#ifdef MONGO_SSL
+#include <openssl/ssl.h>
+#endif
+
+namespace mongo {
+
+ const int SOCK_FAMILY_UNKNOWN_ERROR=13078;
+
+ void disableNagle(int sock);
+
+#if defined(_WIN32)
+
+ typedef short sa_family_t;
+ typedef int socklen_t;
+
+ // This won't actually be used on windows
+ struct sockaddr_un {
+ short sun_family;
+ char sun_path[108]; // length from unix header
+ };
+
+#else // _WIN32
+
+ inline void closesocket(int s) { close(s); }
+ const int INVALID_SOCKET = -1;
+ typedef int SOCKET;
+
+#endif // _WIN32
+
+ inline string makeUnixSockPath(int port) {
+ return mongoutils::str::stream() << cmdLine.socket << "/mongodb-" << port << ".sock";
+ }
+
+ // If an ip address is passed in, just return that. If a hostname is passed
+ // in, look up its ip and return that. Returns "" on failure.
+ string hostbyname(const char *hostname);
+
+ void enableIPv6(bool state=true);
+ bool IPv6Enabled();
+ void setSockTimeouts(int sock, double secs);
+
+ /**
+ * wrapped around os representation of network address
+ */
+ struct SockAddr {
+ SockAddr() {
+ addressSize = sizeof(sa);
+ memset(&sa, 0, sizeof(sa));
+ sa.ss_family = AF_UNSPEC;
+ }
+ SockAddr(int sourcePort); /* listener side */
+ SockAddr(const char *ip, int port); /* EndPoint (remote) side, or if you want to specify which interface locally */
+
+ template <typename T> T& as() { return *(T*)(&sa); }
+ template <typename T> const T& as() const { return *(const T*)(&sa); }
+
+ string toString(bool includePort=true) const;
+
+ /**
+ * @return one of AF_INET, AF_INET6, or AF_UNIX
+ */
+ sa_family_t getType() const;
+
+ unsigned getPort() const;
+
+ string getAddr() const;
+
+ bool isLocalHost() const;
+
+ bool operator==(const SockAddr& r) const;
+
+ bool operator!=(const SockAddr& r) const;
+
+ bool operator<(const SockAddr& r) const;
+
+ const sockaddr* raw() const {return (sockaddr*)&sa;}
+ sockaddr* raw() {return (sockaddr*)&sa;}
+
+ socklen_t addressSize;
+ private:
+ struct sockaddr_storage sa;
+ };
+
+ extern SockAddr unknownAddress; // ( "0.0.0.0", 0 )
+
+ /** this is not cache and does a syscall */
+ string getHostName();
+
+ /** this is cached, so if changes during the process lifetime
+ * will be stale */
+ string getHostNameCached();
+
+ /**
+ * thrown by Socket and SockAddr
+ */
+ class SocketException : public DBException {
+ public:
+ const enum Type { CLOSED , RECV_ERROR , SEND_ERROR, RECV_TIMEOUT, SEND_TIMEOUT, FAILED_STATE, CONNECT_ERROR } _type;
+
+ SocketException( Type t , string server , int code = 9001 , string extra="" )
+ : DBException( "socket exception" , code ) , _type(t) , _server(server), _extra(extra){ }
+ virtual ~SocketException() throw() {}
+
+ bool shouldPrint() const { return _type != CLOSED; }
+ virtual string toString() const;
+
+ private:
+ string _server;
+ string _extra;
+ };
+
+#ifdef MONGO_SSL
+ class SSLManager : boost::noncopyable {
+ public:
+ SSLManager( bool client );
+
+ void setupPEM( const string& keyFile , const string& password );
+ void setupPubPriv( const string& privateKeyFile , const string& publicKeyFile );
+
+ /**
+ * creates an SSL context to be used for this file descriptor
+ * caller should delete
+ */
+ SSL * secure( int fd );
+
+ static int password_cb( char *buf,int num, int rwflag,void *userdata );
+
+ private:
+ bool _client;
+ SSL_CTX* _context;
+ string _password;
+ };
+#endif
+
+ /**
+ * thin wrapped around file descriptor and system calls
+ * todo: ssl
+ */
+ class Socket {
+ public:
+ Socket(int sock, const SockAddr& farEnd);
+
+ /** In some cases the timeout will actually be 2x this value - eg we do a partial send,
+ then the timeout fires, then we try to send again, then the timeout fires again with
+ no data sent, then we detect that the other side is down.
+
+ Generally you don't want a timeout, you should be very prepared for errors if you set one.
+ */
+ Socket(double so_timeout = 0, int logLevel = 0 );
+
+ bool connect(SockAddr& farEnd);
+ void close();
+
+ void send( const char * data , int len, const char *context );
+ void send( const vector< pair< char *, int > > &data, const char *context );
+
+ // recv len or throw SocketException
+ void recv( char * data , int len );
+ int unsafe_recv( char *buf, int max );
+
+ int getLogLevel() const { return _logLevel; }
+ void setLogLevel( int ll ) { _logLevel = ll; }
+
+ SockAddr remoteAddr() const { return _remote; }
+ string remoteString() const { return _remote.toString(); }
+ unsigned remotePort() const { return _remote.getPort(); }
+
+ void clearCounters() { _bytesIn = 0; _bytesOut = 0; }
+ long long getBytesIn() const { return _bytesIn; }
+ long long getBytesOut() const { return _bytesOut; }
+
+ void setTimeout( double secs );
+
+ bool stillConnected();
+
+#ifdef MONGO_SSL
+ /** secures inline */
+ void secure( SSLManager * ssl );
+
+ void secureAccepted( SSLManager * ssl );
+#endif
+
+ /**
+ * call this after a fork for server sockets
+ */
+ void postFork();
+
+ private:
+ void _init();
+
+ /** raw send, same semantics as ::send */
+ public:
+ int _send( const char * data , int len );
+ private:
+
+ /** sends dumbly, just each buffer at a time */
+ void _send( const vector< pair< char *, int > > &data, const char *context );
+
+ /** raw recv, same semantics as ::recv */
+ int _recv( char * buf , int max );
+
+ int _fd;
+ SockAddr _remote;
+ double _timeout;
+
+ long long _bytesIn;
+ long long _bytesOut;
+
+#ifdef MONGO_SSL
+ shared_ptr<SSL> _ssl;
+ SSLManager * _sslAccepted;
+#endif
+
+ protected:
+ int _logLevel; // passed to log() when logging errors
+
+ };
+
+
+} // namespace mongo
diff --git a/src/mongo/util/ntservice.cpp b/src/mongo/util/ntservice.cpp
new file mode 100644
index 00000000000..93cfd4a2de0
--- /dev/null
+++ b/src/mongo/util/ntservice.cpp
@@ -0,0 +1,408 @@
+// ntservice.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "ntservice.h"
+#include "../db/client.h"
+#include "winutil.h"
+#include "text.h"
+#include <direct.h>
+
+#if defined(_WIN32)
+
+namespace mongo {
+
+ void shutdownServer();
+
+ SERVICE_STATUS_HANDLE ServiceController::_statusHandle = NULL;
+ std::wstring ServiceController::_serviceName;
+ ServiceCallback ServiceController::_serviceCallback = NULL;
+
+ ServiceController::ServiceController() {}
+
+ bool initService();
+
+ // returns true if the service is started.
+ bool serviceParamsCheck( boost::program_options::variables_map& params, const std::string dbpath, int argc, char* argv[] ) {
+ bool installService = false;
+ bool removeService = false;
+ bool reinstallService = false;
+ bool startService = false;
+
+ std::wstring windowsServiceName = L"MongoDB";
+ std::wstring windowsServiceDisplayName = L"Mongo DB";
+ std::wstring windowsServiceDescription = L"Mongo DB Server";
+ std::wstring windowsServiceUser = L"";
+ std::wstring windowsServicePassword = L"";
+
+ if (params.count("install")) {
+ if ( ! params.count( "logpath" ) ) {
+ cerr << "--install has to be used with --logpath" << endl;
+ ::exit(-1);
+ }
+ installService = true;
+ }
+ if (params.count("reinstall")) {
+ if ( ! params.count( "logpath" ) ) {
+ cerr << "--reinstall has to be used with --logpath" << endl;
+ ::exit(-1);
+ }
+ reinstallService = true;
+ }
+ if (params.count("remove")) {
+ removeService = true;
+ }
+ if (params.count("service")) {
+ startService = true;
+ }
+
+ if (params.count("serviceName")) {
+ string x = params["serviceName"].as<string>();
+ windowsServiceName = wstring(x.size(),L' ');
+ for ( size_t i=0; i<x.size(); i++) {
+ windowsServiceName[i] = x[i];
+ }
+ }
+ if (params.count("serviceDisplayName")) {
+ string x = params["serviceDisplayName"].as<string>();
+ windowsServiceDisplayName = wstring(x.size(),L' ');
+ for ( size_t i=0; i<x.size(); i++) {
+ windowsServiceDisplayName[i] = x[i];
+ }
+ }
+ if (params.count("serviceDescription")) {
+ string x = params["serviceDescription"].as<string>();
+ windowsServiceDescription = wstring(x.size(),L' ');
+ for ( size_t i=0; i<x.size(); i++) {
+ windowsServiceDescription[i] = x[i];
+ }
+ }
+ if (params.count("serviceUser")) {
+ string x = params["serviceUser"].as<string>();
+ windowsServiceUser = wstring(x.size(),L' ');
+ for ( size_t i=0; i<x.size(); i++) {
+ windowsServiceUser[i] = x[i];
+ }
+ }
+ if (params.count("servicePassword")) {
+ string x = params["servicePassword"].as<string>();
+ windowsServicePassword = wstring(x.size(),L' ');
+ for ( size_t i=0; i<x.size(); i++) {
+ windowsServicePassword[i] = x[i];
+ }
+ }
+
+ if ( reinstallService ) {
+ ServiceController::removeService( windowsServiceName );
+ }
+ if ( installService || reinstallService ) {
+ if ( !ServiceController::installService( windowsServiceName , windowsServiceDisplayName, windowsServiceDescription, windowsServiceUser, windowsServicePassword, dbpath, argc, argv ) )
+ dbexit( EXIT_NTSERVICE_ERROR );
+ dbexit( EXIT_CLEAN );
+ }
+ else if ( removeService ) {
+ if ( !ServiceController::removeService( windowsServiceName ) )
+ dbexit( EXIT_NTSERVICE_ERROR );
+ dbexit( EXIT_CLEAN );
+ }
+ else if ( startService ) {
+ if ( !ServiceController::startService( windowsServiceName , mongo::initService ) )
+ dbexit( EXIT_NTSERVICE_ERROR );
+ return true;
+ }
+ return false;
+ }
+
+ bool ServiceController::installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, const std::wstring& serviceUser, const std::wstring& servicePassword, const std::string dbpath, int argc, char* argv[] ) {
+ assert(argc >= 1);
+
+ stringstream commandLine;
+
+ char exePath[1024];
+ GetModuleFileNameA( NULL, exePath, sizeof exePath );
+ commandLine << '"' << exePath << "\" ";
+
+ for ( int i = 1; i < argc; i++ ) {
+ std::string arg( argv[ i ] );
+ // replace install command to indicate process is being started as a service
+ if ( arg == "--install" || arg == "--reinstall" ) {
+ arg = "--service";
+ }
+ else if ( arg == "--dbpath" && i + 1 < argc ) {
+ commandLine << arg << " \"" << dbpath << "\" ";
+ i++;
+ continue;
+ }
+ else if ( arg == "--logpath" && i + 1 < argc ) {
+ commandLine << arg << " \"" << argv[i+1] << "\" ";
+ i++;
+ continue;
+ }
+ else if ( arg == "-f" && i + 1 < argc ) {
+ commandLine << arg << " \"" << argv[i+1] << "\" ";
+ i++;
+ continue;
+ }
+ else if ( arg == "--config" && i + 1 < argc ) {
+ commandLine << arg << " \"" << argv[i+1] << "\" ";
+ i++;
+ continue;
+ }
+ else if ( arg == "--pidfilepath" && i + 1 < argc ) {
+ commandLine << arg << " \"" << argv[i+1] << "\" ";
+ i++;
+ continue;
+ }
+ else if ( arg == "--repairpath" && i + 1 < argc ) {
+ commandLine << arg << " \"" << argv[i+1] << "\" ";
+ i++;
+ continue;
+ }
+ else if ( arg == "--keyfile" && i + 1 < argc ) {
+ commandLine << arg << " \"" << argv[i+1] << "\" ";
+ i++;
+ continue;
+ }
+ else if ( arg.length() > 9 && arg.substr(0, 9) == "--service" ) {
+ // Strip off --service(Name|User|Password) arguments
+ i++;
+ continue;
+ }
+ commandLine << arg << " ";
+ }
+
+ SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+ if ( schSCManager == NULL ) {
+ DWORD err = ::GetLastError();
+ cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
+ return false;
+ }
+
+ // Make sure servise doesn't already exist.
+ // TODO: Check to see if service is in "Deleting" status, suggest the user close down Services MMC snap-ins.
+ SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
+ if ( schService != NULL ) {
+ cerr << "There is already a service named " << toUtf8String(serviceName) << ". Aborting" << endl;
+ ::CloseServiceHandle( schService );
+ ::CloseServiceHandle( schSCManager );
+ return false;
+ }
+ std::basic_ostringstream< TCHAR > commandLineWide;
+ commandLineWide << commandLine.str().c_str();
+
+ cerr << "Creating service " << toUtf8String(serviceName) << "." << endl;
+
+ // create new service
+ schService = ::CreateService( schSCManager, serviceName.c_str(), displayName.c_str(),
+ SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START, SERVICE_ERROR_NORMAL,
+ commandLineWide.str().c_str(), NULL, NULL, L"\0\0", NULL, NULL );
+ if ( schService == NULL ) {
+ DWORD err = ::GetLastError();
+ cerr << "Error creating service: " << GetWinErrMsg(err) << endl;
+ ::CloseServiceHandle( schSCManager );
+ return false;
+ }
+
+ cerr << "Service creation successful." << endl;
+ cerr << "Service can be started from the command line via 'net start \"" << toUtf8String(serviceName) << "\"'." << endl;
+
+ bool serviceInstalled;
+
+ // TODO: If neccessary grant user "Login as a Service" permission.
+ if ( !serviceUser.empty() ) {
+ std::wstring actualServiceUser;
+ if ( serviceUser.find(L"\\") == string::npos ) {
+ actualServiceUser = L".\\" + serviceUser;
+ }
+ else {
+ actualServiceUser = serviceUser;
+ }
+
+ cerr << "Setting service login credentials. User: " << toUtf8String(actualServiceUser) << endl;
+ serviceInstalled = ::ChangeServiceConfig( schService, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, actualServiceUser.c_str(), servicePassword.c_str(), NULL );
+ if ( !serviceInstalled ) {
+ cerr << "Setting service login failed. Service has 'LocalService' permissions." << endl;
+ }
+ }
+
+ // set the service description
+ SERVICE_DESCRIPTION serviceDescription;
+ serviceDescription.lpDescription = (LPTSTR)serviceDesc.c_str();
+ serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_DESCRIPTION, &serviceDescription );
+
+#if 1
+ if ( ! serviceInstalled ) {
+#else
+ // This code sets the mongod service to auto-restart, forever.
+ // This might be a fine thing to do except that when mongod or Windows has a crash, the mongo.lock
+ // file is still around, so any attempt at a restart will immediately fail. With auto-restart, we
+ // go into a loop, crashing and restarting, crashing and restarting, until someone comes in and
+ // disables the service or deletes the mongod.lock file.
+ //
+ // I'm leaving the old code here for now in case we solve this and are able to turn SC_ACTION_RESTART
+ // back on.
+ //
+ if ( serviceInstalled ) {
+ SC_ACTION aActions[ 3 ] = { { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 }, { SC_ACTION_RESTART, 0 } };
+
+ SERVICE_FAILURE_ACTIONS serviceFailure;
+ ZeroMemory( &serviceFailure, sizeof( SERVICE_FAILURE_ACTIONS ) );
+ serviceFailure.cActions = 3;
+ serviceFailure.lpsaActions = aActions;
+
+ // set service recovery options
+ serviceInstalled = ::ChangeServiceConfig2( schService, SERVICE_CONFIG_FAILURE_ACTIONS, &serviceFailure );
+
+ }
+ else {
+#endif
+ cerr << "Could not set service description. Check the event log for more details." << endl;
+ }
+
+ ::CloseServiceHandle( schService );
+ ::CloseServiceHandle( schSCManager );
+
+ return serviceInstalled;
+ }
+
+ bool ServiceController::removeService( const std::wstring& serviceName ) {
+ SC_HANDLE schSCManager = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+ if ( schSCManager == NULL ) {
+ DWORD err = ::GetLastError();
+ cerr << "Error connecting to the Service Control Manager: " << GetWinErrMsg(err) << endl;
+ return false;
+ }
+
+ SC_HANDLE schService = ::OpenService( schSCManager, serviceName.c_str(), SERVICE_ALL_ACCESS );
+ if ( schService == NULL ) {
+ cerr << "Could not find a service named " << toUtf8String(serviceName) << " to uninstall." << endl;
+ ::CloseServiceHandle( schSCManager );
+ return false;
+ }
+
+ SERVICE_STATUS serviceStatus;
+
+ // stop service if its running
+ if ( ::ControlService( schService, SERVICE_CONTROL_STOP, &serviceStatus ) ) {
+ cerr << "Service " << toUtf8String(serviceName) << " is currently running. Stopping service." << endl;
+ while ( ::QueryServiceStatus( schService, &serviceStatus ) ) {
+ if ( serviceStatus.dwCurrentState == SERVICE_STOP_PENDING ) {
+ Sleep( 1000 );
+ }
+ else { break; }
+ }
+ cerr << "Service stopped." << endl;
+ }
+
+ cerr << "Deleting service " << toUtf8String(serviceName) << "." << endl;
+ bool serviceRemoved = ::DeleteService( schService );
+
+ ::CloseServiceHandle( schService );
+ ::CloseServiceHandle( schSCManager );
+
+ if (serviceRemoved) {
+ cerr << "Service deleted successfully." << endl;
+ }
+ else {
+ cerr << "Failed to delete service." << endl;
+ }
+
+ return serviceRemoved;
+ }
+
+ bool ServiceController::startService( const std::wstring& serviceName, ServiceCallback startService ) {
+ _serviceName = serviceName;
+ _serviceCallback = startService;
+
+ SERVICE_TABLE_ENTRY dispTable[] = {
+ { (LPTSTR)serviceName.c_str(), (LPSERVICE_MAIN_FUNCTION)ServiceController::initService },
+ { NULL, NULL }
+ };
+
+ return StartServiceCtrlDispatcher( dispTable );
+ }
+
+ bool ServiceController::reportStatus( DWORD reportState, DWORD waitHint ) {
+ if ( _statusHandle == NULL )
+ return false;
+
+ static DWORD checkPoint = 1;
+
+ SERVICE_STATUS ssStatus;
+
+ DWORD dwControlsAccepted;
+ switch ( reportState ) {
+ case SERVICE_START_PENDING:
+ case SERVICE_STOP_PENDING:
+ case SERVICE_STOPPED:
+ dwControlsAccepted = 0;
+ break;
+ default:
+ dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+ break;
+ }
+
+ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ssStatus.dwServiceSpecificExitCode = 0;
+ ssStatus.dwControlsAccepted = dwControlsAccepted;
+ ssStatus.dwCurrentState = reportState;
+ ssStatus.dwWin32ExitCode = NO_ERROR;
+ ssStatus.dwWaitHint = waitHint;
+ ssStatus.dwCheckPoint = ( reportState == SERVICE_RUNNING || reportState == SERVICE_STOPPED ) ? 0 : checkPoint++;
+
+ return SetServiceStatus( _statusHandle, &ssStatus );
+ }
+
+ void WINAPI ServiceController::initService( DWORD argc, LPTSTR *argv ) {
+ _statusHandle = RegisterServiceCtrlHandler( _serviceName.c_str(), serviceCtrl );
+ if ( !_statusHandle )
+ return;
+
+ reportStatus( SERVICE_START_PENDING, 1000 );
+
+ _serviceCallback();
+ dbexit( EXIT_CLEAN );
+
+ reportStatus( SERVICE_STOPPED );
+ }
+
+ static void serviceShutdown( const char* controlCodeName ) {
+ Client::initThread( "serviceShutdown" );
+ log() << "got " << controlCodeName << " request from Windows Service Controller, " <<
+ ( inShutdown() ? "already in shutdown" : "will terminate after current cmd ends" ) << endl;
+ ServiceController::reportStatus( SERVICE_STOP_PENDING );
+ if ( ! inShutdown() ) {
+ exitCleanly( EXIT_WINDOWS_SERVICE_STOP );
+ ServiceController::reportStatus( SERVICE_STOPPED );
+ }
+ }
+
+ void WINAPI ServiceController::serviceCtrl( DWORD ctrlCode ) {
+ switch ( ctrlCode ) {
+ case SERVICE_CONTROL_STOP:
+ serviceShutdown( "SERVICE_CONTROL_STOP" );
+ break;
+ case SERVICE_CONTROL_SHUTDOWN:
+ serviceShutdown( "SERVICE_CONTROL_SHUTDOWN" );
+ break;
+ }
+ }
+
+} // namespace mongo
+
+#endif
diff --git a/src/mongo/util/ntservice.h b/src/mongo/util/ntservice.h
new file mode 100644
index 00000000000..2570dfa9bef
--- /dev/null
+++ b/src/mongo/util/ntservice.h
@@ -0,0 +1,49 @@
+// ntservice.h
+
+/* 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
+
+#if defined(_WIN32)
+#include <windows.h>
+
+namespace mongo {
+
+ typedef bool ( *ServiceCallback )( void );
+ bool serviceParamsCheck( boost::program_options::variables_map& params, const std::string dbpath, int argc, char* argv[] );
+
+ class ServiceController {
+ public:
+ ServiceController();
+ virtual ~ServiceController() {}
+
+ static bool installService( const std::wstring& serviceName, const std::wstring& displayName, const std::wstring& serviceDesc, const std::wstring& serviceUser, const std::wstring& servicePassword, const std::string dbpath, int argc, char* argv[] );
+ static bool removeService( const std::wstring& serviceName );
+ static bool startService( const std::wstring& serviceName, ServiceCallback startService );
+ static bool reportStatus( DWORD reportState, DWORD waitHint = 0 );
+
+ static void WINAPI initService( DWORD argc, LPTSTR *argv );
+ static void WINAPI serviceCtrl( DWORD ctrlCode );
+
+ protected:
+ static std::wstring _serviceName;
+ static SERVICE_STATUS_HANDLE _statusHandle;
+ static ServiceCallback _serviceCallback;
+ };
+
+} // namespace mongo
+
+#endif
diff --git a/src/mongo/util/optime.h b/src/mongo/util/optime.h
new file mode 100644
index 00000000000..031ad960d20
--- /dev/null
+++ b/src/mongo/util/optime.h
@@ -0,0 +1,170 @@
+// optime.h - OpTime class
+
+/* 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 "../db/concurrency.h"
+
+namespace mongo {
+ void exitCleanly( ExitCode code );
+
+ struct ClockSkewException : public DBException {
+ ClockSkewException() : DBException( "clock skew exception" , 20001 ) {}
+ };
+
+ /* replsets used to use RSOpTime.
+ M/S uses OpTime.
+ But this is useable from both.
+ */
+ typedef unsigned long long ReplTime;
+
+ /* Operation sequence #. A combination of current second plus an ordinal value.
+ */
+#pragma pack(4)
+ class OpTime {
+ unsigned i; // ordinal comes first so we can do a single 64 bit compare on little endian
+ unsigned secs;
+ static OpTime last;
+ static OpTime skewed();
+ public:
+ static void setLast(const Date_t &date) {
+ notifier().notify_all(); // won't really do anything until write-lock released
+
+ last = OpTime(date);
+ }
+ unsigned getSecs() const {
+ return secs;
+ }
+ unsigned getInc() const {
+ return i;
+ }
+ OpTime(Date_t date) {
+ reinterpret_cast<unsigned long long&>(*this) = date.millis;
+ dassert( (int)secs >= 0 );
+ }
+ OpTime(ReplTime x) {
+ reinterpret_cast<unsigned long long&>(*this) = x;
+ dassert( (int)secs >= 0 );
+ }
+ OpTime(unsigned a, unsigned b) {
+ secs = a;
+ i = b;
+ dassert( (int)secs >= 0 );
+ }
+ OpTime( const OpTime& other ) {
+ secs = other.secs;
+ i = other.i;
+ dassert( (int)secs >= 0 );
+ }
+ OpTime() {
+ secs = 0;
+ i = 0;
+ }
+ // it isn't generally safe to not be locked for this. so use now(). some tests use this.
+ static OpTime now_inlock() {
+ notifier().notify_all(); // won't really do anything until write-lock released
+
+ unsigned t = (unsigned) time(0);
+ if ( last.secs == t ) {
+ last.i++;
+ return last;
+ }
+ if ( t < last.secs ) {
+ return skewed(); // separate function to keep out of the hot code path
+ }
+ last = OpTime(t, 1);
+ return last;
+ }
+ static OpTime now();
+ static OpTime last_inlock();
+
+ // Waits for global OpTime to be different from *this
+ // Must be atLeastReadLocked
+ // Defined in instance.cpp (only current user) as it needs dbtemprelease
+ void waitForDifferent(unsigned millis);
+
+ /* We store OpTime's in the database as BSON Date datatype -- we needed some sort of
+ 64 bit "container" for these values. While these are not really "Dates", that seems a
+ better choice for now than say, Number, which is floating point. Note the BinData type
+ is perhaps the cleanest choice, lacking a true unsigned64 datatype, but BinData has 5
+ bytes of overhead.
+ */
+ unsigned long long asDate() const {
+ return reinterpret_cast<const unsigned long long*>(&i)[0];
+ }
+ long long asLL() const {
+ return reinterpret_cast<const long long*>(&i)[0];
+ }
+
+ bool isNull() const { return secs == 0; }
+
+ string toStringLong() const {
+ char buf[64];
+ time_t_to_String(secs, buf);
+ stringstream ss;
+ ss << time_t_to_String_short(secs) << ' ';
+ ss << hex << secs << ':' << i;
+ return ss.str();
+ }
+
+ string toStringPretty() const {
+ stringstream ss;
+ ss << time_t_to_String_short(secs) << ':' << hex << i;
+ return ss.str();
+ }
+
+ string toString() const {
+ stringstream ss;
+ ss << hex << secs << ':' << i;
+ return ss.str();
+ }
+
+ bool operator==(const OpTime& r) const {
+ return i == r.i && secs == r.secs;
+ }
+ bool operator!=(const OpTime& r) const {
+ return !(*this == r);
+ }
+ bool operator<(const OpTime& r) const {
+ if ( secs != r.secs )
+ return secs < r.secs;
+ return i < r.i;
+ }
+ bool operator<=(const OpTime& r) const {
+ return *this < r || *this == r;
+ }
+ bool operator>(const OpTime& r) const {
+ return !(*this <= r);
+ }
+ bool operator>=(const OpTime& r) const {
+ return !(*this < r);
+ }
+ private:
+
+ // The following functions are to get around the need to define class-level statics in a cpp
+ static boost::condition& notifier() {
+ static boost::condition* holder = new boost::condition();
+ return *holder;
+ };
+ static boost::mutex& notifyMutex() {
+ static boost::mutex* holder = new boost::mutex();
+ return *holder;
+ };
+ };
+#pragma pack()
+
+} // namespace mongo
diff --git a/src/mongo/util/password.cpp b/src/mongo/util/password.cpp
new file mode 100644
index 00000000000..18164c3aa0a
--- /dev/null
+++ b/src/mongo/util/password.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2010 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.
+ */
+
+#include "pch.h"
+#include "password.h"
+#include <iostream>
+
+#ifndef _WIN32
+#include <termios.h>
+#endif
+
+using namespace std;
+
+namespace mongo {
+
+ string askPassword() {
+
+ std::string password;
+ cout << "Enter password: ";
+#ifndef _WIN32
+ const int stdinfd = 0;
+ termios termio;
+ tcflag_t old = 0;
+ if ( isatty( stdinfd ) ) {
+ int i = tcgetattr( stdinfd, &termio );
+ if( i == -1 ) {
+ cerr << "Cannot get terminal attributes " << errnoWithDescription() << endl;
+ return string();
+ }
+ old = termio.c_lflag;
+ termio.c_lflag &= ~ECHO;
+ i = tcsetattr( stdinfd, TCSANOW, &termio );
+ if( i == -1 ) {
+ cerr << "Cannot set terminal attributes " << errnoWithDescription() << endl;
+ return string();
+ }
+ }
+
+ getline( cin, password );
+
+ if ( isatty( stdinfd ) ) {
+ termio.c_lflag = old;
+ int i = tcsetattr( stdinfd, TCSANOW, &termio );
+ if( i == -1 ) {
+ cerr << "Cannot set terminal attributes " << errnoWithDescription() << endl;
+ return string();
+ }
+ }
+#else
+ HANDLE stdinh = GetStdHandle( STD_INPUT_HANDLE );
+ if ( stdinh == INVALID_HANDLE_VALUE) {
+ cerr << "Cannot get stdin handle " << GetLastError() << "\n";
+ return string();
+ }
+
+ DWORD old;
+ if ( !GetConsoleMode( stdinh, &old ) ) {
+ cerr << "Cannot get console mode " << GetLastError() << "\n";
+ return string();
+ }
+
+ DWORD noecho = ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT;
+ if ( !SetConsoleMode( stdinh, noecho ) ) {
+ cerr << "Cannot set console mode " << GetLastError() << "\n";
+ return string();
+ }
+
+ getline( cin, password );
+
+ if ( !SetConsoleMode( stdinh, old ) ) {
+ cerr << "Cannot set console mode " << GetLastError() << "\n";
+ return string();
+ }
+#endif
+ cout << "\n";
+ return password;
+ }
+}
diff --git a/src/mongo/util/password.h b/src/mongo/util/password.h
new file mode 100644
index 00000000000..519f712ee7e
--- /dev/null
+++ b/src/mongo/util/password.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010 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 <boost/program_options.hpp>
+#include <string>
+
+namespace mongo {
+
+ struct PasswordValue : public boost::program_options::typed_value<std::string> {
+
+ PasswordValue( std::string* val )
+ : boost::program_options::typed_value<std::string>( val ) { }
+
+ unsigned min_tokens() const {
+ return 0;
+ }
+
+ unsigned max_tokens() const {
+ return 1;
+ }
+
+ bool is_required() const {
+ return false;
+ }
+
+ void xparse( boost::any& value_store,
+ const std::vector<std::string>& new_tokens ) const {
+ if ( !value_store.empty() )
+#if BOOST_VERSION >= 104200
+ boost::throw_exception( boost::program_options::validation_error( boost::program_options::validation_error::multiple_values_not_allowed ) );
+#else
+ boost::throw_exception( boost::program_options::validation_error( "multiple values not allowed" ) );
+#endif
+ else if ( !new_tokens.empty() )
+ boost::program_options::typed_value<std::string>::xparse
+ (value_store, new_tokens);
+ else
+ value_store = std::string();
+ }
+
+ };
+
+ std::string askPassword();
+
+}
diff --git a/src/mongo/util/paths.h b/src/mongo/util/paths.h
new file mode 100644
index 00000000000..bb82df0c730
--- /dev/null
+++ b/src/mongo/util/paths.h
@@ -0,0 +1,124 @@
+// @file paths.h
+// file paths and directory handling
+
+/* Copyright 2010 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 "mongoutils/str.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace mongo {
+
+ using namespace mongoutils;
+
+ extern string dbpath;
+
+ /** this is very much like a boost::path. however, we define a new type to get some type
+ checking. if you want to say 'my param MUST be a relative path", use this.
+ */
+ struct RelativePath {
+ string _p;
+
+ bool empty() const { return _p.empty(); }
+
+ static RelativePath fromRelativePath(string f) {
+ RelativePath rp;
+ rp._p = f;
+ return rp;
+ }
+
+ /** from a full path */
+ static RelativePath fromFullPath(path f) {
+ path dbp(dbpath); // normalizes / and backslash
+ string fullpath = f.string();
+ string relative = str::after(fullpath, dbp.string());
+ if( relative.empty() ) {
+ log() << "warning file is not under db path? " << fullpath << ' ' << dbp.string() << endl;
+ RelativePath rp;
+ rp._p = fullpath;
+ return rp;
+ }
+ /*uassert(13600,
+ str::stream() << "file path is not under the db path? " << fullpath << ' ' << dbpath,
+ relative != fullpath);*/
+ if( str::startsWith(relative, "/") || str::startsWith(relative, "\\") ) {
+ relative.erase(0, 1);
+ }
+ RelativePath rp;
+ rp._p = relative;
+ return rp;
+ }
+
+ string toString() const { return _p; }
+
+ bool operator!=(const RelativePath& r) const { return _p != r._p; }
+ bool operator==(const RelativePath& r) const { return _p == r._p; }
+ bool operator<(const RelativePath& r) const { return _p < r._p; }
+
+ string asFullPath() const {
+ path x(dbpath);
+ x /= _p;
+ return x.string();
+ }
+
+ };
+
+ inline dev_t getPartition(const string& path){
+ struct stat stats;
+
+ if (stat(path.c_str(), &stats) != 0){
+ uasserted(13646, str::stream() << "stat() failed for file: " << path << " " << errnoWithDescription());
+ }
+
+ return stats.st_dev;
+ }
+
+ inline bool onSamePartition(const string& path1, const string& path2){
+ dev_t dev1 = getPartition(path1);
+ dev_t dev2 = getPartition(path2);
+
+ return dev1 == dev2;
+ }
+
+ inline void flushMyDirectory(const boost::filesystem::path& file){
+#ifdef __linux__ // this isn't needed elsewhere
+ // if called without a fully qualified path it asserts; that makes mongoperf fail. so make a warning. need a better solution longer term.
+ // massert(13652, str::stream() << "Couldn't find parent dir for file: " << file.string(), );
+ if( !file.has_branch_path() ) {
+ log() << "warning flushMYDirectory couldn't find parent dir for file: " << file.string() << endl;
+ return;
+ }
+
+
+ boost::filesystem::path dir = file.branch_path(); // parent_path in new boosts
+
+ log(1) << "flushing directory " << dir.string() << endl;
+
+ int fd = ::open(dir.string().c_str(), O_RDONLY); // DO NOT THROW OR ASSERT BEFORE CLOSING
+ massert(13650, str::stream() << "Couldn't open directory '" << dir.string() << "' for flushing: " << errnoWithDescription(), fd >= 0);
+ if (fsync(fd) != 0){
+ int e = errno;
+ close(fd);
+ massert(13651, str::stream() << "Couldn't fsync directory '" << dir.string() << "': " << errnoWithDescription(e), false);
+ }
+ close(fd);
+#endif
+ }
+
+}
diff --git a/src/mongo/util/processinfo.cpp b/src/mongo/util/processinfo.cpp
new file mode 100644
index 00000000000..082d42b3bc0
--- /dev/null
+++ b/src/mongo/util/processinfo.cpp
@@ -0,0 +1,48 @@
+// processinfo.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "processinfo.h"
+#include "mmap.h"
+
+#include <iostream>
+using namespace std;
+
+namespace mongo {
+
+ class PidFileWiper {
+ public:
+ ~PidFileWiper() {
+ ofstream out( path.c_str() , ios_base::out );
+ out.close();
+ }
+
+ void write( const string& p ) {
+ path = p;
+ ofstream out( path.c_str() , ios_base::out );
+ out << getpid() << endl;
+ out.close();
+ }
+
+ string path;
+ } pidFileWiper;
+
+ void writePidFile( const string& path ) {
+ pidFileWiper.write( path );
+ }
+
+}
diff --git a/src/mongo/util/processinfo.h b/src/mongo/util/processinfo.h
new file mode 100644
index 00000000000..5272831eb74
--- /dev/null
+++ b/src/mongo/util/processinfo.h
@@ -0,0 +1,67 @@
+// processinfo.h
+
+/* 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 <sys/types.h>
+#include <string>
+
+#ifndef _WIN32
+#include <unistd.h>
+#else
+typedef int pid_t;
+int getpid();
+#endif
+
+namespace mongo {
+
+ class BSONObjBuilder;
+
+ class ProcessInfo {
+ public:
+ ProcessInfo( pid_t pid = getpid() );
+ ~ProcessInfo();
+
+ /**
+ * @return mbytes
+ */
+ int getVirtualMemorySize();
+
+ /**
+ * @return mbytes
+ */
+ int getResidentSize();
+
+ /**
+ * Append platform-specific data to obj
+ */
+ void getExtraInfo(BSONObjBuilder& info);
+
+ bool supported();
+
+ static bool blockCheckSupported();
+ static bool blockInMemory( char * start );
+
+ private:
+ pid_t _pid;
+ };
+
+ void writePidFile( const std::string& path );
+
+ void printMemInfo( const char * whereContextStr = 0 );
+
+}
diff --git a/src/mongo/util/processinfo_darwin.cpp b/src/mongo/util/processinfo_darwin.cpp
new file mode 100644
index 00000000000..9f73cbffd4f
--- /dev/null
+++ b/src/mongo/util/processinfo_darwin.cpp
@@ -0,0 +1,116 @@
+// processinfo_darwin.cpp
+
+/* 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.
+ */
+
+#include "../pch.h"
+#include "processinfo.h"
+#include "log.h"
+
+#include <mach/vm_statistics.h>
+#include <mach/task_info.h>
+#include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#include <mach/mach_traps.h>
+#include <mach/task.h>
+#include <mach/vm_map.h>
+#include <mach/shared_region.h>
+#include <iostream>
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+using namespace std;
+
+namespace mongo {
+
+ ProcessInfo::ProcessInfo( pid_t pid ) : _pid( pid ) {
+ }
+
+ ProcessInfo::~ProcessInfo() {
+ }
+
+ bool ProcessInfo::supported() {
+ return true;
+ }
+
+ int ProcessInfo::getVirtualMemorySize() {
+ task_t result;
+
+ mach_port_t task;
+
+ if ( ( result = task_for_pid( mach_task_self() , _pid , &task) ) != KERN_SUCCESS ) {
+ cout << "error getting task\n";
+ return 0;
+ }
+
+#if !defined(__LP64__)
+ task_basic_info_32 ti;
+#else
+ task_basic_info_64 ti;
+#endif
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ if ( ( result = task_info( task , TASK_BASIC_INFO , (task_info_t)&ti, &count ) ) != KERN_SUCCESS ) {
+ cout << "error getting task_info: " << result << endl;
+ return 0;
+ }
+ return (int)((double)ti.virtual_size / (1024.0 * 1024 ) );
+ }
+
+ int ProcessInfo::getResidentSize() {
+ task_t result;
+
+ mach_port_t task;
+
+ if ( ( result = task_for_pid( mach_task_self() , _pid , &task) ) != KERN_SUCCESS ) {
+ cout << "error getting task\n";
+ return 0;
+ }
+
+
+#if !defined(__LP64__)
+ task_basic_info_32 ti;
+#else
+ task_basic_info_64 ti;
+#endif
+ mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
+ if ( ( result = task_info( task , TASK_BASIC_INFO , (task_info_t)&ti, &count ) ) != KERN_SUCCESS ) {
+ cout << "error getting task_info: " << result << endl;
+ return 0;
+ }
+ return (int)( ti.resident_size / (1024 * 1024 ) );
+ }
+
+ void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {}
+
+ bool ProcessInfo::blockCheckSupported() {
+ return true;
+ }
+
+ bool ProcessInfo::blockInMemory( char * start ) {
+ static long pageSize = 0;
+ if ( pageSize == 0 ) {
+ pageSize = sysconf( _SC_PAGESIZE );
+ }
+ start = start - ( (unsigned long long)start % pageSize );
+ char x = 0;
+ if ( mincore( start , 128 , &x ) ) {
+ log() << "mincore failed: " << errnoWithDescription() << endl;
+ return 1;
+ }
+ return x & 0x1;
+ }
+
+}
diff --git a/src/mongo/util/processinfo_linux2.cpp b/src/mongo/util/processinfo_linux2.cpp
new file mode 100644
index 00000000000..3eaccafd030
--- /dev/null
+++ b/src/mongo/util/processinfo_linux2.cpp
@@ -0,0 +1,244 @@
+// processinfo_linux2.cpp
+
+/* 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.
+ */
+
+#include "processinfo.h"
+
+#include <iostream>
+#include <stdio.h>
+#include <malloc.h>
+#include <db/jsobj.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+using namespace std;
+
+#define KLONG long
+#define KLF "l"
+
+namespace mongo {
+
+ class LinuxProc {
+ public:
+ LinuxProc( pid_t pid = getpid() ) {
+ char name[128];
+ sprintf( name , "/proc/%d/stat" , pid );
+
+ FILE * f = fopen( name , "r");
+ if ( ! f ) {
+ stringstream ss;
+ ss << "couldn't open [" << name << "] " << errnoWithDescription();
+ string s = ss.str();
+ // help the assert# control uasserted( 13538 , s.c_str() );
+ msgassertedNoTrace( 13538 , s.c_str() );
+ }
+ int found = fscanf(f,
+ "%d %s %c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu "
+ "%lu %lu %ld %ld " /* utime stime cutime cstime */
+ "%ld %ld "
+ "%ld "
+ "%ld "
+ "%lu " /* start_time */
+ "%lu "
+ "%ld " // rss
+ "%lu %"KLF"u %"KLF"u %"KLF"u %"KLF"u %"KLF"u "
+ /*
+ "%*s %*s %*s %*s "
+ "%"KLF"u %*lu %*lu "
+ "%d %d "
+ "%lu %lu"
+ */
+
+ ,
+
+ &_pid,
+ _comm,
+ &_state,
+ &_ppid, &_pgrp, &_session, &_tty, &_tpgid,
+ &_flags, &_min_flt, &_cmin_flt, &_maj_flt, &_cmaj_flt,
+ &_utime, &_stime, &_cutime, &_cstime,
+ &_priority, &_nice,
+ &_alarm,
+ &_nlwp,
+ &_start_time,
+ &_vsize,
+ &_rss,
+ &_rss_rlim, &_start_code, &_end_code, &_start_stack, &_kstk_esp, &_kstk_eip
+
+ /*
+ &_wchan,
+ &_exit_signal, &_processor,
+ &_rtprio, &_sched
+ */
+ );
+ if ( found == 0 ) {
+ cout << "system error: reading proc info" << endl;
+ }
+ fclose( f );
+ }
+
+ unsigned long getVirtualMemorySize() {
+ return _vsize;
+ }
+
+ unsigned long getResidentSize() {
+ return (unsigned long)_rss * 4 * 1024;
+ }
+
+ int _pid;
+ // The process ID.
+
+ char _comm[128];
+ // The filename of the executable, in parentheses. This is visible whether or not the executable is swapped out.
+
+ char _state;
+ //One character from the string "RSDZTW" where R is running, S is sleeping in an interruptible wait, D is waiting in uninterruptible
+ // disk sleep, Z is zombie, T is traced or stopped (on a signal), and W is paging.
+
+ int _ppid;
+ // The PID of the parent.
+
+ int _pgrp;
+ // The process group ID of the process.
+
+ int _session;
+ // The session ID of the process.
+
+ int _tty;
+ // The tty the process uses.
+
+ int _tpgid;
+ // The process group ID of the process which currently owns the tty that the process is connected to.
+
+ unsigned long _flags; // %lu
+ // The kernel flags word of the process. For bit meanings, see the PF_* defines in <linux/sched.h>. Details depend on the kernel version.
+
+ unsigned long _min_flt; // %lu
+ // The number of minor faults the process has made which have not required loading a memory page from disk.
+
+ unsigned long _cmin_flt; // %lu
+ // The number of minor faults that the process
+
+ unsigned long _maj_flt; // %lu
+ // The number of major faults the process has made which have required loading a memory page from disk.
+
+ unsigned long _cmaj_flt; // %lu
+ // The number of major faults that the process
+
+ unsigned long _utime; // %lu
+ // The number of jiffies that this process has been scheduled in user mode.
+
+ unsigned long _stime; // %lu
+ // The number of jiffies that this process has been scheduled in kernel mode.
+
+ long _cutime; // %ld
+ // The number of jiffies that this removed field.
+
+ long _cstime; // %ld
+
+ long _priority;
+ long _nice;
+
+ long _nlwp; // %ld
+ // The time in jiffies before the next SIGALRM is sent to the process due to an interval timer.
+
+ unsigned long _alarm;
+
+ unsigned long _start_time; // %lu
+ // The time in jiffies the process started after system boot.
+
+ unsigned long _vsize; // %lu
+ // Virtual memory size in bytes.
+
+ long _rss; // %ld
+ // Resident Set Size: number of pages the process has in real memory, minus 3 for administrative purposes. This is just the pages which
+ // count towards text, data, or stack space. This does not include pages which have not been demand-loaded in, or which are swapped out
+
+ unsigned long _rss_rlim; // %lu
+ // Current limit in bytes on the rss of the process (usually 4294967295 on i386).
+
+ unsigned long _start_code; // %lu
+ // The address above which program text can run.
+
+ unsigned long _end_code; // %lu
+ // The address below which program text can run.
+
+ unsigned long _start_stack; // %lu
+ // The address of the start of the stack.
+
+ unsigned long _kstk_esp; // %lu
+ // The current value of esp (stack pointer), as found in the kernel stack page for the process.
+
+ unsigned long _kstk_eip; // %lu
+ // The current EIP (instruction pointer).
+
+
+
+ };
+
+
+ ProcessInfo::ProcessInfo( pid_t pid ) : _pid( pid ) {
+ }
+
+ ProcessInfo::~ProcessInfo() {
+ }
+
+ bool ProcessInfo::supported() {
+ return true;
+ }
+
+ int ProcessInfo::getVirtualMemorySize() {
+ LinuxProc p(_pid);
+ return (int)( p.getVirtualMemorySize() / ( 1024.0 * 1024 ) );
+ }
+
+ int ProcessInfo::getResidentSize() {
+ LinuxProc p(_pid);
+ return (int)( p.getResidentSize() / ( 1024.0 * 1024 ) );
+ }
+
+ void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {
+ // [dm] i don't think mallinfo works. (64 bit.) ??
+ struct mallinfo malloc_info = mallinfo(); // structure has same name as function that returns it. (see malloc.h)
+ info.append("heap_usage_bytes", malloc_info.uordblks/*main arena*/ + malloc_info.hblkhd/*mmap blocks*/);
+ //docs claim hblkhd is included in uordblks but it isn't
+
+ LinuxProc p(_pid);
+ info.append("page_faults", (int)p._maj_flt);
+ }
+
+ bool ProcessInfo::blockCheckSupported() {
+ return true;
+ }
+
+ bool ProcessInfo::blockInMemory( char * start ) {
+ static long pageSize = 0;
+ if ( pageSize == 0 ) {
+ pageSize = sysconf( _SC_PAGESIZE );
+ }
+ start = start - ( (unsigned long long)start % pageSize );
+ unsigned char x = 0;
+ if ( mincore( start , 128 , &x ) ) {
+ log() << "mincore failed: " << errnoWithDescription() << endl;
+ return 1;
+ }
+ return x & 0x1;
+ }
+
+
+}
diff --git a/src/mongo/util/processinfo_none.cpp b/src/mongo/util/processinfo_none.cpp
new file mode 100644
index 00000000000..7d1e84d377c
--- /dev/null
+++ b/src/mongo/util/processinfo_none.cpp
@@ -0,0 +1,55 @@
+// processinfo_none.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "processinfo.h"
+
+#include <iostream>
+using namespace std;
+
+namespace mongo {
+
+ ProcessInfo::ProcessInfo( pid_t pid ) {
+ }
+
+ ProcessInfo::~ProcessInfo() {
+ }
+
+ bool ProcessInfo::supported() {
+ return false;
+ }
+
+ int ProcessInfo::getVirtualMemorySize() {
+ return -1;
+ }
+
+ int ProcessInfo::getResidentSize() {
+ return -1;
+ }
+
+ void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {}
+
+ bool ProcessInfo::blockCheckSupported() {
+ return false;
+ }
+
+ bool ProcessInfo::blockInMemory( char * start ) {
+ assert(0);
+ return true;
+ }
+
+}
diff --git a/src/mongo/util/processinfo_win32.cpp b/src/mongo/util/processinfo_win32.cpp
new file mode 100644
index 00000000000..87d92db7e18
--- /dev/null
+++ b/src/mongo/util/processinfo_win32.cpp
@@ -0,0 +1,102 @@
+// processinfo_win32.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "processinfo.h"
+#include <iostream>
+#include <psapi.h>
+#include "../bson/bsonobjbuilder.h"
+using namespace std;
+
+int getpid() {
+ return GetCurrentProcessId();
+}
+
+namespace mongo {
+
+ int _wconvertmtos( SIZE_T s ) {
+ return (int)( s / ( 1024 * 1024 ) );
+ }
+
+ ProcessInfo::ProcessInfo( pid_t pid ) {
+ }
+
+ ProcessInfo::~ProcessInfo() {
+ }
+
+ bool ProcessInfo::supported() {
+ return true;
+ }
+
+ int ProcessInfo::getVirtualMemorySize() {
+ MEMORYSTATUSEX mse;
+ mse.dwLength = sizeof(mse);
+ assert( GlobalMemoryStatusEx( &mse ) );
+ DWORDLONG x = (mse.ullTotalVirtual - mse.ullAvailVirtual) / (1024 * 1024) ;
+ assert( x <= 0x7fffffff );
+ return (int) x;
+ }
+
+ int ProcessInfo::getResidentSize() {
+ PROCESS_MEMORY_COUNTERS pmc;
+ assert( GetProcessMemoryInfo( GetCurrentProcess() , &pmc, sizeof(pmc) ) );
+ return _wconvertmtos( pmc.WorkingSetSize );
+ }
+
+ void ProcessInfo::getExtraInfo(BSONObjBuilder& info) {
+ MEMORYSTATUSEX mse;
+ mse.dwLength = sizeof(mse);
+ PROCESS_MEMORY_COUNTERS pmc;
+ if( GetProcessMemoryInfo( GetCurrentProcess() , &pmc, sizeof(pmc) ) ) {
+ info.append("page_faults", static_cast<int>(pmc.PageFaultCount));
+ info.append("usagePageFileMB", static_cast<int>(pmc.PagefileUsage / 1024 / 1024));
+ }
+ if( GlobalMemoryStatusEx( &mse ) ) {
+ info.append("totalPageFileMB", static_cast<int>(mse.ullTotalPageFile / 1024 / 1024));
+ info.append("availPageFileMB", static_cast<int>(mse.ullAvailPageFile / 1024 / 1024));
+ info.append("ramMB", static_cast<int>(mse.ullTotalPhys / 1024 / 1024));
+ }
+ }
+
+ bool ProcessInfo::blockCheckSupported() {
+ return true;
+ }
+
+ bool ProcessInfo::blockInMemory( char * start ) {
+#if 0
+ // code for printing out page fault addresses and pc's --
+ // this could be useful for targetting heavy pagefault locations in the code
+ static BOOL bstat = InitializeProcessForWsWatch( GetCurrentProcess() );
+ PSAPI_WS_WATCH_INFORMATION_EX wiex[30];
+ DWORD bufsize = sizeof(wiex);
+ bstat = GetWsChangesEx( GetCurrentProcess(), &wiex[0], &bufsize );
+ if (bstat) {
+ for (int i=0; i<30; i++) {
+ if (wiex[i].BasicInfo.FaultingPc == 0) break;
+ cout << "faulting pc = " << wiex[i].BasicInfo.FaultingPc << " address = " << wiex[i].BasicInfo.FaultingVa << " thread id = " << wiex[i].FaultingThreadId << endl;
+ }
+ }
+#endif
+ PSAPI_WORKING_SET_EX_INFORMATION wsinfo;
+ wsinfo.VirtualAddress = start;
+ BOOL result = QueryWorkingSetEx( GetCurrentProcess(), &wsinfo, sizeof(wsinfo) );
+ if ( result )
+ if ( wsinfo.VirtualAttributes.Valid )
+ return true;
+ return false;
+ }
+}
diff --git a/src/mongo/util/queue.h b/src/mongo/util/queue.h
new file mode 100644
index 00000000000..4223bd6c256
--- /dev/null
+++ b/src/mongo/util/queue.h
@@ -0,0 +1,106 @@
+// @file queue.h
+
+/* 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 "../pch.h"
+
+#include <queue>
+
+#include "../util/timer.h"
+
+namespace mongo {
+
+ /**
+ * simple blocking queue
+ */
+ template<typename T> class BlockingQueue : boost::noncopyable {
+ public:
+ BlockingQueue() : _lock("BlockingQueue") { }
+
+ void push(T const& t) {
+ scoped_lock l( _lock );
+ _queue.push( t );
+ _condition.notify_one();
+ }
+
+ bool empty() const {
+ scoped_lock l( _lock );
+ return _queue.empty();
+ }
+
+ size_t size() const {
+ scoped_lock l( _lock );
+ return _queue.size();
+ }
+
+
+ bool tryPop( T & t ) {
+ scoped_lock l( _lock );
+ if ( _queue.empty() )
+ return false;
+
+ t = _queue.front();
+ _queue.pop();
+
+ return true;
+ }
+
+ T blockingPop() {
+
+ scoped_lock l( _lock );
+ while( _queue.empty() )
+ _condition.wait( l.boost() );
+
+ T t = _queue.front();
+ _queue.pop();
+ return t;
+ }
+
+
+ /**
+ * blocks waiting for an object until maxSecondsToWait passes
+ * if got one, return true and set in t
+ * otherwise return false and t won't be changed
+ */
+ bool blockingPop( T& t , int maxSecondsToWait ) {
+
+ Timer timer;
+
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.sec += maxSecondsToWait;
+
+ scoped_lock l( _lock );
+ while( _queue.empty() ) {
+ if ( ! _condition.timed_wait( l.boost() , xt ) )
+ return false;
+ }
+
+ t = _queue.front();
+ _queue.pop();
+ return true;
+ }
+
+ private:
+ std::queue<T> _queue;
+
+ mutable mongo::mutex _lock;
+ boost::condition _condition;
+ };
+
+}
diff --git a/src/mongo/util/ramlog.cpp b/src/mongo/util/ramlog.cpp
new file mode 100644
index 00000000000..d7a839a3fff
--- /dev/null
+++ b/src/mongo/util/ramlog.cpp
@@ -0,0 +1,190 @@
+// ramlog.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "log.h"
+#include "ramlog.h"
+#include "mongoutils/html.h"
+#include "mongoutils/str.h"
+
+namespace mongo {
+
+ using namespace mongoutils;
+
+ RamLog::RamLog( string name ) : _name(name), _lastWrite(0) {
+ h = 0; n = 0;
+ for( int i = 0; i < N; i++ )
+ lines[i][C-1] = 0;
+
+ if ( name.size() ) {
+
+ if ( ! _namedLock )
+ _namedLock = new mongo::mutex("RamLog::_namedLock");
+
+ scoped_lock lk( *_namedLock );
+ if ( ! _named )
+ _named = new RM();
+ (*_named)[name] = this;
+ }
+
+ }
+
+ RamLog::~RamLog() {
+
+ }
+
+ void RamLog::write(LogLevel ll, const string& str) {
+ _lastWrite = time(0);
+
+ char *p = lines[(h+n)%N];
+
+ unsigned sz = str.size();
+ if( sz < C ) {
+ if ( str.c_str()[sz-1] == '\n' ) {
+ memcpy(p, str.c_str(), sz-1);
+ p[sz-1] = 0;
+ }
+ else
+ strcpy(p, str.c_str());
+ }
+ else {
+ memcpy(p, str.c_str(), C-1);
+ }
+
+ if( n < N ) n++;
+ else h = (h+1) % N;
+ }
+
+ void RamLog::get( vector<const char*>& v) const {
+ for( unsigned x=0, i=h; x++ < n; i=(i+1)%N )
+ v.push_back(lines[i]);
+ }
+
+ int RamLog::repeats(const vector<const char *>& v, int i) {
+ for( int j = i-1; j >= 0 && j+8 > i; j-- ) {
+ if( strcmp(v[i]+20,v[j]+20) == 0 ) {
+ for( int x = 1; ; x++ ) {
+ if( j+x == i ) return j;
+ if( i+x>=(int) v.size() ) return -1;
+ if( strcmp(v[i+x]+20,v[j+x]+20) ) return -1;
+ }
+ return -1;
+ }
+ }
+ return -1;
+ }
+
+
+ string RamLog::clean(const vector<const char *>& v, int i, string line ) {
+ if( line.empty() ) line = v[i];
+ if( i > 0 && strncmp(v[i], v[i-1], 11) == 0 )
+ return string(" ") + line.substr(11);
+ return v[i];
+ }
+
+ string RamLog::color(string line) {
+ string s = str::after(line, "replSet ");
+ if( str::startsWith(s, "warning") || startsWith(s, "error") )
+ return html::red(line);
+ if( str::startsWith(s, "info") ) {
+ if( str::endsWith(s, " up\n") )
+ return html::green(line);
+ else if( str::contains(s, " down ") || str::endsWith(s, " down\n") )
+ return html::yellow(line);
+ return line; //html::blue(line);
+ }
+
+ return line;
+ }
+
+ /* turn http:... into an anchor */
+ string RamLog::linkify(const char *s) {
+ const char *p = s;
+ const char *h = strstr(p, "http://");
+ if( h == 0 ) return s;
+
+ const char *sp = h + 7;
+ while( *sp && *sp != ' ' ) sp++;
+
+ string url(h, sp-h);
+ stringstream ss;
+ ss << string(s, h-s) << "<a href=\"" << url << "\">" << url << "</a>" << sp;
+ return ss.str();
+ }
+
+ void RamLog::toHTML(stringstream& s) {
+ vector<const char*> v;
+ get( v );
+
+ s << "<pre>\n";
+ for( int i = 0; i < (int)v.size(); i++ ) {
+ assert( strlen(v[i]) > 20 );
+ int r = repeats(v, i);
+ if( r < 0 ) {
+ s << color( linkify( clean(v,i).c_str() ) ) << '\n';
+ }
+ else {
+ stringstream x;
+ x << string(v[i], 0, 20);
+ int nr = (i-r);
+ int last = i+nr-1;
+ for( ; r < i ; r++ ) x << '.';
+ if( 1 ) {
+ stringstream r;
+ if( nr == 1 ) r << "repeat last line";
+ else r << "repeats last " << nr << " lines; ends " << string(v[last]+4,0,15);
+ s << html::a("", r.str(), clean(v,i,x.str()));
+ }
+ else s << x.str();
+ s << '\n';
+ i = last;
+ }
+ }
+ s << "</pre>\n";
+ }
+
+ // ---------------
+ // static things
+ // ---------------
+
+ RamLog* RamLog::get( string name ) {
+ if ( ! _named )
+ return 0;
+
+ scoped_lock lk( *_namedLock );
+ RM::iterator i = _named->find( name );
+ if ( i == _named->end() )
+ return 0;
+ return i->second;
+ }
+
+ void RamLog::getNames( vector<string>& names ) {
+ if ( ! _named )
+ return;
+
+ scoped_lock lk( *_namedLock );
+ for ( RM::iterator i=_named->begin(); i!=_named->end(); ++i ) {
+ if ( i->second->n )
+ names.push_back( i->first );
+ }
+ }
+
+ mongo::mutex* RamLog::_namedLock;
+ RamLog::RM* RamLog::_named = 0;
+
+ Tee* const warnings = new RamLog("warnings"); // Things put here go in serverStatus
+}
diff --git a/src/mongo/util/ramlog.h b/src/mongo/util/ramlog.h
new file mode 100644
index 00000000000..d3d5c8fbb4e
--- /dev/null
+++ b/src/mongo/util/ramlog.h
@@ -0,0 +1,65 @@
+// ramlog.h
+
+/* 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 "log.h"
+
+namespace mongo {
+
+ class RamLog : public Tee {
+ public:
+ RamLog( string name );
+
+ virtual void write(LogLevel ll, const string& str);
+
+ void get( vector<const char*>& v) const;
+
+ void toHTML(stringstream& s);
+
+ static RamLog* get( string name );
+ static void getNames( vector<string>& names );
+
+ time_t lastWrite() { return _lastWrite; } // 0 if no writes
+
+ protected:
+ static int repeats(const vector<const char *>& v, int i);
+ static string clean(const vector<const char *>& v, int i, string line="");
+ static string color(string line);
+
+ /* turn http:... into an anchor */
+ static string linkify(const char *s);
+
+ private:
+ ~RamLog(); // want this private as we want to leak so we can use them till the very end
+
+ enum {
+ N = 128, // number of links
+ C = 256 // max size of line
+ };
+ char lines[N][C];
+ unsigned h; // current position
+ unsigned n; // numer of lines stores 0 o N
+ string _name;
+
+ typedef map<string,RamLog*> RM;
+ static mongo::mutex* _namedLock;
+ static RM* _named;
+ time_t _lastWrite;
+ };
+
+}
diff --git a/src/mongo/util/scopeguard.h b/src/mongo/util/scopeguard.h
new file mode 100644
index 00000000000..b87a4b51871
--- /dev/null
+++ b/src/mongo/util/scopeguard.h
@@ -0,0 +1,427 @@
+////////////////////////////////////////////////////////////////////////////////
+// The Loki Library
+// Copyright (c) 2000 Andrei Alexandrescu
+// Copyright (c) 2000 Petru Marginean
+// Copyright (c) 2005 Joshua Lehrer
+//
+// Permission to use, copy, modify, distribute and sell this software for any
+// purpose is hereby granted without fee, provided that the above copyright
+// notice appear in all copies and that both that copyright notice and this
+// permission notice appear in supporting documentation.
+// The author makes no representations about the
+// suitability of this software for any purpose. It is provided "as is"
+// without express or implied warranty.
+////////////////////////////////////////////////////////////////////////////////
+#ifndef LOKI_SCOPEGUARD_H_
+#define LOKI_SCOPEGUARD_H_
+
+namespace mongo
+{
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// \class RefToValue
+ ///
+ /// Transports a reference as a value
+ /// Serves to implement the Colvin/Gibbons trick for SmartPtr/ScopeGuard
+ ////////////////////////////////////////////////////////////////////////////////
+
+ template <class T>
+ class RefToValue
+ {
+ public:
+
+ RefToValue(T& ref) : ref_(ref)
+ {}
+
+ RefToValue(const RefToValue& rhs) : ref_(rhs.ref_)
+ {}
+
+ operator T& () const
+ {
+ return ref_;
+ }
+
+ private:
+ // Disable - not implemented
+ RefToValue();
+ RefToValue& operator=(const RefToValue&);
+
+ T& ref_;
+ };
+
+
+ ////////////////////////////////////////////////////////////////////////////////
+ /// RefToValue creator.
+ ////////////////////////////////////////////////////////////////////////////////
+
+ template <class T>
+ inline RefToValue<T> ByRef(T& t)
+ {
+ return RefToValue<T>(t);
+ }
+
+
+
+
+ ////////////////////////////////////////////
+ /// ScopeGuard
+ /*
+ Trivial example for use:
+
+ FILE* f = fopen("myfile.txt", "w+");
+ if (!f)
+ return error;
+ ON_BLOCK_EXIT(fclose, f);
+
+
+ More complicated example:
+
+ ScopeGuard guard = MakeGuard(my_rollback_func, myparam);
+ ...
+ if (successful) {
+ guard.Dismiss();
+ return;
+ }
+ // guard is still active here and will fire at scope exit
+ ...
+
+
+ */
+
+
+ class ScopeGuardImplBase
+ {
+ ScopeGuardImplBase& operator =(const ScopeGuardImplBase&);
+
+ protected:
+
+ ~ScopeGuardImplBase()
+ {}
+
+ ScopeGuardImplBase(const ScopeGuardImplBase& other) throw()
+ : dismissed_(other.dismissed_)
+ {
+ other.Dismiss();
+ }
+
+ template <typename J>
+ static void SafeExecute(J& j) throw()
+ {
+ if (!j.dismissed_)
+ try
+ {
+ j.Execute();
+ }
+ catch(...)
+ {}
+ }
+
+ mutable bool dismissed_;
+
+ public:
+ ScopeGuardImplBase() throw() : dismissed_(false)
+ {}
+
+ void Dismiss() const throw()
+ {
+ dismissed_ = true;
+ }
+ };
+
+ ////////////////////////////////////////////////////////////////
+ ///
+ /// \typedef typedef const ScopeGuardImplBase& ScopeGuard
+ ///
+ /// See Andrei's and Petru Marginean's CUJ article
+ /// http://www.cuj.com/documents/s=8000/cujcexp1812alexandr/alexandr.htm
+ ///
+ /// Changes to the original code by Joshua Lehrer:
+ /// http://www.lehrerfamily.com/scopeguard.html
+ ////////////////////////////////////////////////////////////////
+
+ typedef const ScopeGuardImplBase& ScopeGuard;
+
+ template <typename F>
+ class ScopeGuardImpl0 : public ScopeGuardImplBase
+ {
+ public:
+ static ScopeGuardImpl0<F> MakeGuard(F fun)
+ {
+ return ScopeGuardImpl0<F>(fun);
+ }
+
+ ~ScopeGuardImpl0() throw()
+ {
+ SafeExecute(*this);
+ }
+
+ void Execute()
+ {
+ fun_();
+ }
+
+ protected:
+ ScopeGuardImpl0(F fun) : fun_(fun)
+ {}
+
+ F fun_;
+ };
+
+ template <typename F>
+ inline ScopeGuardImpl0<F> MakeGuard(F fun)
+ {
+ return ScopeGuardImpl0<F>::MakeGuard(fun);
+ }
+
+ template <typename F, typename P1>
+ class ScopeGuardImpl1 : public ScopeGuardImplBase
+ {
+ public:
+ static ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
+ {
+ return ScopeGuardImpl1<F, P1>(fun, p1);
+ }
+
+ ~ScopeGuardImpl1() throw()
+ {
+ SafeExecute(*this);
+ }
+
+ void Execute()
+ {
+ fun_(p1_);
+ }
+
+ protected:
+ ScopeGuardImpl1(F fun, P1 p1) : fun_(fun), p1_(p1)
+ {}
+
+ F fun_;
+ const P1 p1_;
+ };
+
+ template <typename F, typename P1>
+ inline ScopeGuardImpl1<F, P1> MakeGuard(F fun, P1 p1)
+ {
+ return ScopeGuardImpl1<F, P1>::MakeGuard(fun, p1);
+ }
+
+ template <typename F, typename P1, typename P2>
+ class ScopeGuardImpl2: public ScopeGuardImplBase
+ {
+ public:
+ static ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
+ {
+ return ScopeGuardImpl2<F, P1, P2>(fun, p1, p2);
+ }
+
+ ~ScopeGuardImpl2() throw()
+ {
+ SafeExecute(*this);
+ }
+
+ void Execute()
+ {
+ fun_(p1_, p2_);
+ }
+
+ protected:
+ ScopeGuardImpl2(F fun, P1 p1, P2 p2) : fun_(fun), p1_(p1), p2_(p2)
+ {}
+
+ F fun_;
+ const P1 p1_;
+ const P2 p2_;
+ };
+
+ template <typename F, typename P1, typename P2>
+ inline ScopeGuardImpl2<F, P1, P2> MakeGuard(F fun, P1 p1, P2 p2)
+ {
+ return ScopeGuardImpl2<F, P1, P2>::MakeGuard(fun, p1, p2);
+ }
+
+ template <typename F, typename P1, typename P2, typename P3>
+ class ScopeGuardImpl3 : public ScopeGuardImplBase
+ {
+ public:
+ static ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
+ {
+ return ScopeGuardImpl3<F, P1, P2, P3>(fun, p1, p2, p3);
+ }
+
+ ~ScopeGuardImpl3() throw()
+ {
+ SafeExecute(*this);
+ }
+
+ void Execute()
+ {
+ fun_(p1_, p2_, p3_);
+ }
+
+ protected:
+ ScopeGuardImpl3(F fun, P1 p1, P2 p2, P3 p3) : fun_(fun), p1_(p1), p2_(p2), p3_(p3)
+ {}
+
+ F fun_;
+ const P1 p1_;
+ const P2 p2_;
+ const P3 p3_;
+ };
+
+ template <typename F, typename P1, typename P2, typename P3>
+ inline ScopeGuardImpl3<F, P1, P2, P3> MakeGuard(F fun, P1 p1, P2 p2, P3 p3)
+ {
+ return ScopeGuardImpl3<F, P1, P2, P3>::MakeGuard(fun, p1, p2, p3);
+ }
+
+ //************************************************************
+
+ template <class Obj, typename MemFun>
+ class ObjScopeGuardImpl0 : public ScopeGuardImplBase
+ {
+ public:
+ static ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
+ {
+ return ObjScopeGuardImpl0<Obj, MemFun>(obj, memFun);
+ }
+
+ ~ObjScopeGuardImpl0() throw()
+ {
+ SafeExecute(*this);
+ }
+
+ void Execute()
+ {
+ (obj_.*memFun_)();
+ }
+
+ protected:
+ ObjScopeGuardImpl0(Obj& obj, MemFun memFun) : obj_(obj), memFun_(memFun)
+ {}
+
+ Obj& obj_;
+ MemFun memFun_;
+ };
+
+ template <class Obj, typename MemFun>
+ inline ObjScopeGuardImpl0<Obj, MemFun> MakeObjGuard(Obj& obj, MemFun memFun)
+ {
+ return ObjScopeGuardImpl0<Obj, MemFun>::MakeObjGuard(obj, memFun);
+ }
+
+ template <typename Ret, class Obj1, class Obj2>
+ inline ObjScopeGuardImpl0<Obj1,Ret(Obj2::*)()> MakeGuard(Ret(Obj2::*memFun)(), Obj1 &obj)
+ {
+ return ObjScopeGuardImpl0<Obj1,Ret(Obj2::*)()>::MakeObjGuard(obj,memFun);
+ }
+
+ template <typename Ret, class Obj1, class Obj2>
+ inline ObjScopeGuardImpl0<Obj1,Ret(Obj2::*)()> MakeGuard(Ret(Obj2::*memFun)(), Obj1 *obj)
+ {
+ return ObjScopeGuardImpl0<Obj1,Ret(Obj2::*)()>::MakeObjGuard(*obj,memFun);
+ }
+
+ template <class Obj, typename MemFun, typename P1>
+ class ObjScopeGuardImpl1 : public ScopeGuardImplBase
+ {
+ public:
+ static ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
+ {
+ return ObjScopeGuardImpl1<Obj, MemFun, P1>(obj, memFun, p1);
+ }
+
+ ~ObjScopeGuardImpl1() throw()
+ {
+ SafeExecute(*this);
+ }
+
+ void Execute()
+ {
+ (obj_.*memFun_)(p1_);
+ }
+
+ protected:
+ ObjScopeGuardImpl1(Obj& obj, MemFun memFun, P1 p1) : obj_(obj), memFun_(memFun), p1_(p1)
+ {}
+
+ Obj& obj_;
+ MemFun memFun_;
+ const P1 p1_;
+ };
+
+ template <class Obj, typename MemFun, typename P1>
+ inline ObjScopeGuardImpl1<Obj, MemFun, P1> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1)
+ {
+ return ObjScopeGuardImpl1<Obj, MemFun, P1>::MakeObjGuard(obj, memFun, p1);
+ }
+
+ template <typename Ret, class Obj1, class Obj2, typename P1a, typename P1b>
+ inline ObjScopeGuardImpl1<Obj1,Ret(Obj2::*)(P1a),P1b> MakeGuard(Ret(Obj2::*memFun)(P1a), Obj1 &obj, P1b p1)
+ {
+ return ObjScopeGuardImpl1<Obj1,Ret(Obj2::*)(P1a),P1b>::MakeObjGuard(obj,memFun,p1);
+ }
+
+ template <typename Ret, class Obj1, class Obj2, typename P1a, typename P1b>
+ inline ObjScopeGuardImpl1<Obj1,Ret(Obj2::*)(P1a),P1b> MakeGuard(Ret(Obj2::*memFun)(P1a), Obj1 *obj, P1b p1)
+ {
+ return ObjScopeGuardImpl1<Obj1,Ret(Obj2::*)(P1a),P1b>::MakeObjGuard(*obj,memFun,p1);
+ }
+
+ template <class Obj, typename MemFun, typename P1, typename P2>
+ class ObjScopeGuardImpl2 : public ScopeGuardImplBase
+ {
+ public:
+ static ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
+ {
+ return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>(obj, memFun, p1, p2);
+ }
+
+ ~ObjScopeGuardImpl2() throw()
+ {
+ SafeExecute(*this);
+ }
+
+ void Execute()
+ {
+ (obj_.*memFun_)(p1_, p2_);
+ }
+
+ protected:
+ ObjScopeGuardImpl2(Obj& obj, MemFun memFun, P1 p1, P2 p2) : obj_(obj), memFun_(memFun), p1_(p1), p2_(p2)
+ {}
+
+ Obj& obj_;
+ MemFun memFun_;
+ const P1 p1_;
+ const P2 p2_;
+ };
+
+ template <class Obj, typename MemFun, typename P1, typename P2>
+ inline ObjScopeGuardImpl2<Obj, MemFun, P1, P2> MakeObjGuard(Obj& obj, MemFun memFun, P1 p1, P2 p2)
+ {
+ return ObjScopeGuardImpl2<Obj, MemFun, P1, P2>::MakeObjGuard(obj, memFun, p1, p2);
+ }
+
+ template <typename Ret, class Obj1, class Obj2, typename P1a, typename P1b, typename P2a, typename P2b>
+ inline ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1a,P2a),P1b,P2b> MakeGuard(Ret(Obj2::*memFun)(P1a,P2a), Obj1 &obj, P1b p1, P2b p2)
+ {
+ return ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1a,P2a),P1b,P2b>::MakeObjGuard(obj,memFun,p1,p2);
+ }
+
+ template <typename Ret, class Obj1, class Obj2, typename P1a, typename P1b, typename P2a, typename P2b>
+ inline ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1a,P2a),P1b,P2b> MakeGuard(Ret(Obj2::*memFun)(P1a,P2a), Obj1 *obj, P1b p1, P2b p2)
+ {
+ return ObjScopeGuardImpl2<Obj1,Ret(Obj2::*)(P1a,P2a),P1b,P2b>::MakeObjGuard(*obj,memFun,p1,p2);
+ }
+
+} // namespace Loki
+
+#define LOKI_CONCATENATE_DIRECT(s1, s2) s1##s2
+#define LOKI_CONCATENATE(s1, s2) LOKI_CONCATENATE_DIRECT(s1, s2)
+#define LOKI_ANONYMOUS_VARIABLE(str) LOKI_CONCATENATE(str, __LINE__)
+
+#define ON_BLOCK_EXIT ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = MakeGuard
+#define ON_BLOCK_EXIT_OBJ ScopeGuard LOKI_ANONYMOUS_VARIABLE(scopeGuard) = MakeObjGuard
+
+#endif //LOKI_SCOPEGUARD_H_
diff --git a/src/mongo/util/signal_handlers.cpp b/src/mongo/util/signal_handlers.cpp
new file mode 100644
index 00000000000..0e9ec7a9b15
--- /dev/null
+++ b/src/mongo/util/signal_handlers.cpp
@@ -0,0 +1,122 @@
+// signal_handlers.cpp
+
+/**
+* Copyright (C) 2010 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+
+#if !defined(_WIN32) // TODO: windows support
+#include <unistd.h>
+#endif
+
+#if !defined(_WIN32) && !defined(NOEXECINFO)
+#include <execinfo.h>
+#endif
+
+#include "log.h"
+#include "signal_handlers.h"
+
+namespace mongo {
+
+ /*
+ * WARNING: PLEASE READ BEFORE CHANGING THIS MODULE
+ *
+ * All code in this module should be singal-friendly. Before adding any system
+ * call or other dependency, please make sure the latter still holds.
+ *
+ */
+
+ static int rawWrite( int fd , char* c , int size ) {
+#if !defined(_WIN32)
+
+ int toWrite = size;
+ int writePos = 0;
+ int wrote;
+ while ( toWrite > 0 ) {
+ wrote = write( fd , &c[writePos] , toWrite );
+ if ( wrote < 1 ) break;
+ toWrite -= wrote;
+ writePos += wrote;
+ }
+ return writePos;
+
+#else
+
+ return -1;
+
+#endif
+ }
+
+ static int formattedWrite( int fd , const char* format, ... ) {
+ const int MAX_ENTRY = 256;
+ static char entryBuf[MAX_ENTRY];
+
+ va_list ap;
+ va_start( ap , format );
+ int entrySize = vsnprintf( entryBuf , MAX_ENTRY-1 , format , ap );
+ if ( entrySize < 0 ) {
+ return -1;
+ }
+
+ if ( rawWrite( fd , entryBuf , entrySize ) < 0 ) {
+ return -1;
+ }
+
+ return 0;
+ }
+
+ static void formattedBacktrace( int fd ) {
+
+#if !defined(_WIN32) && !defined(NOEXECINFO)
+
+ int numFrames;
+ const int MAX_DEPTH = 20;
+ void* stackFrames[MAX_DEPTH];
+
+ numFrames = backtrace( stackFrames , 20 );
+ for ( int i = 0; i < numFrames; i++ ) {
+ formattedWrite( fd , "%p " , stackFrames[i] );
+ }
+ formattedWrite( fd , "\n" );
+
+ backtrace_symbols_fd( stackFrames , numFrames , fd );
+
+#else
+
+ formattedWrite( fd, "backtracing not implemented for this platform yet\n" );
+
+#endif
+
+ }
+
+ void printStackAndExit( int signalNum ) {
+ int fd = Logstream::getLogDesc();
+
+ if ( fd >= 0 ) {
+ formattedWrite( fd , "Received signal %d\n" , signalNum );
+ formattedWrite( fd , "Backtrace: " );
+ formattedBacktrace( fd );
+ formattedWrite( fd , "===\n" );
+ }
+
+ ::exit( EXIT_ABRUPT );
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/signal_handlers.h b/src/mongo/util/signal_handlers.h
new file mode 100644
index 00000000000..9d3a735a723
--- /dev/null
+++ b/src/mongo/util/signal_handlers.h
@@ -0,0 +1,34 @@
+// signal_handlers.h
+
+/**
+* Copyright (C) 2010 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#pragma once
+
+#include "../pch.h"
+
+namespace mongo {
+
+ /**
+ * Obtains the log file handler and writes the current thread's stack trace to
+ * it. This call issues an exit(). The function can safely be called from within a
+ * signal handler.
+ *
+ * @param signal that this hadler is called for
+ */
+ void printStackAndExit( int signalNum );
+
+} // namespace mongo
diff --git a/src/mongo/util/string_writer.h b/src/mongo/util/string_writer.h
new file mode 100755
index 00000000000..e83881bf6f6
--- /dev/null
+++ b/src/mongo/util/string_writer.h
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2011 (c) 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "pch.h"
+
+namespace mongo {
+
+ class StringWriter {
+ public:
+ virtual ~StringWriter() {};
+ virtual void writeString(stringstream &ss) const = 0;
+ };
+}
diff --git a/src/mongo/util/stringutils.cpp b/src/mongo/util/stringutils.cpp
new file mode 100644
index 00000000000..229f57bb3cb
--- /dev/null
+++ b/src/mongo/util/stringutils.cpp
@@ -0,0 +1,44 @@
+// stringutils.cpp
+
+/**
+* Copyright (C) 2008 10gen Inc.
+*
+* This program is free software: you can redistribute it and/or modify
+* it under the terms of the GNU Affero General Public License, version 3,
+* as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU Affero General Public License for more details.
+*
+* You should have received a copy of the GNU Affero General Public License
+* along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "pch.h"
+
+namespace mongo {
+
+ void splitStringDelim( const string& str , vector<string>* res , char delim ) {
+ if ( str.empty() )
+ return;
+
+ size_t beg = 0;
+ size_t pos = str.find( delim );
+ while ( pos != string::npos ) {
+ res->push_back( str.substr( beg, pos - beg) );
+ beg = ++pos;
+ pos = str.find( delim, beg );
+ }
+ res->push_back( str.substr( beg ) );
+ }
+
+ void joinStringDelim( const vector<string>& strs , string* res , char delim ) {
+ for ( vector<string>::const_iterator it = strs.begin(); it != strs.end(); ++it ) {
+ if ( it !=strs.begin() ) res->push_back( delim );
+ res->append( *it );
+ }
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/stringutils.h b/src/mongo/util/stringutils.h
new file mode 100644
index 00000000000..93598aa520b
--- /dev/null
+++ b/src/mongo/util/stringutils.h
@@ -0,0 +1,139 @@
+// stringutils.h
+
+/* Copyright 2010 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
+
+namespace mongo {
+
+ // see also mongoutils/str.h - perhaps move these there?
+ // see also text.h
+
+ void splitStringDelim( const string& str , vector<string>* res , char delim );
+
+ void joinStringDelim( const vector<string>& strs , string* res , char delim );
+
+ inline string tolowerString( const string& input ) {
+ string::size_type sz = input.size();
+
+ boost::scoped_array<char> line(new char[sz+1]);
+ char * copy = line.get();
+
+ for ( string::size_type i=0; i<sz; i++ ) {
+ char c = input[i];
+ copy[i] = (char)tolower( (int)c );
+ }
+ copy[sz] = 0;
+ return string(copy);
+ }
+
+ /**
+ * Non numeric characters are compared lexicographically; numeric substrings
+ * are compared numerically; dots separate ordered comparable subunits.
+ * For convenience, character 255 is greater than anything else.
+ */
+ inline int lexNumCmp( const char *s1, const char *s2 ) {
+ //cout << "START : " << s1 << "\t" << s2 << endl;
+
+ bool startWord = true;
+
+ while( *s1 && *s2 ) {
+
+ bool d1 = ( *s1 == '.' );
+ bool d2 = ( *s2 == '.' );
+ if ( d1 && !d2 )
+ return -1;
+ if ( d2 && !d1 )
+ return 1;
+ if ( d1 && d2 ) {
+ ++s1; ++s2;
+ startWord = true;
+ continue;
+ }
+
+ bool p1 = ( *s1 == (char)255 );
+ bool p2 = ( *s2 == (char)255 );
+ //cout << "\t\t " << p1 << "\t" << p2 << endl;
+ if ( p1 && !p2 )
+ return 1;
+ if ( p2 && !p1 )
+ return -1;
+
+ bool n1 = isNumber( *s1 );
+ bool n2 = isNumber( *s2 );
+
+ if ( n1 && n2 ) {
+ // get rid of leading 0s
+ if ( startWord ) {
+ while ( *s1 == '0' ) s1++;
+ while ( *s2 == '0' ) s2++;
+ }
+
+ char * e1 = (char*)s1;
+ char * e2 = (char*)s2;
+
+ // find length
+ // if end of string, will break immediately ('\0')
+ while ( isNumber (*e1) ) e1++;
+ while ( isNumber (*e2) ) e2++;
+
+ int len1 = (int)(e1-s1);
+ int len2 = (int)(e2-s2);
+
+ int result;
+ // if one is longer than the other, return
+ if ( len1 > len2 ) {
+ return 1;
+ }
+ else if ( len2 > len1 ) {
+ return -1;
+ }
+ // if the lengths are equal, just strcmp
+ else if ( (result = strncmp(s1, s2, len1)) != 0 ) {
+ return result;
+ }
+
+ // otherwise, the numbers are equal
+ s1 = e1;
+ s2 = e2;
+ startWord = false;
+ continue;
+ }
+
+ if ( n1 )
+ return 1;
+
+ if ( n2 )
+ return -1;
+
+ if ( *s1 > *s2 )
+ return 1;
+
+ if ( *s2 > *s1 )
+ return -1;
+
+ s1++; s2++;
+ startWord = false;
+ }
+
+ if ( *s1 )
+ return 1;
+ if ( *s2 )
+ return -1;
+ return 0;
+ }
+
+} // namespace mongo
diff --git a/src/mongo/util/systeminfo.h b/src/mongo/util/systeminfo.h
new file mode 100755
index 00000000000..be4404ff785
--- /dev/null
+++ b/src/mongo/util/systeminfo.h
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2011 (c) 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <cstddef>
+
+namespace mongo {
+
+ class SystemInfo {
+ public:
+ /*
+ Get the amount of physical memory available on the host.
+
+ This should only be used for "advisory" purposes, and not as a hard
+ value, because this could be deceptive on virtual hosts, and because
+ this will return zero on platforms that do not support it.
+
+ @returns amount of physical memory, or zero
+ */
+ static size_t getPhysicalRam();
+
+ private:
+ // don't instantiate this class
+ SystemInfo(); // no implementation
+ };
+
+}
diff --git a/src/mongo/util/systeminfo_linux2.cpp b/src/mongo/util/systeminfo_linux2.cpp
new file mode 100755
index 00000000000..c1b7c861768
--- /dev/null
+++ b/src/mongo/util/systeminfo_linux2.cpp
@@ -0,0 +1,47 @@
+/**
+ * Copyright (c) 2011 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pch.h"
+#include "util/systeminfo.h"
+
+#include <unistd.h>
+
+namespace mongo {
+
+ size_t SystemInfo::getPhysicalRam() {
+ /*
+ The value of this should not be changing while the system is running,
+ so it should be safe to do this once for the lifetime of the
+ application.
+
+ This could present a race condition if multiple threads do this at
+ the same time, but all paths through here will produce the same
+ result, so it's not worth locking or worrying about it.
+ */
+ static bool unknown = true;
+ static size_t ramSize = 0;
+
+ if (unknown) {
+ long pages = sysconf(_SC_PHYS_PAGES);
+ long page_size = sysconf(_SC_PAGE_SIZE);
+ ramSize = pages * page_size;
+ unknown = false;
+ }
+
+ return ramSize;
+ }
+
+}
diff --git a/src/mongo/util/systeminfo_none.cpp b/src/mongo/util/systeminfo_none.cpp
new file mode 100755
index 00000000000..d22ce17f6b9
--- /dev/null
+++ b/src/mongo/util/systeminfo_none.cpp
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2011 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pch.h"
+#include "util/systeminfo.h"
+
+namespace mongo {
+
+ size_t SystemInfo::getPhysicalRam() {
+ return 0;
+ }
+
+}
diff --git a/src/mongo/util/systeminfo_win32.cpp b/src/mongo/util/systeminfo_win32.cpp
new file mode 100755
index 00000000000..19c182878ee
--- /dev/null
+++ b/src/mongo/util/systeminfo_win32.cpp
@@ -0,0 +1,48 @@
+/**
+ * Copyright (c) 2011 10gen Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pch.h"
+#include "util/systeminfo.h"
+
+#include <windows.h>
+
+namespace mongo {
+
+ size_t SystemInfo::getPhysicalRam() {
+ /*
+ The value of this should not be changing while the system is running,
+ so it should be safe to do this once for the lifetime of the
+ application.
+
+ This could present a race condition if multiple threads do this at
+ the same time, but all paths through here will produce the same
+ result, so it's not worth locking or worrying about it.
+ */
+ static bool unknown = true;
+ static size_t ramSize = 0;
+
+ if (unknown) {
+ MEMORYSTATUSEX status;
+ status.dwLength = sizeof(status);
+ GlobalMemoryStatusEx(&status);
+ ramSize = static_cast<size_t>(status.ullTotalPhys);
+ unknown = false;
+ }
+
+ return ramSize;
+ }
+
+}
diff --git a/src/mongo/util/text.cpp b/src/mongo/util/text.cpp
new file mode 100644
index 00000000000..51a2556afdc
--- /dev/null
+++ b/src/mongo/util/text.cpp
@@ -0,0 +1,115 @@
+// text.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "text.h"
+#include "unittest.h"
+
+namespace mongo {
+
+ inline int leadingOnes(unsigned char c) {
+ if (c < 0x80) return 0;
+ static const char _leadingOnes[128] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 - 0x8F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 - 0x99
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xA0 - 0xA9
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xB0 - 0xB9
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xC0 - 0xC9
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xD0 - 0xD9
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xE0 - 0xE9
+ 4, 4, 4, 4, 4, 4, 4, 4, // 0xF0 - 0xF7
+ 5, 5, 5, 5, // 0xF8 - 0xFB
+ 6, 6, // 0xFC - 0xFD
+ 7, // 0xFE
+ 8, // 0xFF
+ };
+ return _leadingOnes[c & 0x7f];
+
+ }
+
+ bool isValidUTF8(const char *s) {
+ int left = 0; // how many bytes are left in the current codepoint
+ while (*s) {
+ const unsigned char c = (unsigned char) *(s++);
+ const int ones = leadingOnes(c);
+ if (left) {
+ if (ones != 1) return false; // should be a continuation byte
+ left--;
+ }
+ else {
+ if (ones == 0) continue; // ASCII byte
+ if (ones == 1) return false; // unexpected continuation byte
+ if (c > 0xF4) return false; // codepoint too large (< 0x10FFFF)
+ if (c == 0xC0 || c == 0xC1) return false; // codepoints <= 0x7F shouldn't be 2 bytes
+
+ // still valid
+ left = ones-1;
+ }
+ }
+ if (left!=0) return false; // string ended mid-codepoint
+ return true;
+ }
+
+#if defined(_WIN32)
+
+ std::string toUtf8String(const std::wstring& wide) {
+ if (wide.size() > boost::integer_traits<int>::const_max)
+ throw std::length_error(
+ "Wide string cannot be more than INT_MAX characters long.");
+ if (wide.size() == 0)
+ return "";
+
+ // Calculate necessary buffer size
+ int len = ::WideCharToMultiByte(
+ CP_UTF8, 0, wide.c_str(), static_cast<int>(wide.size()),
+ NULL, 0, NULL, NULL);
+
+ // Perform actual conversion
+ if (len > 0) {
+ std::vector<char> buffer(len);
+ len = ::WideCharToMultiByte(
+ CP_UTF8, 0, wide.c_str(), static_cast<int>(wide.size()),
+ &buffer[0], static_cast<int>(buffer.size()), NULL, NULL);
+ if (len > 0) {
+ assert(len == static_cast<int>(buffer.size()));
+ return std::string(&buffer[0], buffer.size());
+ }
+ }
+
+ throw boost::system::system_error(
+ ::GetLastError(), boost::system::system_category);
+ }
+
+#if defined(_UNICODE)
+ std::wstring toWideString(const char *s) {
+ std::basic_ostringstream<TCHAR> buf;
+ buf << s;
+ return buf.str();
+ }
+#endif
+
+#endif
+
+ struct TextUnitTest : public UnitTest {
+ void run() {
+ assert( parseLL("123") == 123 );
+ assert( parseLL("-123000000000") == -123000000000LL );
+ }
+ } textUnitTest;
+
+}
+
diff --git a/src/mongo/util/text.h b/src/mongo/util/text.h
new file mode 100644
index 00000000000..bf25c86fd39
--- /dev/null
+++ b/src/mongo/util/text.h
@@ -0,0 +1,148 @@
+// text.h
+/*
+ * Copyright 2010 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.
+ */
+
+/* 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
+
+namespace mongo {
+
+ class StringSplitter {
+ public:
+ /** @param big the string to be split
+ @param splitter the delimiter
+ */
+ StringSplitter( const char * big , const char * splitter )
+ : _big( big ) , _splitter( splitter ) {
+ }
+
+ /** @return true if more to be taken via next() */
+ bool more() {
+ return _big[0] != 0;
+ }
+
+ /** get next split string fragment */
+ string next() {
+ const char * foo = strstr( _big , _splitter );
+ if ( foo ) {
+ string s( _big , foo - _big );
+ _big = foo + 1;
+ while ( *_big && strstr( _big , _splitter ) == _big )
+ _big++;
+ return s;
+ }
+
+ string s = _big;
+ _big += strlen( _big );
+ return s;
+ }
+
+ void split( vector<string>& l ) {
+ while ( more() ) {
+ l.push_back( next() );
+ }
+ }
+
+ vector<string> split() {
+ vector<string> l;
+ split( l );
+ return l;
+ }
+
+ static vector<string> split( const string& big , const string& splitter ) {
+ StringSplitter ss( big.c_str() , splitter.c_str() );
+ return ss.split();
+ }
+
+ static string join( vector<string>& l , const string& split ) {
+ stringstream ss;
+ for ( unsigned i=0; i<l.size(); i++ ) {
+ if ( i > 0 )
+ ss << split;
+ ss << l[i];
+ }
+ return ss.str();
+ }
+
+ private:
+ const char * _big;
+ const char * _splitter;
+ };
+
+ /* This doesn't defend against ALL bad UTF8, but it will guarantee that the
+ * string can be converted to sequence of codepoints. However, it doesn't
+ * guarantee that the codepoints are valid.
+ */
+ bool isValidUTF8(const char *s);
+ inline bool isValidUTF8(string s) { return isValidUTF8(s.c_str()); }
+
+#if defined(_WIN32)
+
+ std::string toUtf8String(const std::wstring& wide);
+
+ std::wstring toWideString(const char *s);
+
+ /* like toWideString but UNICODE macro sensitive */
+# if !defined(_UNICODE)
+#error temp error
+ inline std::string toNativeString(const char *s) { return s; }
+# else
+ inline std::wstring toNativeString(const char *s) { return toWideString(s); }
+# endif
+
+#endif
+
+ // expect that n contains a base ten number and nothing else after it
+ // NOTE win version hasn't been tested directly
+ inline long long parseLL( const char *n ) {
+ long long ret;
+ uassert( 13307, "cannot convert empty string to long long", *n != 0 );
+#if !defined(_WIN32)
+ char *endPtr = 0;
+ errno = 0;
+ ret = strtoll( n, &endPtr, 10 );
+ uassert( 13305, "could not convert string to long long", *endPtr == 0 && errno == 0 );
+#elif _MSC_VER>=1600 // 1600 is VS2k10 1500 is VS2k8
+ size_t endLen = 0;
+ try {
+ ret = stoll( n, &endLen, 10 );
+ }
+ catch ( ... ) {
+ endLen = 0;
+ }
+ uassert( 13306, "could not convert string to long long", endLen != 0 && n[ endLen ] == 0 );
+#else // stoll() wasn't introduced until VS 2010.
+ char* endPtr = 0;
+ ret = _strtoi64( n, &endPtr, 10 );
+ uassert( 13310, "could not convert string to long long", (*endPtr == 0) && (ret != _I64_MAX) && (ret != _I64_MIN) );
+#endif // !defined(_WIN32)
+ return ret;
+ }
+}
diff --git a/src/mongo/util/time_support.h b/src/mongo/util/time_support.h
new file mode 100644
index 00000000000..18181eb805a
--- /dev/null
+++ b/src/mongo/util/time_support.h
@@ -0,0 +1,255 @@
+// @file time_support.h
+
+/* Copyright 2010 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 <cstdio> // sscanf
+#include <ctime>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/thread/xtime.hpp>
+#undef assert
+#define assert MONGO_assert
+
+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
+ }
+
+ // uses ISO 8601 dates without trailing Z
+ // colonsOk should be false when creating filenames
+ inline string terseCurrentTime(bool colonsOk=true) {
+ struct tm t;
+ time_t_to_Struct( time(0) , &t );
+
+ const char* fmt = (colonsOk ? "%Y-%m-%dT%H:%M:%S" : "%Y-%m-%dT%H-%M-%S");
+ char buf[32];
+ assert(strftime(buf, sizeof(buf), fmt, &t) == 19);
+ return buf;
+ }
+
+ inline string timeToISOString(time_t time) {
+ struct tm t;
+ time_t_to_Struct( time, &t );
+
+ const char* fmt = "%Y-%m-%dT%H:%M:%SZ";
+ char buf[32];
+ assert(strftime(buf, sizeof(buf), fmt, &t) == 20);
+ return buf;
+ }
+
+ inline boost::gregorian::date currentDate() {
+ boost::posix_time::ptime now = boost::posix_time::second_clock::local_time();
+ return now.date();
+ }
+
+ // parses time of day in "hh:mm" format assuming 'hh' is 00-23
+ inline bool toPointInTime( const string& str , boost::posix_time::ptime* timeOfDay ) {
+ int hh = 0;
+ int mm = 0;
+ if ( 2 != sscanf( str.c_str() , "%d:%d" , &hh , &mm ) ) {
+ return false;
+ }
+
+ // verify that time is well formed
+ if ( ( hh / 24 ) || ( mm / 60 ) ) {
+ return false;
+ }
+
+ boost::posix_time::ptime res( currentDate() , boost::posix_time::hours( hh ) + boost::posix_time::minutes( mm ) );
+ *timeOfDay = res;
+ return true;
+ }
+
+#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)
+ inline void sleepsecs(int s) {
+ // todo : add an assert here that we are not locked in d.dbMutex. there may be debugging things where we
+ // are but otherwise it's quite likely that would be a mistake.
+ Sleep(s*1000);
+ }
+ inline void sleepmillis(long long s) {
+ assert( s <= 0xffffffff );
+ Sleep((DWORD) s);
+ }
+ inline void sleepmicros(long long s) {
+ if ( s <= 0 )
+ return;
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.sec += (int)( s / 1000000 );
+ xt.nsec += (int)(( s % 1000000 ) * 1000);
+ if ( xt.nsec >= 1000000000 ) {
+ xt.nsec -= 1000000000;
+ xt.sec++;
+ }
+ boost::thread::sleep(xt);
+ }
+#elif 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(long long s) {
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.sec += (int)( s / 1000 );
+ xt.nsec += (int)(( s % 1000 ) * 1000000);
+ if ( xt.nsec >= 1000000000 ) {
+ xt.nsec -= 1000000000;
+ xt.sec++;
+ }
+ boost::thread::sleep(xt);
+ }
+ inline void sleepmicros(long long s) {
+ if ( s <= 0 )
+ return;
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ xt.sec += (int)( s / 1000000 );
+ xt.nsec += (int)(( 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(long long 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(long long s) {
+ sleepmicros( s * 1000 );
+ }
+#endif
+
+ extern long long jsTime_virtual_skew;
+ extern boost::thread_specific_ptr<long long> jsTime_virtual_thread_skew;
+
+ // DO NOT TOUCH except for testing
+ inline void jsTimeVirtualSkew( long long skew ){
+ jsTime_virtual_skew = skew;
+ }
+ inline long long getJSTimeVirtualSkew(){
+ return jsTime_virtual_skew;
+ }
+
+ inline void jsTimeVirtualThreadSkew( long long skew ){
+ jsTime_virtual_thread_skew.reset(new long long(skew));
+ }
+ inline long long getJSTimeVirtualThreadSkew(){
+ if(jsTime_virtual_thread_skew.get()){
+ return *(jsTime_virtual_thread_skew.get());
+ }
+ else return 0;
+ }
+
+ /** Date_t is milliseconds since epoch */
+ inline Date_t jsTime();
+
+ /** warning this will wrap */
+ inline unsigned curTimeMicros();
+
+ inline unsigned long long curTimeMicros64();
+#ifdef _WIN32 // no gettimeofday on windows
+ inline unsigned long long curTimeMillis64() {
+ boost::xtime xt;
+ boost::xtime_get(&xt, boost::TIME_UTC);
+ return ((unsigned long long)xt.sec) * 1000 + xt.nsec / 1000000;
+ }
+ 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 + getJSTimeVirtualSkew() + getJSTimeVirtualThreadSkew();
+ }
+ 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;
+ }
+ 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;
+ }
+#else
+# include <sys/time.h>
+ inline unsigned long long curTimeMillis64() {
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ return ((unsigned long long)tv.tv_sec) * 1000 + tv.tv_usec / 1000;
+ }
+ inline Date_t jsTime() {
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ unsigned long long t = tv.tv_usec / 1000;
+ return ((unsigned long long) tv.tv_sec * 1000) + t + getJSTimeVirtualSkew() + getJSTimeVirtualThreadSkew();
+ }
+ inline unsigned long long curTimeMicros64() {
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ return (((unsigned long long) tv.tv_sec) * 1000*1000) + tv.tv_usec;
+ }
+ inline unsigned curTimeMicros() {
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ unsigned secs = tv.tv_sec % 1024;
+ return secs*1000*1000 + tv.tv_usec;
+ }
+#endif
+
+} // namespace mongo
diff --git a/src/mongo/util/timer.h b/src/mongo/util/timer.h
new file mode 100644
index 00000000000..224651ac224
--- /dev/null
+++ b/src/mongo/util/timer.h
@@ -0,0 +1,115 @@
+// @file timer.h
+
+/* Copyright 2010 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 "time_support.h"
+
+namespace mongo {
+
+#if !defined(_WIN32)
+
+ /**
+ * simple scoped timer
+ */
+ class Timer /*copyable*/ {
+ public:
+ Timer() { reset(); }
+ int seconds() const { return (int)(micros() / 1000000); }
+ int millis() const { return (int)(micros() / 1000); }
+ int minutes() const { return seconds() / 60; }
+
+
+ /** gets time interval and resets at the same time. this way we can call curTimeMicros
+ once instead of twice if one wanted millis() and then reset().
+ @return time in millis
+ */
+ int millisReset() {
+ unsigned long long now = curTimeMicros64();
+ int m = (int)((now-old)/1000);
+ old = now;
+ return m;
+ }
+
+ // note: dubious that the resolution is as anywhere near as high as ethod name implies!
+ unsigned long long micros() const {
+ unsigned long long n = curTimeMicros64();
+ return n - old;
+ }
+ unsigned long long micros(unsigned long long & n) const { // returns cur time in addition to timer result
+ n = curTimeMicros64();
+ return n - old;
+ }
+
+ void reset() { old = curTimeMicros64(); }
+ private:
+ unsigned long long old;
+ };
+
+#else
+
+ class Timer /*copyable*/ {
+ public:
+ Timer() { reset(); }
+
+ int seconds() const {
+ int s = static_cast<int>((now() - old) / countsPerSecond);
+ return s;
+ }
+
+ int millis() const {
+ return (int)
+ ((now() - old) * 1000.0 / countsPerSecond);
+ }
+
+ int minutes() const { return seconds() / 60; }
+
+ /** gets time interval and resets at the same time. this way we can call curTimeMicros
+ once instead of twice if one wanted millis() and then reset().
+ @return time in millis
+ */
+ int millisReset() {
+ unsigned long long nw = now();
+ int m = static_cast<int>((nw - old) * 1000.0 / countsPerSecond);
+ old = nw;
+ return m;
+ }
+
+ void reset() {
+ old = now();
+ }
+
+ unsigned long long micros() const {
+ return (unsigned long long)
+ ((now() - old) * 1000000.0 / countsPerSecond);
+ }
+
+ static unsigned long long countsPerSecond;
+
+ private:
+ unsigned long long now() const {
+ LARGE_INTEGER i;
+ QueryPerformanceCounter(&i);
+ return i.QuadPart;
+ }
+
+ unsigned long long old;
+ };
+
+#endif
+
+} // namespace mongo
diff --git a/src/mongo/util/unittest.h b/src/mongo/util/unittest.h
new file mode 100644
index 00000000000..94be444363f
--- /dev/null
+++ b/src/mongo/util/unittest.h
@@ -0,0 +1,62 @@
+// unittest.h
+
+/* 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
+
+namespace mongo {
+
+ /* The idea here is to let all initialization of global variables (classes inheriting from UnitTest)
+ complete before we run the tests -- otherwise order of initilization being arbitrary may mess
+ us up. The app's main() function should call runTests().
+
+ To define a unit test, inherit from this and implement run. instantiate one object for the new class
+ as a global.
+
+ These tests are ran on *every* startup of mongod, so they have to be very lightweight. But it is a
+ good quick check for a bad build.
+ */
+ struct UnitTest {
+ UnitTest() {
+ registerTest(this);
+ }
+ virtual ~UnitTest() {}
+
+ // assert if fails
+ virtual void run() = 0;
+
+ static bool testsInProgress() { return running; }
+ private:
+ static vector<UnitTest*> *tests;
+ static bool running;
+ public:
+ static void registerTest(UnitTest *t) {
+ if ( tests == 0 )
+ tests = new vector<UnitTest*>();
+ tests->push_back(t);
+ }
+
+ static void runTests() {
+ running = true;
+ for ( vector<UnitTest*>::iterator i = tests->begin(); i != tests->end(); i++ ) {
+ (*i)->run();
+ }
+ running = false;
+ }
+ };
+
+
+} // namespace mongo
diff --git a/src/mongo/util/util.cpp b/src/mongo/util/util.cpp
new file mode 100644
index 00000000000..356c640f449
--- /dev/null
+++ b/src/mongo/util/util.cpp
@@ -0,0 +1,220 @@
+// @file util.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include "goodies.h"
+#include "unittest.h"
+#include "file_allocator.h"
+#include "optime.h"
+#include "time_support.h"
+#include "mongoutils/str.h"
+#include "timer.h"
+
+namespace mongo {
+
+#if defined(_WIN32)
+ unsigned long long Timer::countsPerSecond;
+ struct AtStartup {
+ AtStartup() {
+ LARGE_INTEGER x;
+ bool ok = QueryPerformanceFrequency(&x);
+ assert(ok);
+ Timer::countsPerSecond = x.QuadPart;
+ }
+ } atstartuputil;
+#endif
+
+ string hexdump(const char *data, unsigned len) {
+ assert( len < 1000000 );
+ const unsigned char *p = (const unsigned char *) data;
+ stringstream ss;
+ for( unsigned i = 0; i < 4 && i < len; i++ ) {
+ ss << std::hex << setw(2) << setfill('0');
+ unsigned n = p[i];
+ ss << n;
+ ss << ' ';
+ }
+ string s = ss.str();
+ return s;
+ }
+
+ boost::thread_specific_ptr<string> _threadName;
+
+ unsigned _setThreadName( const char * name ) {
+ if ( ! name ) name = "NONE";
+
+ static unsigned N = 0;
+
+ if ( strcmp( name , "conn" ) == 0 ) {
+ string* x = _threadName.get();
+ if ( x && mongoutils::str::startsWith( *x , "conn" ) ) {
+ int n = atoi( x->c_str() + 4 );
+ if ( n > 0 )
+ return n;
+ warning() << "unexpected thread name [" << *x << "] parsed to " << n << endl;
+ }
+ unsigned n = ++N;
+ stringstream ss;
+ ss << name << n;
+ _threadName.reset( new string( ss.str() ) );
+ return n;
+ }
+
+ _threadName.reset( new string(name) );
+ return 0;
+ }
+
+#if defined(_WIN32)
+#define MS_VC_EXCEPTION 0x406D1388
+#pragma pack(push,8)
+ typedef struct tagTHREADNAME_INFO {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+ } THREADNAME_INFO;
+#pragma pack(pop)
+
+ void setWinThreadName(const char *name) {
+ /* is the sleep here necessary???
+ Sleep(10);
+ */
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = name;
+ info.dwThreadID = -1;
+ info.dwFlags = 0;
+ __try {
+ RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER) {
+ }
+ }
+
+ unsigned setThreadName(const char *name) {
+ unsigned n = _setThreadName( name );
+#if !defined(_DEBUG)
+ // naming might be expensive so don't do "conn*" over and over
+ if( string("conn") == name )
+ return n;
+#endif
+ setWinThreadName(name);
+ return n;
+ }
+
+#else
+
+ unsigned setThreadName(const char * name ) {
+ return _setThreadName( name );
+ }
+
+#endif
+
+ string getThreadName() {
+ string * s = _threadName.get();
+ if ( s )
+ return *s;
+ return "";
+ }
+
+ vector<UnitTest*> *UnitTest::tests = 0;
+ bool UnitTest::running = false;
+
+ const char *default_getcurns() { return ""; }
+ const char * (*getcurns)() = default_getcurns;
+
+ int logLevel = 0;
+ int tlogLevel = 0;
+ mongo::mutex Logstream::mutex("Logstream");
+ int Logstream::doneSetup = Logstream::magicNumber();
+
+ bool isPrime(int n) {
+ int z = 2;
+ while ( 1 ) {
+ if ( z*z > n )
+ break;
+ if ( n % z == 0 )
+ return false;
+ z++;
+ }
+ return true;
+ }
+
+ int nextPrime(int n) {
+ n |= 1; // 2 goes to 3...don't care...
+ while ( !isPrime(n) )
+ n += 2;
+ return n;
+ }
+
+ struct UtilTest : public UnitTest {
+ void run() {
+ assert( isPrime(3) );
+ assert( isPrime(2) );
+ assert( isPrime(13) );
+ assert( isPrime(17) );
+ assert( !isPrime(9) );
+ assert( !isPrime(6) );
+ assert( nextPrime(4) == 5 );
+ assert( nextPrime(8) == 11 );
+
+ assert( endsWith("abcde", "de") );
+ assert( !endsWith("abcde", "dasdfasdfashkfde") );
+
+ assert( swapEndian(0x01020304) == 0x04030201 );
+
+ }
+ } utilTest;
+
+ OpTime OpTime::last(0, 0);
+
+ /* this is a good place to set a breakpoint when debugging, as lots of warning things
+ (assert, wassert) call it.
+ */
+ void sayDbContext(const char *errmsg) {
+ if ( errmsg ) {
+ problem() << errmsg << endl;
+ }
+ printStackTrace();
+ }
+
+ /* note: can't use malloc herein - may be in signal handler.
+ logLockless() likely does not comply and should still be fixed todo
+ likewise class string?
+ */
+ void rawOut( const string &s ) {
+ if( s.empty() ) return;
+
+ char buf[64];
+ time_t_to_String( time(0) , buf );
+ /* truncate / don't show the year: */
+ buf[19] = ' ';
+ buf[20] = 0;
+
+ Logstream::logLockless(buf);
+ Logstream::logLockless(s);
+ Logstream::logLockless("\n");
+ }
+
+ ostream& operator<<( ostream &s, const ThreadSafeString &o ) {
+ s << o.toString();
+ return s;
+ }
+
+ bool StaticObserver::_destroyingStatics = false;
+
+} // namespace mongo
diff --git a/src/mongo/util/version.cpp b/src/mongo/util/version.cpp
new file mode 100644
index 00000000000..1e4bc457f91
--- /dev/null
+++ b/src/mongo/util/version.cpp
@@ -0,0 +1,288 @@
+// @file version.cpp
+
+/* 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.
+ */
+
+#include "pch.h"
+#include <cstdlib>
+#include <iostream>
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include "unittest.h"
+#include "version.h"
+#include "stringutils.h"
+#include "../db/jsobj.h"
+#include "file.h"
+#include "ramlog.h"
+#include "../db/cmdline.h"
+
+namespace mongo {
+
+ /* Approved formats for versionString:
+ * 1.2.3
+ * 1.2.3-pre-
+ * 1.2.3-rc4 (up to rc9)
+ * 1.2.3-rc4-pre-
+ * If you really need to do something else you'll need to fix _versionArray()
+ */
+ const char versionString[] = "2.1.0-pre-";
+
+ // See unit test for example outputs
+ static BSONArray _versionArray(const char* version){
+ // this is inefficient, but cached so it doesn't matter
+ BSONArrayBuilder b;
+ string curPart;
+ const char* c = version;
+ int finalPart = 0; // 0 = final release, -100 = pre, -10 to -1 = -10 + X for rcX
+ do { //walks versionString including NUL byte
+ if (!(*c == '.' || *c == '-' || *c == '\0')){
+ curPart += *c;
+ continue;
+ }
+
+ try {
+ unsigned num = stringToNum(curPart.c_str());
+ b.append((int) num);
+ }
+ catch (...){ // not a number
+ if (curPart.empty()){
+ assert(*c == '\0');
+ break;
+ }
+ else if (startsWith(curPart, "rc")){
+ finalPart = -10 + stringToNum(curPart.c_str()+2);
+ break;
+ }
+ else if (curPart == "pre"){
+ finalPart = -100;
+ break;
+ }
+ }
+
+ curPart = "";
+ } while (*c++);
+
+ b.append(finalPart);
+ return b.arr();
+ }
+
+ const BSONArray versionArray = _versionArray(versionString);
+
+ string mongodVersion() {
+ stringstream ss;
+ ss << "db version v" << versionString << ", pdfile version " << PDFILE_VERSION << "." << PDFILE_VERSION_MINOR;
+ return ss.str();
+ }
+
+#ifndef _SCONS
+ // only works in scons
+ const char * gitVersion() { return "not-scons"; }
+#endif
+
+ void printGitVersion() { log() << "git version: " << gitVersion() << endl; }
+
+#ifndef _SCONS
+#if defined(_WIN32)
+ string sysInfo() {
+ stringstream ss;
+ ss << "not-scons win";
+ ss << " mscver:" << _MSC_FULL_VER << " built:" << __DATE__;
+ ss << " boostver:" << BOOST_VERSION;
+#if( !defined(_MT) )
+#error _MT is not defined
+#endif
+ ss << (sizeof(char *) == 8) ? " 64bit" : " 32bit";
+ return ss.str();
+ }
+#else
+ string sysInfo() { return ""; }
+#endif
+#endif
+
+ void printSysInfo() {
+ log() << "build info: " << sysInfo() << endl;
+ }
+
+
+ static Tee * startupWarningsLog = new RamLog("startupWarnings"); //intentionally leaked
+
+ //
+ // system warnings
+ //
+ void show_warnings() {
+ // each message adds a leading and a trailing newline
+
+ bool warned = false;
+ {
+ const char * foo = strchr( versionString , '.' ) + 1;
+ int bar = atoi( foo );
+ if ( ( 2 * ( bar / 2 ) ) != bar ) {
+ log() << startupWarningsLog;
+ log() << "** NOTE: This is a development version (" << versionString << ") of MongoDB." << startupWarningsLog;
+ log() << "** Not recommended for production." << startupWarningsLog;
+ warned = true;
+ }
+ }
+
+ if ( sizeof(int*) == 4 ) {
+ log() << startupWarningsLog;
+ log() << "** NOTE: when using MongoDB 32 bit, you are limited to about 2 gigabytes of data" << startupWarningsLog;
+ log() << "** see http://blog.mongodb.org/post/137788967/32-bit-limitations" << startupWarningsLog;
+ log() << "** with --journal, the limit is lower" << startupWarningsLog;
+ warned = true;
+ }
+
+#ifdef __linux__
+ if (boost::filesystem::exists("/proc/vz") && !boost::filesystem::exists("/proc/bc")) {
+ log() << startupWarningsLog;
+ log() << "** WARNING: You are running in OpenVZ. This is known to be broken!!!" << startupWarningsLog;
+ warned = true;
+ }
+
+ if (boost::filesystem::exists("/sys/devices/system/node/node1")){
+ // We are on a box with a NUMA enabled kernel and more than 1 numa node (they start at node0)
+ // Now we look at the first line of /proc/self/numa_maps
+ //
+ // Bad example:
+ // $ cat /proc/self/numa_maps
+ // 00400000 default file=/bin/cat mapped=6 N4=6
+ //
+ // Good example:
+ // $ numactl --interleave=all cat /proc/self/numa_maps
+ // 00400000 interleave:0-7 file=/bin/cat mapped=6 N4=6
+
+ File f;
+ f.open("/proc/self/numa_maps", /*read_only*/true);
+ if ( f.is_open() && ! f.bad() ) {
+ char line[100]; //we only need the first line
+ if (read(f.fd, line, sizeof(line)) < 0){
+ warning() << "failed to read from /proc/self/numa_maps: " << errnoWithDescription() << startupWarningsLog;
+ warned = true;
+ }
+ else {
+ // just in case...
+ line[98] = ' ';
+ line[99] = '\0';
+
+ // skip over pointer
+ const char* space = strchr(line, ' ');
+
+ if ( ! space ) {
+ log() << startupWarningsLog;
+ log() << "** WARNING: cannot parse numa_maps" << startupWarningsLog;
+ warned = true;
+ }
+ else if ( ! startsWith(space+1, "interleave") ) {
+ log() << startupWarningsLog;
+ log() << "** WARNING: You are running on a NUMA machine." << startupWarningsLog;
+ log() << "** We suggest launching mongod like this to avoid performance problems:" << startupWarningsLog;
+ log() << "** numactl --interleave=all mongod [other options]" << startupWarningsLog;
+ warned = true;
+ }
+ }
+ }
+ }
+
+ if (cmdLine.dur){
+ fstream f ("/proc/sys/vm/overcommit_memory", ios_base::in);
+ unsigned val;
+ f >> val;
+
+ if (val == 2) {
+ log() << startupWarningsLog;
+ log() << "** WARNING: /proc/sys/vm/overcommit_memory is " << val << startupWarningsLog;
+ log() << "** Journaling works best with it set to 0 or 1" << startupWarningsLog;
+ }
+ }
+
+ if (boost::filesystem::exists("/proc/sys/vm/zone_reclaim_mode")){
+ fstream f ("/proc/sys/vm/zone_reclaim_mode", ios_base::in);
+ unsigned val;
+ f >> val;
+
+ if (val != 0) {
+ log() << startupWarningsLog;
+ log() << "** WARNING: /proc/sys/vm/zone_reclaim_mode is " << val << startupWarningsLog;
+ log() << "** We suggest setting it to 0" << startupWarningsLog;
+ log() << "** http://www.kernel.org/doc/Documentation/sysctl/vm.txt" << startupWarningsLog;
+ }
+ }
+#endif
+
+ if (warned) {
+ log() << startupWarningsLog;
+ }
+ }
+
+ int versionCmp(StringData rhs, StringData lhs) {
+ if (strcmp(rhs.data(),lhs.data()) == 0)
+ return 0;
+
+ // handle "1.2.3-" and "1.2.3-pre"
+ if (rhs.size() < lhs.size()) {
+ if (strncmp(rhs.data(), lhs.data(), rhs.size()) == 0 && lhs.data()[rhs.size()] == '-')
+ return +1;
+ }
+ else if (rhs.size() > lhs.size()) {
+ if (strncmp(rhs.data(), lhs.data(), lhs.size()) == 0 && rhs.data()[lhs.size()] == '-')
+ return -1;
+ }
+
+ return lexNumCmp(rhs.data(), lhs.data());
+ }
+
+ class VersionCmpTest : public UnitTest {
+ public:
+ void run() {
+ assert( versionCmp("1.2.3", "1.2.3") == 0 );
+ assert( versionCmp("1.2.3", "1.2.4") < 0 );
+ assert( versionCmp("1.2.3", "1.2.20") < 0 );
+ assert( versionCmp("1.2.3", "1.20.3") < 0 );
+ assert( versionCmp("2.2.3", "10.2.3") < 0 );
+ assert( versionCmp("1.2.3", "1.2.3-") > 0 );
+ assert( versionCmp("1.2.3", "1.2.3-pre") > 0 );
+ assert( versionCmp("1.2.3", "1.2.4-") < 0 );
+ assert( versionCmp("1.2.3-", "1.2.3") < 0 );
+ assert( versionCmp("1.2.3-pre", "1.2.3") < 0 );
+
+ log(1) << "versionCmpTest passed" << endl;
+ }
+ } versionCmpTest;
+
+ class VersionArrayTest : public UnitTest {
+ public:
+ void run() {
+ assert( _versionArray("1.2.3") == BSON_ARRAY(1 << 2 << 3 << 0) );
+ assert( _versionArray("1.2.0") == BSON_ARRAY(1 << 2 << 0 << 0) );
+ assert( _versionArray("2.0.0") == BSON_ARRAY(2 << 0 << 0 << 0) );
+
+ assert( _versionArray("1.2.3-pre-") == BSON_ARRAY(1 << 2 << 3 << -100) );
+ assert( _versionArray("1.2.0-pre-") == BSON_ARRAY(1 << 2 << 0 << -100) );
+ assert( _versionArray("2.0.0-pre-") == BSON_ARRAY(2 << 0 << 0 << -100) );
+
+ assert( _versionArray("1.2.3-rc0") == BSON_ARRAY(1 << 2 << 3 << -10) );
+ assert( _versionArray("1.2.0-rc1") == BSON_ARRAY(1 << 2 << 0 << -9) );
+ assert( _versionArray("2.0.0-rc2") == BSON_ARRAY(2 << 0 << 0 << -8) );
+
+ // Note that the pre of an rc is the same as the rc itself
+ assert( _versionArray("1.2.3-rc3-pre-") == BSON_ARRAY(1 << 2 << 3 << -7) );
+ assert( _versionArray("1.2.0-rc4-pre-") == BSON_ARRAY(1 << 2 << 0 << -6) );
+ assert( _versionArray("2.0.0-rc5-pre-") == BSON_ARRAY(2 << 0 << 0 << -5) );
+
+ log(1) << "versionArrayTest passed" << endl;
+ }
+ } versionArrayTest;
+}
diff --git a/src/mongo/util/version.h b/src/mongo/util/version.h
new file mode 100644
index 00000000000..64f8b140fd5
--- /dev/null
+++ b/src/mongo/util/version.h
@@ -0,0 +1,27 @@
+#ifndef UTIL_VERSION_HEADER
+#define UTIL_VERSION_HEADER
+
+#include <string>
+
+namespace mongo {
+ struct BSONArray;
+
+ using std::string;
+
+ // mongo version
+ extern const char versionString[];
+ extern const BSONArray versionArray;
+ string mongodVersion();
+ int versionCmp(StringData rhs, StringData lhs); // like strcmp
+
+ const char * gitVersion();
+ void printGitVersion();
+
+ string sysInfo();
+ void printSysInfo();
+
+ void show_warnings();
+
+} // namespace mongo
+
+#endif // UTIL_VERSION_HEADER
diff --git a/src/mongo/util/winutil.h b/src/mongo/util/winutil.h
new file mode 100644
index 00000000000..b69b69a630d
--- /dev/null
+++ b/src/mongo/util/winutil.h
@@ -0,0 +1,44 @@
+// @file winutil.cpp : Windows related utility functions
+//
+// /**
+// * Copyright (C) 2008 10gen Inc.
+// *
+// * This program is free software: you can redistribute it and/or modify
+// * it under the terms of the GNU Affero General Public License, version 3,
+// * as published by the Free Software Foundation.
+// *
+// * This program is distributed in the hope that it will be useful,
+// * but WITHOUT ANY WARRANTY; without even the implied warranty of
+// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// * GNU Affero General Public License for more details.
+// *
+// * You should have received a copy of the GNU Affero General Public License
+// * along with this program. If not, see <http://www.gnu.org/licenses/>.
+// */
+//
+// #include "pch.h"
+
+#pragma once
+
+#if defined(_WIN32)
+#include <windows.h>
+#include "text.h"
+
+namespace mongo {
+
+ inline string GetWinErrMsg(DWORD err) {
+ LPTSTR errMsg;
+ ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, (LPTSTR)&errMsg, 0, NULL );
+ std::string errMsgStr = toUtf8String( errMsg );
+ ::LocalFree( errMsg );
+ // FormatMessage() appends a newline to the end of error messages, we trim it because endl flushes the buffer.
+ errMsgStr = errMsgStr.erase( errMsgStr.length() - 2 );
+ std::ostringstream output;
+ output << errMsgStr << " (" << err << ")";
+
+ return output.str();
+ }
+}
+
+#endif
+