|
#include "stdafx.h"
#include "Transactions\MemManager.h"
#include "Transactions\Allocator.h"
#include <vector>
#include <math.h>
#include <iostream>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// first, we need some silly classes to work with. for the sake of this
// code, consider that we can't modify these classes to add seriziation or
// undo/redo semantics, but need to provide undo/redo support.
//
struct Bar {
Bar() : d(0.0) {}
~Bar() {}
struct Foo {
Foo() : f1(0), f2(0) {}
int f1;
long f2;
};
double d;
Foo v[10];
};
////////////////////////////////////////////////////////////////////////////////
// C++ helpers. we need to define a helper method to make sure that our
// objects are allocated in the transacted heap. this is just one way to do
// that. ideally, you'd want to override operator new() on the class.
//
template <class T>
T* MyNew() { return new (Mm::Allocate(sizeof(T))) T(); }
template <class T>
void MyDelete(T* p) { p->~T(); Mm::Deallocate(p); }
////////////////////////////////////////////////////////////////////////////////
// STL NOTE: specify Mm::Allocator (or your own version) as the allocator
// for all containers that will be created in the managed memory.
//
typedef std::vector<Bar, Mm::Allocator<Bar> > BarList;
// ^^^^^^^^^^^^^^^^^^
////////////////////////////////////////////////////////////////////////////////
// LET'S GIVE IT A TRY!
//
// This method demonstrates a number of ways that transactions get used, and
// serves as a unit test for development of Mm. If you find problems with
// Mm, please try to include a snipit of code for this method that will
// serve to verify the fix. It isn't required, but it would be nice of you. :)
//
int DoWork(int argc, char* argv[], char** envp)
{
// 1 - the trivial (but important) case of an empty transaction. success
// here is not crashing
//
Mm::OpenTransaction();
Mm::CommitTransaction();
Mm::Undo();
Mm::Redo();
// 2 - A simple test of allocating some memory and copying a string there.
// The string should still be there after an undo + redo cycle. Note that
// the pointer "p" is not in managed memory and won't be transacted, but the
// memory it points to IS transacted. If you don't get that, try again
// because it is a critical concept.
//
// Note that allocations and deallocations change memory, and need to be
// put inside transactions or they will cause unhandled exceptions.
//
Mm::OpenTransaction();
static char* text = "This is a test.";
static unsigned int len = strlen(text);
void* p = Mm::Allocate(len+1);
memcpy(p, text, len);
((char*)p)[len] = '\0';
Mm::CommitTransaction();
Mm::Undo();
Mm::Redo();
int ok1 = strcmp((char*)p, text);
if (ok1 != 0) throw;
// 3 - Modify the string and test the contents after undo, and again
// after redo.
//
Mm::OpenTransaction();
((char*)p)[4] = '\0';
Mm::CommitTransaction();
Mm::TXNID txn3 = Mm::GetLastTransactionId();
Mm::Undo();
int ok2 = strcmp((char*)p, text);
if (ok2 != 0) throw;
Mm::Redo();
int ok3 = strcmp((char*)p, "This");
if (ok3 != 0) throw;
// 4 - Deallocate the string. success means it's still there after undo.
// Remember that p isn't transacted, so after deallocation (and redo) it
// points to deallocated memory. Danger!
//
Mm::OpenTransaction();
Mm::Deallocate(p);
Mm::CommitTransaction();
Mm::Undo();
int ok4 = strcmp((char*)p, "This");
if (ok4 != 0) throw;
Mm::Redo();
// 5 - Make increasingly large allocations and immeadiately deallocate
// each. A better memory manager wouldn't need to allocate so much
// virtual address space to handle this. So write one and send it to
// me. :) As long as we don't crash, consider this good.
//
Mm::OpenTransaction();
for (unsigned int s = 0; s < 24; ++s) {
size_t size = pow(2,s);
void* p = Mm::Allocate(size);
Mm::Deallocate(p);
}
Mm::CommitTransaction();
Mm::Undo();
Mm::Redo();
// 6 - Make the same allocation over and over again using the C++
// helper to put the object in transacted memory. We shouldn't crash
// (heh) and only a couple pages should be touched no matter how
// many times we do this.
Mm::OpenTransaction();
for (s = 0; s < 100; ++s) {
Bar* b = MyNew<Bar>();
MyDelete(b);
}
Mm::CommitTransaction();
Mm::Undo();
Mm::Redo();
// 7 -- Make an STL container in transacted memory and fill it with
// Bar objects. If you use STL, be careful not to put a container with
// transacted memory on the stack durning a transaction. Best case it
// causes the transaction to bloat in size, worse case you end up with
// a container that is out of sync with its contents and crashes. This
// should be a pretty big transaction.
Mm::OpenTransaction();
BarList* pbl = MyNew<BarList>();
for (unsigned int i = 0; i < 10000; ++i) {
Bar b;
(*pbl).push_back(b);
}
Mm::CommitTransaction();
Mm::Undo();
Mm::Redo();
// 8 - Make some random changes to the objects in the container above.
Mm::OpenTransaction();
for (unsigned int k = 0; k < 100; ++k) {
(*pbl)[rand()%i].d = 3.14159;
(*pbl)[rand()%i].v[rand()%10].f1 = 11;
(*pbl)[rand()%i].v[rand()%10].f2 = 22;
}
Mm::CommitTransaction();
Mm::Undo();
// all objects in the container should be back to their original state
Mm::Redo();
// 9 - Delete the container
Mm::OpenTransaction();
MyDelete(pbl);
Mm::CommitTransaction();
Mm::Undo();
Mm::Redo();
cout << "# We should have 9 transactions below, and loads of free memory" << endl;
// Mm::DumpStats(cout);
// 10 - undo back to the point we modified the string (#3) and
// verify that the string is restored. Note that in the process we
// undo the STL container back into, then out of, existence while
// moving back in time.
while (Mm::GetLastTransactionId() != txn3) Mm::Undo();
int ok5 = strcmp((char*)p, "This");
if (ok5 != 0) throw;
// memory stats dumped to cout should indicate the memory state after
// transaction 3.
//
cout << "# We should have 9 transactions below, with the undo marker between #3 and #4." << endl;
cout << "# The amount of free memory should match that availble after transaction #3." << endl;
// Mm::DumpStats(cout);
// memory stats dumped to cout should STILL indicate the memory state
// after transaction 3.
//
Mm::OpenTransaction();
Mm::CancelTransaction();
cout << "# We should have 9 transactions below, with the undo marker between #3 and #4." << endl;
cout << "# (Same as above.)" << endl;
// Mm::DumpStats(cout);
// NOW memory stats dumped to cout should indicate the memory state
// after transaction 3 plus all the other memory added in subsequent
// transactions (which can be released since the redo stack has
// been dumped). The transaction won't be empty since we have to
// update the free list prior to committing.
//
Mm::OpenTransaction();
Mm::CommitTransaction();
cout << "# We should have 4 transactions below, with the undo marker after #4." << endl;
cout << "# Transaction #4 should show some pages touched, even though we don't" << endl;
cout << "# explicitly change anything during the transaction." << endl;
// Mm::DumpStats(cout);
return 0;
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
A compiler warns of bogasity, ignore it at your peril. Unless you've done the compiler's job yourself, don't criticize it.