Index: trunk/tools/maxcache/App.cpp |
— | — | @@ -10,7 +10,22 @@ |
11 | 11 | using namespace MaxCache; |
12 | 12 | |
13 | 13 | const Time App::NO_TIME( boost::posix_time::not_a_date_time ); |
| 14 | +const Time App::INFIN_TIME( boost::posix_time::pos_infin ); |
14 | 15 | |
| 16 | +App::~App() { |
| 17 | + SessionList::iterator i; |
| 18 | + for ( i = mActiveSessions.begin(); i != mActiveSessions.end(); i = mActiveSessions.begin() ) { |
| 19 | + Session & session = *i; |
| 20 | + mActiveSessions.erase( i ); |
| 21 | + delete &session; |
| 22 | + } |
| 23 | + for ( i = mFreeSessions.begin(); i != mFreeSessions.end(); i = mFreeSessions.begin() ) { |
| 24 | + Session & session = *i; |
| 25 | + mFreeSessions.erase( i ); |
| 26 | + delete &session; |
| 27 | + } |
| 28 | +} |
| 29 | + |
15 | 30 | void App::run( int argc, char** argv ) { |
16 | 31 | Endpoint endpoint( Tcp::v4(), 9999 ); |
17 | 32 | mAcceptor = AcceptorPtr( new Acceptor( mService, endpoint ) ); |
— | — | @@ -22,204 +37,42 @@ |
23 | 38 | } while ( numHandlers ); |
24 | 39 | } |
25 | 40 | |
| 41 | +Session & App::activateSession() { |
| 42 | + Session * session; |
| 43 | + if ( mFreeSessions.empty() ) { |
| 44 | + session = new Session( *this ); |
| 45 | + mActiveSessions.push_front( *session ); |
| 46 | + } else { |
| 47 | + SessionList::iterator i = mFreeSessions.begin(); |
| 48 | + mFreeSessions.erase( i ); |
| 49 | + mActiveSessions.push_front( *i ); |
| 50 | + session = &*i; |
| 51 | + } |
| 52 | + return *session; |
| 53 | +} |
| 54 | + |
| 55 | +void App::freeSession( Session & session ) { |
| 56 | + session.reset(); |
| 57 | + mActiveSessions.erase( mActiveSessions.iterator_to( session ) ); |
| 58 | + mFreeSessions.push_front( session ); |
| 59 | +} |
| 60 | + |
26 | 61 | void App::startAccept() { |
27 | | - // Create the peer socket |
28 | | - SocketPtr peer( new Socket( mService ) ); |
| 62 | + // Get a session |
| 63 | + Session & session = activateSession(); |
| 64 | + |
29 | 65 | // Start accepting connections |
30 | | - mAcceptor->async_accept( *peer, boost::bind( &App::onAccept, this, peer, _1 ) ); |
| 66 | + mAcceptor->async_accept( session.getSocket(), |
| 67 | + boost::bind( &App::onAccept, this, boost::ref( session ), _1 ) ); |
31 | 68 | } |
32 | 69 | |
33 | | -void App::onAccept( SocketPtr peer, const ErrorCode & acceptError ) { |
| 70 | +void App::onAccept( Session & session, const ErrorCode & acceptError ) { |
34 | 71 | if ( acceptError ) { |
35 | 72 | throw SystemError( acceptError ); |
36 | 73 | } |
37 | 74 | // Handle this connection |
38 | | - startRead( peer ); |
| 75 | + session.startRead(); |
39 | 76 | // Listen for another connection |
40 | 77 | startAccept(); |
41 | 78 | } |
42 | 79 | |
43 | | -void App::startRead( SocketPtr peer ) { |
44 | | - BufferPtr buffer( new Buffer( getMaxCmdLength() ) ); |
45 | | - Asio::async_read_until( *peer, *buffer, "\r\n", |
46 | | - boost::bind( &App::onLineReady, this, peer, buffer, _1, _2 ) ); |
47 | | -} |
48 | | - |
49 | | -void App::onLineReady( SocketPtr peer, BufferPtr buffer, |
50 | | - const ErrorCode & readError, std::size_t n ) |
51 | | -{ |
52 | | - if ( readError ) { |
53 | | - // Close connection |
54 | | - return; |
55 | | - } |
56 | | - |
57 | | - std::istream stream( buffer.get() ); |
58 | | - std::string command; |
59 | | - |
60 | | - // Formatted extraction requires a sensible locale to be set |
61 | | - stream.imbue( std::locale( "C" ) ); |
62 | | - |
63 | | - stream >> command; |
64 | | - if ( handleTooFewParams( peer, stream ) ) return; |
65 | | - |
66 | | - if ( command == "get" ) { |
67 | | - std::string key; |
68 | | - stream >> key; |
69 | | - if ( handleTooFewParams( peer, stream ) ) return; |
70 | | - |
71 | | - CacheEntry * entry = mCache.getEntry( key ); |
72 | | - if ( !entry ) { |
73 | | - writeMessage( peer, "NO: Item not found\r\n" ); |
74 | | - return; |
75 | | - } |
76 | | - |
77 | | - writeCacheEntry( peer, entry->getValue() ); |
78 | | - |
79 | | - } else if ( command == "set" ) { |
80 | | - // Usage: set <key> <expiry> <cost> <value> |
81 | | - std::string key; |
82 | | - boost::uint32_t expiryInterval, clientCost; |
83 | | - StringPtr value( new std::string ); |
84 | | - |
85 | | - stream.width( mMaxKeyLength + 1 ); |
86 | | - stream >> key; |
87 | | - if ( handleTooFewParams( peer, stream ) ) return; |
88 | | - if ( key.size() > mMaxKeyLength ) { |
89 | | - writeMessage( peer, "ERROR: Key too long\r\n" ); |
90 | | - return; |
91 | | - } |
92 | | - |
93 | | - stream >> expiryInterval; |
94 | | - if ( handleTooFewParams( peer, stream ) ) return; |
95 | | - if ( stream.fail() ) { |
96 | | - writeMessage( peer, "ERROR: invalid expiry interval\r\n" ); |
97 | | - return; |
98 | | - } |
99 | | - |
100 | | - // Read the client cost, which is later divided by the size to get the |
101 | | - // real cost |
102 | | - stream >> clientCost; |
103 | | - if ( handleTooFewParams( peer, stream ) ) return; |
104 | | - if ( stream.fail() ) { |
105 | | - writeMessage( peer, "ERROR: invalid clientCost\r\n" ); |
106 | | - return; |
107 | | - } |
108 | | - |
109 | | - stream >> *value; |
110 | | - if ( handleTooFewParams( peer, stream ) ) return; |
111 | | - if ( value->size() > mCache.getMaxEntrySize() ) { |
112 | | - writeMessage( peer, "ERROR: value too big\r\n" ); |
113 | | - return; |
114 | | - } |
115 | | - |
116 | | - Time expiry; |
117 | | - if ( expiryInterval ) { |
118 | | - expiry = getTime() + boost::posix_time::microseconds( expiryInterval ); |
119 | | - } else { |
120 | | - expiry = Time( boost::posix_time::pos_infin ); |
121 | | - } |
122 | | - |
123 | | - mCache.setEntry( key, value, clientCost, expiry ); |
124 | | - writeMessage( peer, "OK: value set\r\n" ); |
125 | | - } else if ( command == "delete" ) { |
126 | | - std::string key; |
127 | | - stream.width( mMaxKeyLength + 1 ); |
128 | | - stream >> key; |
129 | | - if ( handleTooFewParams( peer, stream ) ) return; |
130 | | - if ( key.size() > mMaxKeyLength ) { |
131 | | - writeMessage( peer, "ERROR: Key too long\r\n" ); |
132 | | - return; |
133 | | - } |
134 | | - |
135 | | - if ( mCache.deleteEntry( key ) ) { |
136 | | - writeMessage( peer, "OK: deleted\r\n" ); |
137 | | - } else { |
138 | | - writeMessage( peer, "NO: not found\r\n" ); |
139 | | - } |
140 | | - } else if ( command == "quit" ) { |
141 | | - // Let peer go out of scope and die |
142 | | - } else if ( command == "stats" ) { |
143 | | - writeStats( peer ); |
144 | | - } else if ( command == "kill" ) { |
145 | | - // TODO: for debugging only, remove this |
146 | | - mService.stop(); |
147 | | - } else { |
148 | | - writeMessage( peer, "ERROR: Unknown command\r\n" ); |
149 | | - } |
150 | | -} |
151 | | - |
152 | | -void App::writeMessage( SocketPtr peer, const char * msg ) { |
153 | | - Asio::async_write( |
154 | | - *peer, |
155 | | - Asio::const_buffers_1( msg, std::strlen( msg ) ), |
156 | | - boost::bind( &App::onWriteMessageDone, this, peer, _1, _2 ) ); |
157 | | -} |
158 | | - |
159 | | -void App::onWriteMessageDone( SocketPtr peer, const ErrorCode & writeError, std::size_t n ) { |
160 | | - if ( writeError ) { |
161 | | - // Close connection |
162 | | - return; |
163 | | - } |
164 | | - |
165 | | - startRead( peer ); |
166 | | -} |
167 | | - |
168 | | -void App::writeCacheEntry( SocketPtr peer, StringPtr entry ) { |
169 | | - const char prefix[] = "VALUE: "; |
170 | | - boost::array<Asio::const_buffer, 3> buffers; |
171 | | - buffers[0] = Asio::const_buffer( prefix, sizeof( prefix ) ); |
172 | | - buffers[1] = Asio::const_buffer( entry->data(), entry->size() ); |
173 | | - buffers[2] = Asio::const_buffer( "\r\n", 2 ); |
174 | | - |
175 | | - Asio::async_write( *peer, buffers, |
176 | | - boost::bind( |
177 | | - &App::onWriteCacheEntryDone, this, peer, |
178 | | - entry, // just to keep a reference in memory so buffers[1] doesn't dangle |
179 | | - _1, _2 |
180 | | - ) |
181 | | - ); |
182 | | -} |
183 | | - |
184 | | -void App::onWriteCacheEntryDone( SocketPtr peer, StringPtr entry, |
185 | | - const ErrorCode & writeError, std::size_t n ) |
186 | | -{ |
187 | | - if ( writeError ) { |
188 | | - // Close connection |
189 | | - return; |
190 | | - } |
191 | | - startRead( peer ); |
192 | | - // entry will go out of scope here and may be deleted |
193 | | -} |
194 | | - |
195 | | -void App::writeStats( SocketPtr peer ) { |
196 | | - std::stringstream s; |
197 | | - s << "STATS:" |
198 | | - << " num-bytes=" << mCache.getNumBytes() |
199 | | - << " num-entries=" << mCache.getSize() |
200 | | - << " load-factor=" << mCache.getLoadFactor() |
201 | | - << " max-bytes=" << mCache.getMaxBytes() |
202 | | - << " max-load-factor=" << mCache.getMaxLoadFactor() |
203 | | - << "\r\n"; |
204 | | - StringPtr sp( new std::string( s.str() ) ); |
205 | | - Asio::async_write( |
206 | | - *peer, |
207 | | - Asio::const_buffers_1( sp->data(), sp->size() ), |
208 | | - boost::bind( |
209 | | - &App::onWriteStatsDone, this, peer, |
210 | | - sp, // just to keep a reference to sp |
211 | | - _1, _2 |
212 | | - ) |
213 | | - ); |
214 | | -} |
215 | | - |
216 | | -void App::onWriteStatsDone( SocketPtr peer, StringPtr buffer, |
217 | | - const ErrorCode & writeError, std::size_t n ) |
218 | | -{ |
219 | | - if ( writeError ) { |
220 | | - // Close connection |
221 | | - return; |
222 | | - } |
223 | | - startRead( peer ); |
224 | | - // buffer will go out of scope here and will be deleted |
225 | | -} |
226 | | - |
Index: trunk/tools/maxcache/Cache.cpp |
— | — | @@ -9,6 +9,7 @@ |
10 | 10 | mBuckets( new KeyTable::bucket_type[mNumBuckets] ), |
11 | 11 | mKeyTable( KeyTable::bucket_traits( mBuckets.get(), mNumBuckets ) ), |
12 | 12 | mClock( 0 ), |
| 13 | + mEntryPool( 1024 ), |
13 | 14 | mNumBytes( mNumBuckets * sizeof( KeyTable::bucket_type ) ), |
14 | 15 | mMaxBytes( maxBytes ), |
15 | 16 | mMaxEntrySizeLog2( 28 ), |
— | — | @@ -31,7 +32,7 @@ |
32 | 33 | { |
33 | 34 | boost::uint64_t cost = ( (boost::uint64_t)clientCost << mMaxEntrySizeLog2 ) |
34 | 35 | / value->size(); |
35 | | - CacheEntry * entry = new CacheEntry( *this, key, value, cost, expiry ); |
| 36 | + CacheEntry * entry = newEntry( key, value, cost, expiry ); |
36 | 37 | setEntryPointer( entry ); |
37 | 38 | } |
38 | 39 | |
— | — | @@ -45,7 +46,7 @@ |
46 | 47 | // Too big, ignore this request. |
47 | 48 | // This is equivalent to immediate eviction, so it doesn't break |
48 | 49 | // the semantics of the insert operation. |
49 | | - delete entry; |
| 50 | + freeEntry( entry ); |
50 | 51 | return; |
51 | 52 | } |
52 | 53 | |
— | — | @@ -93,7 +94,7 @@ |
94 | 95 | mKeyTable.erase( mKeyTable.iterator_to( entry ) ); |
95 | 96 | mCostTree.erase( mCostTree.iterator_to( entry ) ); |
96 | 97 | mExpiryTree.erase( mExpiryTree.iterator_to( entry ) ); |
97 | | - delete &entry; |
| 98 | + freeEntry( &entry ); |
98 | 99 | } |
99 | 100 | |
100 | 101 | void Cache::evictUntilNormalSize() { |
Index: trunk/tools/maxcache/Session.cpp |
— | — | @@ -0,0 +1,216 @@ |
| 2 | +#include "Session.h" |
| 3 | +#include "App.h" |
| 4 | +#include <boost/bind.hpp> |
| 5 | +#include <boost/foreach.hpp> |
| 6 | + |
| 7 | +using namespace MaxCache; |
| 8 | + |
| 9 | +Session::Session( App & app ) |
| 10 | + : mApp( app ), |
| 11 | + mSocket( app.getService() ), |
| 12 | + mInputBuffer( app.getMaxCmdLength() ) |
| 13 | +{} |
| 14 | + |
| 15 | +void Session::reset() |
| 16 | +{ |
| 17 | + if ( mSocket.is_open() ) { |
| 18 | + mSocket.shutdown( Socket::shutdown_both ); |
| 19 | + mSocket.close(); |
| 20 | + } |
| 21 | + if ( mInputBuffer.size() ) { |
| 22 | + mInputBuffer.consume( mInputBuffer.size() ); |
| 23 | + } |
| 24 | + mWriteBuffer.clear(); |
| 25 | +} |
| 26 | + |
| 27 | +void Session::startRead() |
| 28 | +{ |
| 29 | + Asio::async_read_until( mSocket, mInputBuffer, "\r\n", |
| 30 | + boost::bind( &Session::onLineReady, this, _1, _2 ) ); |
| 31 | +} |
| 32 | + |
| 33 | +void Session::onLineReady( const ErrorCode & readError, std::size_t n ) |
| 34 | +{ |
| 35 | + if ( readError ) { |
| 36 | + // Close connection |
| 37 | + mApp.freeSession( *this ); |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + std::string line; |
| 42 | + line.reserve( n ); |
| 43 | + BOOST_FOREACH( Asio::const_buffer block, mInputBuffer.data() ) { |
| 44 | + line.append( Asio::buffer_cast<const char*>( block ), Asio::buffer_size( block ) ); |
| 45 | + } |
| 46 | + mInputBuffer.consume( n ); |
| 47 | + std::stringstream stream( line ); |
| 48 | + |
| 49 | + // Formatted extraction requires a sensible locale to be set |
| 50 | + stream.imbue( std::locale( "C" ) ); |
| 51 | + |
| 52 | + std::string command; |
| 53 | + stream >> command; |
| 54 | + if ( handleTooFewParams( stream ) ) return; |
| 55 | + |
| 56 | + if ( command == "get" ) { |
| 57 | + std::string key; |
| 58 | + stream >> key; |
| 59 | + if ( handleTooFewParams( stream ) ) return; |
| 60 | + |
| 61 | + CacheEntry * entry = mApp.getCache().getEntry( key ); |
| 62 | + if ( !entry ) { |
| 63 | + writeMessage( "NO: Item not found\r\n" ); |
| 64 | + return; |
| 65 | + } |
| 66 | + |
| 67 | + writeCacheEntry( entry->getValue() ); |
| 68 | + |
| 69 | + } else if ( command == "set" ) { |
| 70 | + // Usage: set <key> <expiry> <cost> <value> |
| 71 | + std::string key; |
| 72 | + boost::uint32_t expiryInterval, clientCost; |
| 73 | + StringPtr value( new std::string ); |
| 74 | + |
| 75 | + stream.width( mApp.getMaxKeyLength() + 1 ); |
| 76 | + stream >> key; |
| 77 | + if ( handleTooFewParams( stream ) ) return; |
| 78 | + if ( key.size() > mApp.getMaxKeyLength() ) { |
| 79 | + writeMessage( "ERROR: Key too long\r\n" ); |
| 80 | + return; |
| 81 | + } |
| 82 | + |
| 83 | + stream >> expiryInterval; |
| 84 | + if ( handleTooFewParams( stream ) ) return; |
| 85 | + if ( stream.fail() ) { |
| 86 | + writeMessage( "ERROR: invalid expiry interval\r\n" ); |
| 87 | + return; |
| 88 | + } |
| 89 | + |
| 90 | + // Read the client cost, which is later divided by the size to get the |
| 91 | + // real cost |
| 92 | + stream >> clientCost; |
| 93 | + if ( handleTooFewParams( stream ) ) return; |
| 94 | + if ( stream.fail() ) { |
| 95 | + writeMessage( "ERROR: invalid clientCost\r\n" ); |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + stream >> *value; |
| 100 | + if ( handleTooFewParams( stream ) ) return; |
| 101 | + if ( value->size() > mApp.getCache().getMaxEntrySize() ) { |
| 102 | + // This won't happen very often, usually the ASIO buffer limit will |
| 103 | + // be exceeded instead. |
| 104 | + writeMessage( "ERROR: value too big\r\n" ); |
| 105 | + return; |
| 106 | + } |
| 107 | + |
| 108 | + Time expiry; |
| 109 | + if ( expiryInterval ) { |
| 110 | + expiry = mApp.getTime() + boost::posix_time::microseconds( expiryInterval ); |
| 111 | + } else { |
| 112 | + expiry = Time( App::INFIN_TIME ); |
| 113 | + } |
| 114 | + |
| 115 | + mApp.getCache().setEntry( key, value, clientCost, expiry ); |
| 116 | + writeMessage( "OK: value set\r\n" ); |
| 117 | + } else if ( command == "delete" ) { |
| 118 | + std::string key; |
| 119 | + stream.width( mApp.getMaxKeyLength() + 1 ); |
| 120 | + stream >> key; |
| 121 | + if ( handleTooFewParams( stream ) ) return; |
| 122 | + if ( key.size() > mApp.getMaxKeyLength() ) { |
| 123 | + writeMessage( "ERROR: Key too long\r\n" ); |
| 124 | + return; |
| 125 | + } |
| 126 | + |
| 127 | + if ( mApp.getCache().deleteEntry( key ) ) { |
| 128 | + writeMessage( "OK: deleted\r\n" ); |
| 129 | + } else { |
| 130 | + writeMessage( "NO: not found\r\n" ); |
| 131 | + } |
| 132 | + } else if ( command == "quit" ) { |
| 133 | + mApp.freeSession( *this ); |
| 134 | + } else if ( command == "stats" ) { |
| 135 | + writeStats(); |
| 136 | + } else if ( command == "kill" ) { |
| 137 | + // TODO: for debugging only, remove this |
| 138 | + mApp.stop(); |
| 139 | + } else { |
| 140 | + writeMessage( "ERROR: Unknown command\r\n" ); |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +void Session::writeMessage( const char * msg ) { |
| 145 | + Asio::async_write( |
| 146 | + mSocket, |
| 147 | + Asio::const_buffers_1( msg, std::strlen( msg ) ), |
| 148 | + boost::bind( &Session::onWriteMessageDone, this, _1, _2 ) ); |
| 149 | +} |
| 150 | + |
| 151 | +void Session::onWriteMessageDone( const ErrorCode & writeError, std::size_t n ) { |
| 152 | + if ( writeError ) { |
| 153 | + // Close connection |
| 154 | + mApp.freeSession( *this ); |
| 155 | + return; |
| 156 | + } |
| 157 | + |
| 158 | + startRead(); |
| 159 | +} |
| 160 | + |
| 161 | +void Session::writeCacheEntry( StringPtr entry ) { |
| 162 | + const char prefix[] = "VALUE: "; |
| 163 | + |
| 164 | + boost::array<Asio::const_buffer, 3> buffers; |
| 165 | + buffers[0] = Asio::const_buffer( prefix, sizeof( prefix ) ); |
| 166 | + buffers[1] = Asio::const_buffer( entry->data(), entry->size() ); |
| 167 | + buffers[2] = Asio::const_buffer( "\r\n", 2 ); |
| 168 | + |
| 169 | + Asio::async_write( mSocket, buffers, |
| 170 | + boost::bind( |
| 171 | + &Session::onWriteCacheEntryDone, this, |
| 172 | + entry, // just to keep a reference in memory so buffers[1] doesn't dangle |
| 173 | + _1, _2 |
| 174 | + ) |
| 175 | + ); |
| 176 | +} |
| 177 | + |
| 178 | +void Session::onWriteCacheEntryDone( StringPtr entry, const ErrorCode & writeError, |
| 179 | + std::size_t n ) |
| 180 | +{ |
| 181 | + if ( writeError ) { |
| 182 | + // Close connection |
| 183 | + mApp.freeSession( *this ); |
| 184 | + return; |
| 185 | + } |
| 186 | + startRead(); |
| 187 | + // entry will go out of scope here and may be deleted |
| 188 | +} |
| 189 | + |
| 190 | +void Session::writeStats() { |
| 191 | + std::stringstream s; |
| 192 | + s << "STATS:" |
| 193 | + << " num-bytes=" << mApp.getCache().getNumBytes() |
| 194 | + << " num-entries=" << mApp.getCache().getSize() |
| 195 | + << " load-factor=" << mApp.getCache().getLoadFactor() |
| 196 | + << " max-bytes=" << mApp.getCache().getMaxBytes() |
| 197 | + << " max-load-factor=" << mApp.getCache().getMaxLoadFactor() |
| 198 | + << "\r\n"; |
| 199 | + mWriteBuffer = s.str(); |
| 200 | + Asio::async_write( |
| 201 | + mSocket, |
| 202 | + Asio::const_buffers_1( mWriteBuffer.data(), mWriteBuffer.size() ), |
| 203 | + boost::bind( &Session::onWriteStatsDone, this, _1, _2 ) |
| 204 | + ); |
| 205 | +} |
| 206 | + |
| 207 | +void Session::onWriteStatsDone( const ErrorCode & writeError, std::size_t n ) |
| 208 | +{ |
| 209 | + if ( writeError ) { |
| 210 | + // Close connection |
| 211 | + mApp.freeSession( *this ); |
| 212 | + return; |
| 213 | + } |
| 214 | + startRead(); |
| 215 | + // buffer will go out of scope here and will be deleted |
| 216 | +} |
| 217 | + |
Property changes on: trunk/tools/maxcache/Session.cpp |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 218 | + native |
Index: trunk/tools/maxcache/App.h |
— | — | @@ -9,22 +9,11 @@ |
10 | 10 | #include <string> |
11 | 11 | #include "typedefs.h" |
12 | 12 | #include "Cache.h" |
| 13 | +#include "Session.h" |
13 | 14 | |
14 | 15 | namespace MaxCache { |
15 | 16 | |
16 | | -namespace Asio = boost::asio; |
17 | | -typedef boost::asio::ip::tcp Tcp; |
18 | | -typedef boost::asio::io_service Service; |
19 | | -typedef Tcp::endpoint Endpoint; |
20 | | -typedef Tcp::acceptor Acceptor; |
21 | | -typedef boost::shared_ptr<Acceptor> AcceptorPtr; |
22 | | -typedef Tcp::socket Socket; |
23 | | -typedef boost::shared_ptr<Socket> SocketPtr; |
24 | | -typedef boost::system::error_code ErrorCode; |
25 | | -typedef boost::system::system_error SystemError; |
26 | | -typedef boost::asio::streambuf Buffer; |
27 | | -typedef boost::shared_ptr<Buffer> BufferPtr; |
28 | | -typedef boost::asio::buffers_iterator< Buffer::const_buffers_type > BufferIterator; |
| 17 | +typedef Intrusive::list< Session, SessionListMemberOption > SessionList; |
29 | 18 | |
30 | 19 | class App { |
31 | 20 | public: |
— | — | @@ -33,8 +22,17 @@ |
34 | 23 | mCache( *this, 100000000 ) |
35 | 24 | {} |
36 | 25 | |
| 26 | + ~App(); |
| 27 | + |
37 | 28 | void run( int argc, char** argv ); |
38 | 29 | |
| 30 | + Session & activateSession(); |
| 31 | + void freeSession( Session & session ); |
| 32 | + |
| 33 | + void stop() { |
| 34 | + mService.stop(); |
| 35 | + } |
| 36 | + |
39 | 37 | Service & getService() { |
40 | 38 | return mService; |
41 | 39 | } |
— | — | @@ -56,47 +54,31 @@ |
57 | 55 | mTime = NO_TIME; |
58 | 56 | } |
59 | 57 | |
| 58 | + std::size_t getMaxKeyLength() const { return mMaxKeyLength; } |
| 59 | + |
60 | 60 | std::size_t getMaxCmdLength() const { |
61 | 61 | return mCache.getMaxEntrySize() + mMaxKeyLength + sizeof( "blah\r\n" ); |
62 | 62 | } |
63 | 63 | |
| 64 | + const Cache & getCache() const { return mCache; } |
| 65 | + Cache & getCache() { return mCache; } |
| 66 | + |
64 | 67 | void startAccept(); |
65 | | - void onAccept( SocketPtr peer, const ErrorCode & acceptError ); |
| 68 | + void onAccept( Session & session, const ErrorCode & acceptError ); |
66 | 69 | |
67 | | - void startRead( SocketPtr peer ); |
68 | | - void onLineReady( SocketPtr peer, BufferPtr buffer, |
69 | | - const ErrorCode & readError, std::size_t n ); |
70 | | - |
71 | | - void writeMessage( SocketPtr peer, const char * msg ); |
72 | | - void onWriteMessageDone( SocketPtr peer, const ErrorCode & writeMessage, std::size_t n ); |
73 | | - |
74 | | - void writeCacheEntry( SocketPtr peer, StringPtr entry ); |
75 | | - void onWriteCacheEntryDone( SocketPtr peer, StringPtr entry, |
76 | | - const ErrorCode & writeError, std::size_t n ); |
77 | | - |
78 | | - void writeStats( SocketPtr peer ); |
79 | | - void onWriteStatsDone( SocketPtr peer, StringPtr buffer, |
80 | | - const ErrorCode & writeError, std::size_t n ); |
| 70 | + const static Time NO_TIME; |
| 71 | + const static Time INFIN_TIME; |
81 | 72 | protected: |
82 | | - bool handleTooFewParams( SocketPtr peer, std::istream & stream ) { |
83 | | - if ( stream.eof() ) { |
84 | | - writeMessage( peer, "ERROR: Not enough parameters\r\n" ); |
85 | | - return true; |
86 | | - } else { |
87 | | - return false; |
88 | | - } |
89 | | - } |
90 | 73 | |
91 | | - |
92 | 74 | Service mService; |
93 | 75 | AcceptorPtr mAcceptor; |
| 76 | + SessionList mFreeSessions; |
| 77 | + SessionList mActiveSessions; |
94 | 78 | |
95 | 79 | const std::size_t mMaxKeyLength; |
96 | 80 | |
97 | 81 | Cache mCache; |
98 | 82 | Time mTime; |
99 | | - |
100 | | - const static Time NO_TIME; |
101 | 83 | }; |
102 | 84 | |
103 | 85 | } |
Index: trunk/tools/maxcache/Cache.h |
— | — | @@ -4,6 +4,7 @@ |
5 | 5 | #include "boost-config.h" |
6 | 6 | #include "CacheEntry.h" |
7 | 7 | #include <boost/shared_array.hpp> |
| 8 | +#include <boost/pool/object_pool.hpp> |
8 | 9 | |
9 | 10 | namespace MaxCache { |
10 | 11 | |
— | — | @@ -27,6 +28,8 @@ |
28 | 29 | compare< CacheEntry::CompareExpiries > |
29 | 30 | > ExpiryTree; |
30 | 31 | |
| 32 | +typedef boost::object_pool<CacheEntry> CacheEntryPool; |
| 33 | + |
31 | 34 | class App; |
32 | 35 | |
33 | 36 | class Cache { |
— | — | @@ -119,6 +122,27 @@ |
120 | 123 | */ |
121 | 124 | void evictExpiredEntries(); |
122 | 125 | |
| 126 | + /** |
| 127 | + * Create a new CacheEntry object |
| 128 | + */ |
| 129 | + CacheEntry * newEntry( const std::string & key, StringPtr value, |
| 130 | + boost::uint64_t cost, Time expiry ) |
| 131 | + { |
| 132 | + CacheEntry * entry = mEntryPool.malloc(); |
| 133 | + mEntryPool.set_next_size( 1024 ); |
| 134 | + new ( entry ) CacheEntry( *this, key, value, cost, expiry ); |
| 135 | + return entry; |
| 136 | + //return new CacheEntry( *this, key, value, cost, expiry ); |
| 137 | + } |
| 138 | + |
| 139 | + /** |
| 140 | + * Free a CacheEntry object created by newEntry() |
| 141 | + */ |
| 142 | + void freeEntry( CacheEntry * entry ) { |
| 143 | + mEntryPool.free( entry ); |
| 144 | + //delete entry; |
| 145 | + } |
| 146 | + |
123 | 147 | float mMaxLoadFactor; |
124 | 148 | std::size_t mNumBuckets; |
125 | 149 | boost::shared_array<KeyTable::bucket_type> mBuckets; |
— | — | @@ -128,6 +152,8 @@ |
129 | 153 | ExpiryTree mExpiryTree; |
130 | 154 | boost::uint64_t mClock; |
131 | 155 | |
| 156 | + CacheEntryPool mEntryPool; |
| 157 | + |
132 | 158 | std::size_t mNumBytes; |
133 | 159 | std::size_t mMaxBytes; |
134 | 160 | unsigned char mMaxEntrySizeLog2; |
Index: trunk/tools/maxcache/Session.h |
— | — | @@ -0,0 +1,58 @@ |
| 2 | +#ifndef MAXCACHE_SESSION_H |
| 3 | +#define MAXCACHE_SESSION_H |
| 4 | + |
| 5 | +#include "boost-config.h" |
| 6 | +#include <boost/intrusive/list.hpp> |
| 7 | +#include "typedefs.h" |
| 8 | + |
| 9 | +using namespace boost::intrusive; |
| 10 | + |
| 11 | +namespace MaxCache { |
| 12 | + |
| 13 | +typedef Intrusive::list_member_hook<> ListHook; |
| 14 | + |
| 15 | +class App; |
| 16 | + |
| 17 | +class Session { |
| 18 | + public: |
| 19 | + Session( App & app ); |
| 20 | + void reset(); |
| 21 | + |
| 22 | + Socket & getSocket() { |
| 23 | + return mSocket; |
| 24 | + } |
| 25 | + |
| 26 | + void startRead(); |
| 27 | + void onLineReady( const ErrorCode & readError, std::size_t n ); |
| 28 | + |
| 29 | + void writeMessage( const char * msg ); |
| 30 | + void onWriteMessageDone( const ErrorCode & writeError, std::size_t n ); |
| 31 | + |
| 32 | + void writeCacheEntry( StringPtr entry ); |
| 33 | + void onWriteCacheEntryDone( StringPtr entry, const ErrorCode & writeError, std::size_t n ); |
| 34 | + |
| 35 | + void writeStats(); |
| 36 | + void onWriteStatsDone( const ErrorCode & writeError, std::size_t n ); |
| 37 | + |
| 38 | + ListHook mListHook; |
| 39 | + |
| 40 | + protected: |
| 41 | + bool handleTooFewParams( std::istream & stream ) { |
| 42 | + if ( stream.eof() ) { |
| 43 | + writeMessage( "ERROR: Not enough parameters\r\n" ); |
| 44 | + return true; |
| 45 | + } else { |
| 46 | + return false; |
| 47 | + } |
| 48 | + } |
| 49 | + |
| 50 | + App & mApp; |
| 51 | + Socket mSocket; |
| 52 | + Buffer mInputBuffer; |
| 53 | + std::string mWriteBuffer; |
| 54 | +}; |
| 55 | + |
| 56 | +typedef Intrusive::member_hook< Session, ListHook, &Session::mListHook > SessionListMemberOption; |
| 57 | + |
| 58 | +} |
| 59 | +#endif |
Property changes on: trunk/tools/maxcache/Session.h |
___________________________________________________________________ |
Name: svn:eol-style |
1 | 60 | + native |
Index: trunk/tools/maxcache/typedefs.h |
— | — | @@ -1,12 +1,36 @@ |
2 | 2 | #ifndef MAXCACHE_TYPEDEFS_H |
3 | 3 | #define MAXCACHE_TYPEDEFS_H |
4 | 4 | |
| 5 | +#include "boost-config.h" |
5 | 6 | #include <string> |
6 | 7 | #include <boost/shared_ptr.hpp> |
7 | 8 | #include <boost/date_time/posix_time/posix_time_types.hpp> |
| 9 | +#include <boost/intrusive/intrusive_fwd.hpp> |
| 10 | +#include <boost/asio.hpp> |
| 11 | + |
8 | 12 | namespace MaxCache { |
| 13 | + // Misc |
9 | 14 | typedef boost::shared_ptr<std::string> StringPtr; |
10 | 15 | typedef boost::posix_time::ptime Time; |
| 16 | + |
| 17 | + // Intrusive |
| 18 | + namespace Intrusive = boost::intrusive; |
| 19 | + |
| 20 | + // Asio |
| 21 | + namespace Asio = boost::asio; |
| 22 | + |
| 23 | + typedef Asio::ip::tcp Tcp; |
| 24 | + typedef Asio::io_service Service; |
| 25 | + typedef Tcp::endpoint Endpoint; |
| 26 | + typedef Tcp::acceptor Acceptor; |
| 27 | + typedef boost::shared_ptr<Acceptor> AcceptorPtr; |
| 28 | + typedef boost::system::error_code ErrorCode; |
| 29 | + typedef boost::system::system_error SystemError; |
| 30 | + |
| 31 | + typedef Asio::streambuf Buffer; |
| 32 | + typedef Asio::buffers_iterator< Buffer::const_buffers_type > BufferIterator; |
| 33 | + typedef Tcp::socket Socket; |
| 34 | + |
11 | 35 | } |
12 | 36 | |
13 | 37 | #endif |
Index: trunk/tools/maxcache/CacheEntry.h |
— | — | @@ -14,10 +14,8 @@ |
15 | 15 | |
16 | 16 | namespace MaxCache { |
17 | 17 | |
18 | | -namespace Intrusive = boost::intrusive; |
19 | | - |
20 | 18 | typedef Intrusive::unordered_set_member_hook< store_hash<true> > HashHook; |
21 | | -typedef Intrusive::set_base_hook<> TreeHook; |
| 19 | +typedef Intrusive::set_member_hook<> TreeHook; |
22 | 20 | |
23 | 21 | class Cache; |
24 | 22 | |
Index: trunk/tools/maxcache/Makefile |
— | — | @@ -2,7 +2,7 @@ |
3 | 3 | |
4 | 4 | CFLAGS:=$(CFLAGS) -ggdb3 -Wall |
5 | 5 | |
6 | | -maxcache: maxcache.o App.o CacheEntry.o Cache.o |
| 6 | +maxcache: maxcache.o App.o CacheEntry.o Cache.o Session.o |
7 | 7 | g++ $^ $(CFLAGS) -lboost_system -o $@ |
8 | 8 | |
9 | 9 | %.o : %.cpp |