re-wrote ftfileprovider / ftfilecreator to be more simple and robust.

re-wrote associated tests to be more robust.
added new fttransfermoduletest.cc 



git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@760 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
drbob 2008-10-22 18:12:58 +00:00
parent 26661ffb1c
commit ef9bb372b4
10 changed files with 573 additions and 786 deletions

View File

@ -12,10 +12,10 @@ RSOBJ = ftdata.o ftfileprovider.o ftfilecreator.o ftextralist.o \
ftcontroller.o pqitestor.o ftcontroller.o pqitestor.o
TESTOBJ = ftfileprovidertest.o ftfilecreatortest.o ftextralisttest.o ftdataplextest.o ftserver1test.o ftserver2test.o TESTOBJ = ftfileprovidertest.o ftfilecreatortest.o ftextralisttest.o ftdataplextest.o ftserver1test.o ftserver2test.o fttransfermoduletest.o
TESTS = ftfileprovidertest ftfilecreatortest ftextralisttest ftdataplextest ftserver1test ftserver2test TESTS = ftfileprovidertest ftfilecreatortest ftextralisttest ftdataplextest ftserver1test ftserver2test fttransfermoduletest
all: librs tests all: librs tests
@ -25,6 +25,9 @@ ftfilecreatortest : ftfilecreatortest.o
ftfileprovidertest : ftfileprovidertest.o ftfileprovidertest : ftfileprovidertest.o
$(CC) $(CFLAGS) -o ftfileprovidertest ftfileprovidertest.o $(LIBS) $(CC) $(CFLAGS) -o ftfileprovidertest ftfileprovidertest.o $(LIBS)
fttransfermoduletest : fttransfermoduletest.o
$(CC) $(CFLAGS) -o fttransfermoduletest fttransfermoduletest.o $(LIBS)
ftextralisttest : ftextralisttest.o ftextralisttest : ftextralisttest.o
$(CC) $(CFLAGS) -o ftextralisttest ftextralisttest.o $(LIBS) $(CC) $(CFLAGS) -o ftextralisttest ftextralisttest.o $(LIBS)

View File

@ -220,8 +220,7 @@ bool ftController::FileRequest(std::string fname, std::string hash,
/* add in new item for download */ /* add in new item for download */
std::string savepath = mDownloadPath + "/" + fname; std::string savepath = mDownloadPath + "/" + fname;
std::string chunker = "default"; ftFileCreator *fc = new ftFileCreator(savepath, size, hash, 0);
ftFileCreator *fc = new ftFileCreator(savepath, size, hash, chunker);
ftTransferModule *tm = new ftTransferModule(fc, mDataplex,this); ftTransferModule *tm = new ftTransferModule(fc, mDataplex,this);
/* add into maps */ /* add into maps */

View File

@ -2,6 +2,9 @@
#define FILE_DEBUG 1 #define FILE_DEBUG 1
#define CHUNK_MAX_AGE 30
/*********************************************************** /***********************************************************
* *
* ftFileCreator methods * ftFileCreator methods
@ -9,7 +12,7 @@
***********************************************************/ ***********************************************************/
ftFileCreator::ftFileCreator(std::string path, uint64_t size, std::string ftFileCreator::ftFileCreator(std::string path, uint64_t size, std::string
hash, std::string chunker): ftFileProvider(path,size,hash) hash, uint64_t recvd): ftFileProvider(path,size,hash)
{ {
/* /*
* FIXME any inits to do? * FIXME any inits to do?
@ -26,65 +29,29 @@ hash, std::string chunker): ftFileProvider(path,size,hash)
std::cerr << std::endl; std::cerr << std::endl;
#endif #endif
initialize(chunker);
}
void ftFileCreator::initialize(std::string chunker)
{
RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
if (chunker == "default") /* initialise the Transfer Lists */
fileChunker = new ftFileChunker(total_size); mStart = recvd;
else mEnd = recvd;
fileChunker = new ftFileRandomizeChunker(total_size);
fileChunker->splitFile();
} }
int ftFileCreator::initializeFileAttrs() bool ftFileCreator::getFileData(uint64_t offset,
uint32_t chunk_size, void *data)
{ {
//RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
if (offset + chunk_size > mStart)
/*
* check if the file exists
*/
#ifdef FILE_DEBUG
std::cerr << "ftFileCreator::initializeFileAttrs() Filename: " << file_name;
std::cerr << std::endl;
#endif
/*
* attempt to open file in writing mode
*/
fd = fopen(file_name.c_str(), "w+b");
if (!fd)
{ {
#ifdef FILE_DEBUG /* don't have the data */
std::cerr << "ftFileCreator::initializeFileAttrs() Failed to open (w+b): " << file_name << std::endl; return false;
#endif
return 0;
} }
return ftFileProvider::getFileData(offset, chunk_size, data);
}
/* uint64_t ftFileCreator::getRecvd()
* if it opened, find it's length {
* move to the end RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
*/ return mStart;
if (0 != fseek(fd, 0L, SEEK_END))
{
std::cerr << "ftFileCreator::initializeFileAttrs() Seek Failed" << std::endl;
return 0;
}
/*
* get the file length
*/
recv_size = ftell(fd);
#ifdef FILE_DEBUG
std::cerr << "ftFileCreator::initializeFileAttrs() recv_size: " << recv_size << std::endl;
#endif
return 1;
} }
bool ftFileCreator::addFileData(uint64_t offset, uint32_t chunk_size, void *data) bool ftFileCreator::addFileData(uint64_t offset, uint32_t chunk_size, void *data)
@ -96,28 +63,22 @@ bool ftFileCreator::addFileData(uint64_t offset, uint32_t chunk_size, void *data
std::cerr << ", " << data << ")"; std::cerr << ", " << data << ")";
std::cerr << std::endl; std::cerr << std::endl;
#endif #endif
RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/ /* dodgey checking outside of mutex...
/* check the status */ * much check again inside FileAttrs().
*/
/* Check File is open */
if (fd == NULL)
if (!initializeFileAttrs())
return false;
if (fd==NULL) RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
{
#ifdef FILE_DEBUG
std::cerr << "ftFileCreator::addFileData() initialising";
std::cerr << std::endl;
#endif
int init = initializeFileAttrs();
if (init ==0) {
std::cerr <<"Initialization Failed" << std::endl;
return 0;
}
}
/* /*
* check its at the correct location * check its at the correct location
*/ */
if (offset + chunk_size > this->total_size) if (offset + chunk_size > mSize)
{ {
chunk_size = total_size - offset; chunk_size = mSize - offset;
std::cerr <<"Chunk Size greater than total file size, adjusting chunk " << "size " << chunk_size << std::endl; std::cerr <<"Chunk Size greater than total file size, adjusting chunk " << "size " << chunk_size << std::endl;
} }
@ -133,8 +94,6 @@ bool ftFileCreator::addFileData(uint64_t offset, uint32_t chunk_size, void *data
long int pos; long int pos;
pos = ftell(fd); pos = ftell(fd);
std::cerr << pos << " BEFORE RECV SIZE "<< recv_size << std::endl;
/* /*
* add the data * add the data
*/ */
@ -144,21 +103,18 @@ bool ftFileCreator::addFileData(uint64_t offset, uint32_t chunk_size, void *data
return 0; return 0;
} }
this->recv_size += chunk_size;
pos = ftell(fd); pos = ftell(fd);
#ifdef FILE_DEBUG #ifdef FILE_DEBUG
std::cerr << "ftFileCreator::addFileData() added Data..."; std::cerr << "ftFileCreator::addFileData() added Data...";
std::cerr << std::endl; std::cerr << std::endl;
std::cerr << "recvd: " << recv_size;
std::cerr << " pos: " << pos; std::cerr << " pos: " << pos;
std::cerr << std::endl; std::cerr << std::endl;
#endif #endif
/* /*
* Notify ftFileChunker about chunks received * Notify ftFileChunker about chunks received
*/ */
fileChunker->notifyReceived(offset,chunk_size); notifyReceived(offset,chunk_size);
/* /*
* FIXME HANDLE COMPLETION HERE - Any better way? * FIXME HANDLE COMPLETION HERE - Any better way?
@ -167,6 +123,60 @@ bool ftFileCreator::addFileData(uint64_t offset, uint32_t chunk_size, void *data
return 1; return 1;
} }
int ftFileCreator::initializeFileAttrs()
{
std::cerr << "ftFileCreator::initializeFileAttrs() Filename: ";
std::cerr << file_name;
/*
* check if the file exists
*/
if (ftFileProvider::initializeFileAttrs())
{
return 1;
}
RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
if (fd)
return 1;
{
std::cout <<
"ftFileCreator::initializeFileAttrs() Filename: " << file_name;
}
/*
* attempt to open file
*/
fd = fopen(file_name.c_str(), "w+b");
if (!fd)
{
std::cout <<
"ftFileCreator::initializeFileAttrs() Failed to open (w+b): "<< file_name << std::endl;
return 0;
}
/*
* if it opened, find it's length
* move to the end
*/
if (0 != fseek(fd, 0L, SEEK_END))
{
std::cerr << "ftFileCreator::initializeFileAttrs() Seek Failed" << std::endl;
return 0;
}
uint64_t recvdsize = ftell(fd);
std::cerr << "ftFileCreator::initializeFileAttrs() File Expected Size: " << mSize << " RecvdSize: " << recvdsize << std::endl;
return 1;
}
ftFileCreator::~ftFileCreator() ftFileCreator::~ftFileCreator()
{ {
/* /*
@ -175,546 +185,104 @@ ftFileCreator::~ftFileCreator()
} }
bool ftFileCreator::getMissingChunk(uint64_t &offset, uint32_t &chunk) int ftFileCreator::notifyReceived(uint64_t offset, uint32_t chunk_size)
{ {
#ifdef FILE_DEBUG /* ALREADY LOCKED */
std::cerr << "ftFileCreator::getMissingChunk(???," << chunk << ")";
std::cerr << std::endl;
#endif
return fileChunker->getMissingChunk(offset, chunk);
}
/*********************************************************** /* find the chunk */
* std::map<uint64_t, ftChunk>::iterator it;
* ftFileChunker methods it = mChunks.find(offset);
* if (it == mChunks.end())
***********************************************************/
ftFileChunker::ftFileChunker(uint64_t size): file_size(size), std_chunk_size(10000), monitorPeriod(30)
{
/*
* srand for randomized version - move it to the sub-class?
*/
srand ( time(NULL) );
aggregate_status = 0;
}
ftFileChunker::~ftFileChunker()
{
std::vector<ftChunk>::iterator it;
for(unsigned int i=0; i<allocationTable.size();i++) {
delete allocationTable.at(i); /* Does this need a check? */
}
if(!allocationTable.empty()){
allocationTable.clear();
}
}
int ftFileChunker::splitFile(){
RsStackMutex stack(chunkerMutex); /********** STACK LOCKED MTX ******/
/*
* all in bytes
*/
num_chunks = file_size/std_chunk_size;
/*
* FIXME - Remainder should go into last chunk
*/
uint64_t rem = file_size % std_chunk_size;
unsigned int index=0;
uint64_t max_chunk_size = file_size - (index * std_chunk_size);
#ifdef FILE_DEBUG
std::cerr << "ftFileChunker::splitFile()";
std::cerr << std::endl;
std::cerr << "\tfile_size: " << file_size;
std::cerr << std::endl;
std::cerr << "\tstd_chunk_size: " << std_chunk_size;
std::cerr << std::endl;
std::cerr << "\tnum_chunks: " << num_chunks;
std::cerr << std::endl;
std::cerr << "\trem: " << rem;
std::cerr << std::endl;
std::cerr << "\tmax_chunk_size: " << max_chunk_size;
std::cerr << std::endl;
#endif
time_t now = time(NULL);
for(index=0;index<num_chunks;index++)
{ {
uint64_t offset = index * std_chunk_size; return 0; /* ignoring */
max_chunk_size = file_size - (index * std_chunk_size);
ftChunk *f = new ftChunk(offset,max_chunk_size,now, ftChunk::AVAIL);
allocationTable.push_back(f);
} }
if (rem != 0) ftChunk chunk = it->second;
mChunks.erase(it);
if (chunk.chunk != chunk_size)
{ {
ftChunk *f = new ftChunk(file_size-rem,rem,now, ftChunk::AVAIL); /* partial : shrink chunk */
allocationTable.push_back(f); chunk.chunk -= chunk_size;
num_chunks++; chunk.offset += chunk_size;
mChunks[chunk.offset] = chunk;
} }
/* /* if we've cleaned up chunks, must update counters */
* DEBUGGER it = mChunks.find(chunk.offset);
* for(int j=0;j<allocationTable.size();j++) if (it == mChunks.end())
* { {
* std::cout << "SIZE " << allocationTable.at(j)->max_chunk_size << " " << allocationTable.at(j)->chunk_status << std::endl; mStart = mEnd;
* } }
*/ else if (it == mChunks.begin())
{
mStart += it->second.offset;
}
return 1; return 1;
} }
/* bool ftFileCreator::getMissingChunk(uint64_t &offset, uint32_t &chunk)
* This method sets the offset, chunk may be reset if needed
*/
bool ftFileChunker::getMissingChunk(uint64_t &offset, uint32_t &chunk)
{ {
RsStackMutex stack(chunkerMutex); /********** STACK LOCKED MTX ******/ RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
std::cerr << "ffc::getMissingChunk(...,"<< chunk << ")"<< std::endl;
unsigned int i =0; /* check start point */
bool found = false;
int chunks_after = 0;
int chunks_rem = 0;
#ifdef FILE_DEBUG if (mStart == mSize)
std::cerr << "ftFileChunker::getMissingChunk(???," << chunk << ")"; {
std::cerr << "ffc::getMissingChunk() File Done";
std::cerr << std::endl;
return false;
}
/* check for freed chunks */
time_t ts = time(NULL);
time_t old = ts-CHUNK_MAX_AGE;
std::map<uint64_t, ftChunk>::iterator it;
for(it = mChunks.begin(); it != mChunks.end(); it++)
{
/* very simple algorithm */
if (it->second.ts < old)
{
std::cerr << "ffc::getMissingChunk() ReAlloc";
std::cerr << std::endl;
/* retry this one */
it->second.ts = ts;
chunk = it->second.chunk;
offset = it->second.offset;
return true;
}
}
std::cerr << "ffc::getMissingChunk() new Alloc";
std::cerr << " mStart: " << mStart << " mEnd: " << mEnd;
std::cerr << "mSize: " << mSize;
std::cerr << std::endl; std::cerr << std::endl;
#endif
/* /* else allocate a new chunk */
* This signals file completion if (mSize - mEnd < chunk)
* FIXME Does it need to be more explicit chunk = mSize - mEnd;
*/
if(aggregate_status == num_chunks * ftChunk::RECEIVED) offset = mEnd;
{ mEnd += chunk;
#ifdef FILE_DEBUG
std::cerr << "completed ??";
std::cerr << std::endl;
#endif
return found;
}
mChunks[offset] = ftChunk(offset, chunk, ts);
while(i<allocationTable.size()) return true;
{
#ifdef FILE_DEBUG
std::cerr << "ftFileChunker checking allocTable(" << i << ")";
std::cerr << " of " << allocationTable.size();
std::cerr << std::endl;
#endif
if(allocationTable.at(i)->max_chunk_size >=chunk)
{
#ifdef FILE_DEBUG
std::cerr << "ftFileChunker pre alloc(" << i << ")";
std::cerr << std::endl;
std::cerr << "\toffset: " << allocationTable.at(i)->offset;
std::cerr << std::endl;
std::cerr << "\tmax_chunk: " << allocationTable.at(i)->max_chunk_size;
std::cerr << std::endl;
std::cerr << "\treq_chunk: " << chunk;
std::cerr << std::endl;
#endif
offset = allocationTable.at(i)->offset;
chunks_after = chunk/std_chunk_size; //10KB
/*
* FIXME Handling remaining chunk < 10KB
*/
//if (chunk <
chunks_rem = chunk % std_chunk_size;
chunk -= chunks_rem;
/*std::cout << "Found " << chunk << " at " << i << " "<< chunks_after << std::endl;*/
allocationTable.at(i)->max_chunk_size=0;
allocationTable.at(i)->timestamp = time(NULL);
allocationTable.at(i)->chunk_status = ftChunk::ALLOCATED;
#ifdef FILE_DEBUG
std::cerr << "ftFileChunker postalloc(" << i << ")";
std::cerr << std::endl;
std::cerr << "\tchunks_after: " << chunks_after;
std::cerr << std::endl;
std::cerr << "\tchunks_rem: " << chunks_after;
std::cerr << std::endl;
std::cerr << "\tchunk: " << chunks_after;
std::cerr << std::endl;
#endif
found = true;
break;
}
i++;
}
/* if we get here, there is no available chunk bigger
* than requested ...
* NB: Request size should be a larger than std_chunk_size.
* So Edge (sub chunk allocation) condition is handled here.
*
* Find largest remaining chunk.
*/
if (!found)
{
#ifdef FILE_DEBUG
std::cerr << "ftFileChunker::getMissingChuck() FULL CHUNK not found";
std::cerr << std::endl;
#endif
i=0;
uint64_t max = allocationTable.at(i)->max_chunk_size;
uint64_t size = max;
int maxi = -1;
if (max > 0)
{
maxi = 0;
}
while(i<allocationTable.size())
{
#ifdef FILE_DEBUG
std::cerr << "Checking(" << i << ") " <<
allocationTable.at(i)->max_chunk_size;
std::cerr << std::endl;
#endif
size = allocationTable.at(i)->max_chunk_size;
if(size > max)
{
max = allocationTable.at(i)->max_chunk_size;
maxi = i;
}
i++;
}
if (maxi > -1) //maxi or max
{
#ifdef FILE_DEBUG
std::cerr << "Biggest Avail Chunk: " << max;
std::cerr << std::endl;
#endif
offset = allocationTable.at(maxi)->offset;
chunk = allocationTable.at(maxi)->max_chunk_size;
chunks_after = chunk/std_chunk_size; //10KB
/* Handle end condition ...
* max_chunk_size < std_chunk_size
* Trim if not end condition.
*/
if (chunks_after > 0)
{
chunks_rem = chunk % std_chunk_size;
chunk -= chunks_rem;
}
else
{
/* end condition */
chunks_after = 1;
}
allocationTable.at(maxi)->max_chunk_size=0;
allocationTable.at(maxi)->timestamp = time(NULL);
allocationTable.at(maxi)->chunk_status = ftChunk::ALLOCATED;
found = true;
i = maxi;
}
} //if not found
if (found)
{
// i represents index to chunk(s) we will use.
// chunks_after is the count of how many we will use.
std::cout << "Chunks remaining " << chunks_rem << std::endl;
/*
* update all previous chunks max available size
* Expensive? Can it be smarter FIXME
*/
/* drbob: Think this is wrong?
* disabling...
*
for(unsigned int j=0;j<i;j++)
{
if (allocationTable.at(j)->max_chunk_size >0)
allocationTable.at(j)->max_chunk_size -= chunk;
}
*
*/
for(unsigned int j=i;j<i+chunks_after;j++)
{
allocationTable.at(j)->max_chunk_size = 0;
allocationTable.at(j)->chunk_status = ftChunk::ALLOCATED;
}
// DEBUGGER - Uncomment
for(unsigned int j=0;j<allocationTable.size();j++)
{
std::cout << "After allocation " << allocationTable.at(j)->max_chunk_size << " " << allocationTable.at(j)->chunk_status << std::endl;
}
}
else
{
#ifdef FILE_DEBUG
std::cerr << "ftFileChunker::getMissingChuck() not found";
std::cerr << std::endl;
#endif
}
return found;
} }
/*
* This should run on a separate thread when ftFileChunker is initialized
* FIXME Implemet DrBob's suggestion of request-fired check instead of dedicated
* thread
*/
int ftFileChunker::monitor()
{
int reset = 0;
uint32_t prev_size = 0;
uint32_t size = 0;
std::cout<<"Running monitor.."<<std::endl;
RsStackMutex stack(chunkerMutex); /********** STACK LOCKED MTX ******/
for(unsigned int j=allocationTable.size()-1 ;j>= 0;)
{
if((allocationTable.at(j)->chunk_status == ftChunk::ALLOCATED) &&
(allocationTable.at(j)->timestamp - time(NULL) > 30))
{
allocationTable.at(j)->chunk_status = ftChunk::AVAIL;
if (j == allocationTable.size()-1)
{
/* at end */
prev_size = 0;
size = file_size % std_chunk_size;
if (size == 0)
{
size = std_chunk_size;
}
}
else
{
prev_size = allocationTable.at(j+1)->max_chunk_size;
size = std_chunk_size;
}
allocationTable.at(j)->max_chunk_size = size + prev_size;
prev_size = allocationTable.at(j)->max_chunk_size;
for(j--; j >= 0; j--)
{
if (allocationTable.at(j)->chunk_status != ftChunk::AVAIL)
break;
allocationTable.at(j)->max_chunk_size += prev_size;
prev_size = allocationTable.at(j)->max_chunk_size;
reset++;
}
}
else
{
j--;
}
}
return reset;
}
void ftFileChunker::setMonitorPeriod(int period)
{
monitorPeriod = period;
}
void ftFileChunker::run()
{
while(1)
{
for(int i = 0; i < monitorPeriod; i++)
{
/******************** WINDOWS/UNIX SPECIFIC PART ******************/
#ifndef WINDOWS_SYS
sleep(1);
#else
Sleep(1000);
#endif
/******************* WINDOWS/UNIX SPECIFIC PART ******************/
}
monitor();
}
}
int ftFileChunker::notifyReceived(uint64_t offset, uint32_t chunk_size)
{
RsStackMutex stack(chunkerMutex); /********** STACK LOCKED MTX ******/
int index = offset / std_chunk_size;
/* should receive a multiple of chunk_size.... */
uint32_t chunks = chunk_size / std_chunk_size;
uint32_t rem_chunk = chunk_size % std_chunk_size;
#ifdef FILE_DEBUG
std::cerr << "ftFileChunkerr::notifyReceived(";
std::cerr << offset;
std::cerr << ", " << chunk_size << ")";
std::cerr << std::endl;
std::cerr << "\t# complete chunks: " << chunks;
std::cerr << std::endl;
std::cerr << "\trem_chunk: " << rem_chunk;
std::cerr << std::endl;
#endif
if(allocationTable.at(index)->chunk_status == ftChunk::ALLOCATED){
allocationTable.at(index)->chunk_status = ftChunk::RECEIVED;
aggregate_status += ftChunk::RECEIVED;
#ifdef FILE_DEBUG
std::cerr << "ftFileChunkerr::notifyReceived()";
std::cerr << " flagged as RECVD";
std::cerr << std::endl;
#endif
}
return aggregate_status;
}
/***********************************************************
*
* ftFileRandomizeChunker methods
*
***********************************************************/
ftFileRandomizeChunker::ftFileRandomizeChunker(uint64_t size):
ftFileChunker(size)
{
}
ftFileRandomizeChunker::~ftFileRandomizeChunker()
{
}
bool ftFileRandomizeChunker::getMissingChunk(uint64_t &offset, uint32_t &chunk) {
RsStackMutex stack(chunkerMutex); /********** STACK LOCKED MTX ******/
std::cerr << "Calling getMissingChunk with chunk="<< chunk << std::endl;
unsigned int i =0;
bool found = false;
int chunks_after = 0;
int chunks_rem = 0;
if(aggregate_status == num_chunks * ftChunk::RECEIVED)
return found;
std::vector<int> randomIndex;
while(i<allocationTable.size())
{
if(allocationTable.at(i)->max_chunk_size >=chunk){
randomIndex.push_back(i);
}
i++;
}
/*
* FIXME test sufficiently to make sure its picking every index
*/
if (randomIndex.size()>0)
{
int rnum = rand() % randomIndex.size();
i = randomIndex.at(rnum);
std::cout << "i=" <<i << " rnum " << rnum << std::endl;
offset = allocationTable.at(i)->offset;
chunks_after = chunk/std_chunk_size; //10KB
chunks_rem = chunk % std_chunk_size;
chunk -= chunks_rem;
std::cout << "Found " << chunk << " at index =" << i << " "<< chunks_after << std::endl;
allocationTable.at(i)->max_chunk_size=0;
allocationTable.at(i)->timestamp = time(NULL);
allocationTable.at(i)->chunk_status = ftChunk::ALLOCATED;
found = true;
}
if (!found)
{
i=0;
uint64_t min = allocationTable.at(i)->max_chunk_size - chunk;
uint64_t diff = min;
int mini = -1;
while(i<allocationTable.size())
{
diff = allocationTable.at(i)->max_chunk_size-chunk;
if(diff <= min && diff >0)
{
min = allocationTable.at(i)->max_chunk_size - chunk;
mini = i;
}
i++;
}
if (mini > -1)
{
offset = allocationTable.at(mini)->offset;
chunk = allocationTable.at(mini)->max_chunk_size;
chunks_after = chunk/std_chunk_size; //10KB
chunks_rem = chunk % std_chunk_size;
chunk -= chunks_rem;
allocationTable.at(mini)->max_chunk_size=0;
allocationTable.at(mini)->timestamp = time(NULL);
allocationTable.at(mini)->chunk_status = ftChunk::ALLOCATED;
found = true;
}
} //if not found
if (found)
{
// update all previous chunks max available size
for(unsigned int j=0;j<i;j++)
{
if (allocationTable.at(j)->max_chunk_size >0)
allocationTable.at(j)->max_chunk_size -= chunk;
}
for(unsigned int j=i;j<i+chunks_after;j++)
{
allocationTable.at(j)->max_chunk_size = 0;
allocationTable.at(j)->chunk_status = ftChunk::ALLOCATED;
}
/* for(int j=0;j<allocationTable.size();j++){
std::cout << "After allocation " << j << " " << allocationTable.at(j)->max_chunk_size << " " << allocationTable.at(j)->chunk_status << std::endl;
}*/
}
return found;
}
/*********************************************************** /***********************************************************
* *
* ftChunk methods * ftChunk methods
* *
***********************************************************/ ***********************************************************/
ftChunk::ftChunk(uint64_t offset,uint64_t chunk_size,time_t time, ftChunk::Status s) : offset(offset), max_chunk_size(chunk_size), timestamp(time), chunk_status(s) ftChunk::ftChunk(uint64_t ioffset,uint64_t size,time_t now)
: offset(ioffset), chunk(size), ts(now)
{ {
} }
@ -723,3 +291,4 @@ ftChunk::~ftChunk()
{ {
} }

View File

@ -33,100 +33,56 @@
* *
*/ */
#include "ftfileprovider.h" #include "ftfileprovider.h"
#include "util/rsthreads.h" #include <map>
#include <queue>
class ftChunk; class ftChunk;
class ftFileChunker;
class ftFileCreator: public ftFileProvider class ftFileCreator: public ftFileProvider
{ {
public: public:
ftFileCreator(std::string savepath, uint64_t size, std::string hash,std::string chunker); ftFileCreator(std::string savepath, uint64_t size, std::string hash, uint64_t recvd);
~ftFileCreator(); ~ftFileCreator();
/* /* overloaded from FileProvider */
* overloaded from FileProvider FIXME virtual bool getFileData(uint64_t offset, uint32_t chunk_size, void *data);
* virtual bool getFileData(uint64_t offset, uint32_t chunk_size, void *data); uint64_t getRecvd();
*
*/
/*
* FIXME: initializeFileAttrs
* Should this be a over-ridden version of ftfile provider?
* What happens in the case of simultaneous upload and download?
*/
int initializeFileAttrs();
/* /*
* creation functions for FileCreator * creation functions for FileCreator
*/ */
bool getMissingChunk(uint64_t &offset, uint32_t &chunk); bool getMissingChunk(uint64_t &offset, uint32_t &chunk);
bool addFileData(uint64_t offset, uint32_t chunk_size, void *data); bool addFileData(uint64_t offset, uint32_t chunk_size, void *data);
protected:
virtual int initializeFileAttrs();
private: private:
int notifyReceived(uint64_t offset, uint32_t chunk_size);
/* /*
* structure to track missing chunks * structure to track missing chunks
*/ */
uint64_t recv_size; uint64_t mStart;
std::string hash; uint64_t mEnd;
ftFileChunker *fileChunker;
void initialize(std::string);
RsMutex ftcMutex;
};
/* std::map<uint64_t, ftChunk> mChunks;
This class can either be specialized to follow a different splitting
policy or have an argument to indicate different policies
*/
class ftFileChunker : public RsThread {
public:
/*
* FIXME Does filechunker require hash??
*/
ftFileChunker(uint64_t size);
virtual ~ftFileChunker();
/*
* Breaks up the file into evenly sized chunks
* Initializes all chunks to never_requested
*/
int splitFile();
virtual void run();
virtual bool getMissingChunk(uint64_t &offset, uint32_t &chunk);
bool getMissingChunkRandom(uint64_t &offset, uint32_t &chunk);
int notifyReceived(uint64_t offset, uint32_t chunk_size);
int monitor();
void setMonitorPeriod(int period);
protected:
uint64_t file_size;
uint64_t num_chunks;
uint64_t std_chunk_size;
std::vector<ftChunk*> allocationTable;
unsigned int aggregate_status;
int monitorPeriod;
RsMutex chunkerMutex; /********** STACK LOCKED MTX ******/
};
class ftFileRandomizeChunker : public ftFileChunker {
public:
ftFileRandomizeChunker(uint64_t size);
virtual bool getMissingChunk(uint64_t &offset, uint32_t &chunk);
~ftFileRandomizeChunker();
}; };
class ftChunk { class ftChunk {
public: public:
enum Status {AVAIL, ALLOCATED, RECEIVED}; ftChunk(uint64_t,uint64_t,time_t);
ftChunk(uint64_t,uint64_t,time_t,Status); ftChunk():offset(0), chunk(0), ts(0) {}
uint64_t offset;
uint64_t max_chunk_size;
time_t timestamp;
Status chunk_status;
~ftChunk(); ~ftChunk();
uint64_t offset;
uint64_t chunk;
time_t ts;
}; };
#endif // FT_FILE_PROVIDER_HEADER #endif // FT_FILE_CREATOR_HEADER

View File

@ -1,87 +1,89 @@
#include "ftfilecreator.h" #include "ftfilecreator.h"
main(){ #include "util/utest.h"
uint64_t offset;
uint32_t csize;
/*Test file creator*/
ftFileCreator fcreator("somefile",100000,"hash","default");
csize = 40000;
if (fcreator.getMissingChunk(offset, csize)){
std::cout << "Missing Chunk's offset=" << offset << " chunk_size=" << csize << std::endl;
}
csize = 10000; INITTEST();
if (fcreator.getMissingChunk(offset, csize)){
std::cout << "Missing Chunk's offset=" << offset << " chunk_size=" << csize << std::endl;
}
csize = 15000; static int test_timeout(ftFileCreator *creator);
if (fcreator.getMissingChunk(offset, csize)){ static int test_fill(ftFileCreator *creator);
std::cout << "Missing Chunk's offset=" << offset << " chunk_size=" << csize << std::endl;
}
int main()
{
/* use ftcreator to create a file on tmp drive */
ftFileCreator fcreator("/tmp/rs-ftfc-test.dta",100000,"hash",0);
std::cout << "Test file creator adding data to file out-of-order\n"; test_timeout(&fcreator);
char* alpha = "abcdefghij"; test_fill(&fcreator);
std::cerr << "Call to addFileData =" << fcreator.addFileData(10,10,alpha );
char* numerical = "1234567890";
std::cerr << "Call to addFileData =" << fcreator.addFileData(0,10,numerical );
FINALREPORT("RsTlvItem Stack Tests");
/* Test file creator can write out of order/randomized chunker */ return TESTRESULT();
ftFileCreator franc("somefile1",50000,"hash", "randomize");
csize = 30000;
if (franc.getMissingChunk(offset, csize)){
std::cout << "Offset " << offset << " Csize " << csize << std::endl;
}
char* allA = (char*) malloc(csize);
for(int i=0;i<csize;i++)
allA[i]='a';
std::cerr << "ADD DATA RETUNED " << franc.addFileData(offset,csize,allA );
csize = 10000;
if (franc.getMissingChunk(offset, csize)){
std::cout << "Offset " << offset << " Csize " << csize << std::endl;
}
char* allB = (char*) malloc(csize);
for(int i=0;i<csize;i++)
allB[i]='b';
std::cerr << "ADD DATA RETUNED " << franc.addFileData(offset,csize,allB );
if (franc.getMissingChunk(offset, csize)){
std::cout << "Offset " << offset << " Csize " << csize << std::endl;
}
else {
std::cout << "getMissing chunk returned nothing" << std::endl;
}
csize = 10000;
if (franc.getMissingChunk(offset, csize)){
std::cout << "Offset " << offset << " Csize " << csize << std::endl;
}
char* allC = (char*) malloc(csize);
for(int i=0;i<csize;i++)
allC[i]='c';
std::cerr << "ADD DATA RETUNED " << franc.addFileData(offset,csize,allC );
if (franc.getMissingChunk(offset, csize)){
std::cout << "Offset " << offset << " Csize " << csize << std::endl;
}
else {
std::cout << "getMissing chunk returned nothing" << std::endl;
}
free(allA);
free(allB);
} }
int test_timeout(ftFileCreator *creator)
{
uint32_t chunk = 1000;
uint64_t offset = 0;
int max_timeout = 30;
int max_offset = chunk * max_timeout;
int i;
std::cerr << "60 second test of chunk queue.";
std::cerr << std::endl;
for(i = 0; i < max_timeout; i++)
{
creator->getMissingChunk(offset, chunk);
std::cerr << "Allocated Offset: " << offset << " chunk: " << chunk << std::endl;
CHECK(offset < max_offset);
sleep(1);
}
std::cerr << "Expect Repeats now";
std::cerr << std::endl;
for(i = 0; i < max_timeout; i++)
{
creator->getMissingChunk(offset, chunk);
std::cerr << "Allocated Offset: " << offset << " chunk: " << chunk << std::endl;
CHECK(offset < max_offset);
sleep(1);
}
REPORT("Chunk Queue");
return 1;
}
int test_fill(ftFileCreator *creator)
{
uint32_t chunk = 1000;
uint64_t offset = 0;
uint64_t init_size = creator->getFileSize();
uint64_t init_trans = creator->getRecvd();
std::cerr << "Initial FileSize: " << init_size << std::endl;
std::cerr << "Initial Transferred:" << init_trans << std::endl;
while(creator->getMissingChunk(offset, chunk))
{
/* give it too them */
void *data = malloc(chunk);
creator->addFileData(offset, chunk, data);
free(data);
}
uint64_t end_size = creator->getFileSize();
uint64_t end_trans = creator->getRecvd();
std::cerr << "End FileSize: " << end_size << std::endl;
std::cerr << "End Transferred:" << end_trans << std::endl;
CHECK(init_size == end_size);
CHECK(end_trans == end_size);
REPORT("Test Fill");
return 1;
}

View File

@ -3,8 +3,8 @@
#include "util/rsdir.h" #include "util/rsdir.h"
ftFileProvider::ftFileProvider(std::string path, uint64_t size, std::string ftFileProvider::ftFileProvider(std::string path, uint64_t size, std::string
hash) : total_size(size), hash(hash), file_name(path), fd(NULL) { hash) : mSize(size), hash(hash), file_name(path), fd(NULL)
{
} }
ftFileProvider::~ftFileProvider(){ ftFileProvider::~ftFileProvider(){
@ -13,20 +13,29 @@ ftFileProvider::~ftFileProvider(){
} }
} }
bool ftFileProvider::fileOk()
{
RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
return (fd != NULL);
}
std::string ftFileProvider::getHash() std::string ftFileProvider::getHash()
{ {
RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
return hash; return hash;
} }
uint64_t ftFileProvider::getFileSize() uint64_t ftFileProvider::getFileSize()
{ {
return total_size; RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
return mSize;
} }
bool ftFileProvider::FileDetails(FileInfo &info) bool ftFileProvider::FileDetails(FileInfo &info)
{ {
RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
info.hash = hash; info.hash = hash;
info.size = total_size; info.size = mSize;
info.path = file_name; info.path = file_name;
info.fname = RsDirUtil::getTopDir(file_name); info.fname = RsDirUtil::getTopDir(file_name);
@ -38,16 +47,14 @@ bool ftFileProvider::FileDetails(FileInfo &info)
bool ftFileProvider::getFileData(uint64_t offset, uint32_t chunk_size, void *data) bool ftFileProvider::getFileData(uint64_t offset, uint32_t chunk_size, void *data)
{ {
RsStackMutex stack(ftPMutex); /********** STACK LOCKED MTX ******/ /* dodgey checking outside of mutex...
if (fd==NULL) * much check again inside FileAttrs().
{ */
int init = initializeFileAttrs(); if (fd == NULL)
if (init ==0) if (!initializeFileAttrs())
{ return false;
std::cerr <<"Initialization Failed" << std::endl;
return 0; RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
}
}
/* /*
* Use private data, which has a pointer to file, total size etc * Use private data, which has a pointer to file, total size etc
@ -59,9 +66,9 @@ bool ftFileProvider::getFileData(uint64_t offset, uint32_t chunk_size, void *dat
int data_size = chunk_size; int data_size = chunk_size;
long base_loc = offset; long base_loc = offset;
if (base_loc + data_size > total_size) if (base_loc + data_size > mSize)
{ {
data_size = total_size - base_loc; data_size = mSize - base_loc;
std::cerr <<"Chunk Size greater than total file size, adjusting chunk size " << data_size << std::endl; std::cerr <<"Chunk Size greater than total file size, adjusting chunk size " << data_size << std::endl;
} }
@ -108,6 +115,14 @@ bool ftFileProvider::getFileData(uint64_t offset, uint32_t chunk_size, void *dat
int ftFileProvider::initializeFileAttrs() int ftFileProvider::initializeFileAttrs()
{ {
std::cerr << "ftFileProvider::initializeFileAttrs() Filename: ";
std::cerr << file_name;
RsStackMutex stack(ftcMutex); /********** STACK LOCKED MTX ******/
if (fd)
return 1;
/* /*
* check if the file exists * check if the file exists
*/ */
@ -130,7 +145,6 @@ int ftFileProvider::initializeFileAttrs()
} }
/* /*
* if it opened, find it's length * if it opened, find it's length
* move to the end * move to the end
@ -142,6 +156,9 @@ int ftFileProvider::initializeFileAttrs()
return 0; return 0;
} }
total_size = ftell(fd); uint64_t recvdsize = ftell(fd);
std::cerr << "ftFileProvider::initializeFileAttrs() File Expected Size: " << mSize << " RecvdSize: " << recvdsize << std::endl;
return 1; return 1;
} }

View File

@ -40,15 +40,18 @@ class ftFileProvider
public: public:
ftFileProvider(std::string path, uint64_t size, std::string hash); ftFileProvider(std::string path, uint64_t size, std::string hash);
virtual ~ftFileProvider(); virtual ~ftFileProvider();
virtual bool getFileData(uint64_t offset, uint32_t chunk_size, void *data); virtual bool getFileData(uint64_t offset, uint32_t chunk_size, void *data);
virtual bool FileDetails(FileInfo &info); virtual bool FileDetails(FileInfo &info);
std::string getHash(); std::string getHash();
uint64_t getFileSize(); uint64_t getFileSize();
bool fileOk();
protected: protected:
virtual int initializeFileAttrs(); virtual int initializeFileAttrs(); /* does for both */
uint64_t total_size;
uint64_t mSize;
std::string hash; std::string hash;
std::string file_name; std::string file_name;
FILE *fd; FILE *fd;
@ -65,7 +68,7 @@ protected:
/* /*
* Mutex Required for stuff below * Mutex Required for stuff below
*/ */
RsMutex ftPMutex; RsMutex ftcMutex;
}; };

View File

@ -1,33 +1,72 @@
#include "ftfileprovider.h" #include "ftfileprovider.h"
#include "ftfilecreator.h"
int main(){ #include "util/utest.h"
ftFileProvider fp("dummy.txt",1,"ABCDEF");
char data[2];
long offset = 0;
for (int i=0;i<10;i++) {
if (fp.getFileData(offset,2,&data)){
std::cout <<"Recv data " << data[0] << std::endl; INITTEST()
int main()
{
/* create a random file */
uint64_t size = 100000;
uint32_t max_chunk = 10000;
uint32_t chunk = 1000;
uint64_t offset = 0;
std::string filename = "/tmp/ft_test.dta";
std::string filename2 = "/tmp/ft_test.dta.dup";
/* use creator to make it */
void *data = malloc(max_chunk);
ftFileCreator *creator = new ftFileCreator(filename, size, "hash", 0);
for(offset = 0; offset != size; offset += chunk)
{
if (!creator->addFileData(offset, chunk, data))
{
FAILED("Create Test Data File");
std::cerr << "Failed to add data (CREATE)";
std::cerr << std::endl;
} }
else {
std::cout <<"Recv no data." << std::endl;
}
offset+=2;
} }
delete creator;
std::cerr << "Created file: " << filename << " of size: " << size;
std::cerr << std::endl;
ftFileProvider fp1("dummy1.txt",3,"ABCDEF"); /* load it with file provider */
char data1[3]; creator = new ftFileCreator(filename2, size, "hash", 0);
offset = 0; ftFileProvider *provider = new ftFileProvider(filename, size, "hash");
for (int i=0;i<10;i++) {
if (fp1.getFileData(offset,2,&data1)){ /* create duplicate with file creator */
std::cout <<"Recv data " << data1[0] << std::endl;
while(creator->getMissingChunk(offset, chunk))
{
if (!provider->getFileData(offset, chunk, data))
{
FAILED("Read from Test Data File");
std::cerr << "Failed to get data";
std::cerr << std::endl;
} }
else {
std::cout <<"Revc no data" << std::endl; if (!creator->addFileData(offset, chunk, data))
{
FAILED("Write to Duplicate");
std::cerr << "Failed to add data";
std::cerr << std::endl;
} }
offset+=2;
std::cerr << "Transferred: " << chunk << " @ " << offset;
std::cerr << std::endl;
/* reset chunk size */
chunk = (uint64_t) max_chunk * (rand() / (1.0 + RAND_MAX));
std::cerr << "ChunkSize = " << chunk << std::endl;
} }
return 1; return 1;
} }

View File

@ -140,16 +140,35 @@ bool ftTransferModule::recvFileData(std::string peerId, uint64_t offset,
mit = mFileSources.find(peerId); mit = mFileSources.find(peerId);
if (mit == mFileSources.end()) if (mit == mFileSources.end())
return false; {
#ifdef FT_DEBUG
std::cerr << "ftTransferModule::recvFileData()";
std::cerr << " peer not found in sources";
std::cerr << std::endl;
#endif
return false;
}
if ((mit->second).state != PQIPEER_DOWNLOADING) if ((mit->second).state != PQIPEER_DOWNLOADING)
return false; {
#ifdef FT_DEBUG
std::cerr << "ftTransferModule::recvFileData()";
std::cerr << " peer not downloading???";
std::cerr << std::endl;
#endif
//return false;
}
if (offset != ((mit->second).offset + (mit->second).receivedSize)) if (offset != ((mit->second).offset + (mit->second).receivedSize))
{ {
//fix me //fix me
//received data not expected //received data not expected
return false; #ifdef FT_DEBUG
std::cerr << "ftTransferModule::recvFileData()";
std::cerr << " offset != offset + recvdSize";
std::cerr << std::endl;
#endif
return false;
} }
(mit->second).receivedSize += chunk_size; (mit->second).receivedSize += chunk_size;

View File

@ -0,0 +1,180 @@
/*
* libretroshare/src/ft: fttransfermoduletest.cc
*
* File Transfer for RetroShare.
*
* Copyright 2008 by Robert Fernie.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License Version 2 as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
* Please report all bugs and problems to "retroshare@lunamutt.com".
*
*/
/*
* Test for Multiplexor.....
* As this is a key middle component, it is hard to test without other bits.
* It relies on ftFileProvider/ftFileCreator/ftTransferModule...
*
* And has dummy ftDataSend and ftSearch.
*
*/
#ifdef WIN32
#include "util/rswin.h"
#endif
#include "ft/ftextralist.h"
#include "ft/ftdatamultiplex.h"
#include "ft/ftfilesearch.h"
#include "ftfileprovider.h"
#include "ftfilecreator.h"
#include "ftcontroller.h"
#include "fttransfermodule.h"
#include "util/utest.h"
INITTEST()
void do_random_server_test(ftDataMultiplex *mplex, ftExtraList *eList, std::list<std::string> &files);
void usage(char *name)
{
std::cerr << "Usage: " << name << " [-p <period>] [-d <dperiod>] <path> [<path2> ... ] ";
std::cerr << std::endl;
}
int main(int argc, char **argv)
{
int c;
uint32_t period = 1;
uint32_t dPeriod = 600; /* default 10 minutes */
std::list<std::string> fileList;
while(-1 != (c = getopt(argc, argv, "d:p:")))
{
switch (c)
{
case 'p':
period = atoi(optarg);
break;
case 'd':
dPeriod = atoi(optarg);
break;
default:
usage(argv[0]);
break;
}
}
if (optind >= argc)
{
std::cerr << "Missing Files" << std::endl;
usage(argv[0]);
}
for(; optind < argc; optind++)
{
std::cerr << "Adding: " << argv[optind] << std::endl;
fileList.push_back(std::string(argv[optind]));
}
ftExtraList *eList = new ftExtraList();
eList->start();
ftSearch *ftsd = new ftSearchDummy();
ftFileSearch *ftfs = new ftFileSearch();
ftfs-> addSearchMode(ftsd, RS_FILE_HINTS_LOCAL);
ftfs-> addSearchMode(eList, RS_FILE_HINTS_EXTRA);
ftDataSendPair *ftds1 = new ftDataSendPair(NULL);
ftDataSendPair *ftds2 = new ftDataSendPair(NULL);
/* setup Actual Test bit */
ftDataMultiplex *ftmplex1 = new ftDataMultiplex("ownId", ftds2, ftfs);
ftDataMultiplex *ftmplex2 = new ftDataMultiplex("ownId", ftds1, ftfs);
ftds1->mDataRecv = ftmplex1;
ftds2->mDataRecv = ftmplex2;
ftmplex1->start();
ftmplex2->start();
/* Setup Search with some valid results */
/* Request Data */
/* now work the thread */
std::list<std::string>::iterator it;
uint32_t flags = 0;
for(it = fileList.begin(); it != fileList.end(); it++)
{
eList->hashExtraFile(*it, dPeriod, flags);
}
if (fileList.size() < 1)
{
std::cerr << "come on, give us some files...";
std::cerr << std::endl;
return 0;
}
/* now request files from ftDataMultiplex
* by adding in a ftTransferModule!
*/
std::string filename = *(fileList.begin());
/* wait for file to hash */
FileInfo info;
while(!eList->hashExtraFileDone(filename, info))
{
std::cerr << "Waiting for file to hash";
std::cerr << std::endl;
sleep(1);
}
std::string savename = "/tmp/" + info.fname;
ftFileCreator *creator = new ftFileCreator(savename, info.size, info.hash, 0);
ftController *controller = NULL;
ftTransferModule *transfer = new ftTransferModule(creator, ftmplex1, controller);
ftmplex1->addTransferModule(transfer, creator);
std::list<std::string> peerIds;
peerIds.push_back("ownId2");
transfer->setFileSources(peerIds);
transfer->setPeerState("ownId2", PQIPEER_IDLE, 1000);
//transfer->resumeTransfer();
/* check file progress */
while(1)
{
std::cerr << "File Transfer Status";
std::cerr << std::endl;
std::cerr << "Transfered: " << creator->getRecvd();
std::cerr << std::endl;
transfer->tick();
sleep(1);
}
}