]> git.treefish.org Git - phys/latlib.git/blob - configcache.cpp
Opening cachefiles exclusively with O_EXCL.
[phys/latlib.git] / configcache.cpp
1 #include "configcache.h"
2
3 #include <stdlib.h>
4 #include <iostream>
5 #include <time.h>
6 #include <dirent.h>
7 #include <errno.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10
11 #include <boost/iostreams/filtering_streambuf.hpp>
12 #include <boost/iostreams/stream.hpp>
13 #include <boost/iostreams/filter/bzip2.hpp>
14 #include <boost/iostreams/device/array.hpp>
15 #include <boost/iostreams/copy.hpp>
16
17 #define HEADER_READOK   0
18 #define HEADER_READERR  1
19 #define HEADER_READLAST 2
20
21 struct configcache::iobuffers
22 {
23   boost::iostreams::filtering_istreambuf *in;
24   boost::iostreams::filtering_ostreambuf *out;
25 };
26
27 configcache::configcache(const string& cacheid, const int& nequi, const int& nskip, const string& datadir, char **configmem, const int& configMemSize, const int& cachemode,
28                          ostream *_log){
29   log = _log;
30
31   NEQUI = nequi;
32   NSKIP = nskip;
33   DATADIR = datadir;
34   CACHEID = cacheid;
35
36   if ( cacheid.find("_") != -1 ) {
37     if(log) *log << "CCACHE: Invalid cacheid \"" << cacheid << "\" given. Cacheids must not contain underscores!" << endl << flush;
38     exit(1);
39   }
40
41   configMem = (char*)malloc(configMemSize);
42   tmpConfig = (char*)malloc(configMemSize);
43
44   *configmem = configMem;
45   configSize = configMemSize;
46
47   ioBuffers = new iobuffers;
48   ioBuffers->in = NULL;
49   ioBuffers->out = NULL;
50
51   MODE = cachemode;
52
53   refetchDataFiles = false;
54 }
55
56 string configcache::getFileId(int actnequi, const bool& shortid)
57 {
58   stringstream fileid;
59
60   if(!shortid) fileid << CACHEID << "_" << actnequi << "_" << NSKIP;
61   for(int ipara=0; ipara<Paras.size(); ipara++)
62     fileid << "_" << Paras[ipara].id << Paras[ipara].val;
63
64   return fileid.str();
65 }
66
67 void configcache::fetchDataFiles()
68 {
69   struct dirent *de=NULL;
70   DIR *d=NULL;
71   static infiledesc filedesc;
72   
73   d=opendir(DATADIR.c_str());
74   if(d != NULL){
75     while(de = readdir(d)){
76       string filename = de->d_name;
77       if(isValidInFile(filename, &filedesc)) 
78         {
79           inFiles.push_back(filedesc);
80         }
81     }
82   }
83 }
84
85 bool configcache::isValidInFile(const string& infile, infiledesc *filedesc)
86 {
87   char *inchar, *inParts;
88   string truncIn, truncOut;
89
90   filedesc->filename = infile;
91
92   if( infile.size() < 4 ) return false;
93
94   if( infile.substr(infile.size()-4) == ".dat" )
95     filedesc->extended = false;
96   else if( infile.substr(infile.size()-4) == "edat" )
97     filedesc->extended = true;
98   else
99     return false;
100
101   inchar = new char [infile.size()+1];
102   strcpy (inchar, infile.c_str());
103
104   inParts = strtok( inchar, "_" );
105   for(int iPart=0; inParts!=NULL; iPart++)
106     {
107       if( iPart>3 ) { truncIn += "_"; truncIn += inParts; }
108
109       switch(iPart)
110         {
111         case 1: if(inParts != CACHEID)
112             return false;
113           break;
114         case 2:
115           filedesc->nequi = atoi(inParts);
116           break;
117         case 3: 
118           if(atoi(inParts) != NSKIP) 
119             return false;
120           filedesc->nskip = atoi(inParts);
121           break;
122         }
123       inParts = strtok( NULL, "_");
124     }
125   truncIn = truncIn.substr(0, truncIn.size()-4);
126
127   delete[] inchar;
128
129   if( truncIn.find( getFileId(NEQUI, true) + "_" ) == string::npos ) return false;
130
131   return true;
132 }
133
134 int configcache::readHeader()
135 {
136   long unsigned int headersize;
137   
138   if( readDataToMem((char *)&headersize, sizeof(long unsigned int)) == sizeof(long unsigned int) && inFile.is_open() ) {
139     if ( headersize == 0 )
140       return HEADER_READLAST;
141
142     pair<unsigned long, void *> newHeader;
143
144     if( readDataToMem((char *)&newHeader.first, sizeof(unsigned long)) == sizeof(unsigned long) && inFile.is_open() ) {
145       newHeader.second = malloc(headersize);
146
147       if( readDataToMem((char *)newHeader.second, headersize) == headersize && inFile.is_open() ) {
148         headerStore.push_back(newHeader);
149         return HEADER_READOK;
150       }
151       else {
152         if(log) *log << "CCACHE: Could not read heade-data! Closing dat-file: " << openFileDesc.filename << endl << flush;
153         inFile.close();
154         return HEADER_READERR;
155       }
156     }
157     else {
158       if(log) *log << "CCACHE: Could not read headerid-hash! Closing dat-file: " << openFileDesc.filename << endl << flush;
159       inFile.close();
160       return HEADER_READERR;
161     }
162   }
163   else {
164     if(log) *log << "CCACHE: Could not read header size. Closing dat-file: " << openFileDesc.filename << endl << flush;
165     inFile.close();
166     return HEADER_READERR;
167   }
168 }
169
170 bool configcache::readAllHeaders()
171 {
172   int readHeaderStatus;
173
174   deleteHeaderStore();
175   
176   do {
177     readHeaderStatus = readHeader();
178   }
179   while ( readHeaderStatus == HEADER_READOK );
180
181   if ( readHeaderStatus == HEADER_READLAST ) return true;
182   else if ( readHeaderStatus == HEADER_READERR ) return false;
183 }
184
185 void * configcache::getHeader(const string& headerid) {
186   for (vector< pair<unsigned long, void *> >::iterator headerStoreIt = headerStore.begin(); headerStoreIt != headerStore.end(); ++headerStoreIt)
187     if ( headerStoreIt->first == hash(headerid) )
188       return headerStoreIt->second;
189   
190   return NULL;
191 }
192
193 void configcache::readConfig(bool *readnewconfig, int *nequileft, vector<unsigned long> *excludeFileHashes)
194 {
195   *readnewconfig = false;
196
197   if( DATADIR == "" || !(MODE==CACHE_MODE_RO||MODE==CACHE_MODE_RW) ) return;
198
199   if(refetchDataFiles){
200     refetchDataFiles = false;
201     fetchDataFiles();
202   }
203
204   while(true)
205     {
206       vector<infiledesc>::iterator inFileIt = getNextInfile(excludeFileHashes);
207       int iDidVirtualSkips;
208
209       if( (!inFile.is_open()) && inFileIt == inFiles.end() ) {
210         if (*readnewconfig)
211           *nequileft = nequileft_internal;
212         return;
213       }
214
215       while( (!inFile.is_open()) && inFiles.size() > 0 ) {
216         openFileDesc = *inFileIt;
217
218         if (openFileDesc.nequi < NEQUI)
219           doVirtualEquilibration = true;
220         else
221           doVirtualEquilibration = false;
222
223         firstUsedConfig = true;
224
225         if(log) *log << "CCACHE: Opening dat-file: " << inFileIt->filename << endl << flush;
226         inFile.open( (DATADIR + "/" + inFileIt->filename).c_str(), std::ios::binary );
227         
228         inFiles.erase(inFileIt);
229         
230         if( !inFile.is_open() ) continue;
231
232         ioBuffers->in = new boost::iostreams::filtering_istreambuf;
233         ioBuffers->in->push( boost::iostreams::bzip2_decompressor() );
234         ioBuffers->in->push(inFile);
235       }
236
237       if( inFile.is_open() ) 
238         {
239           if (doVirtualEquilibration) {
240             if(log) *log << "CCACHE: Trying virtual equilibration." << endl << flush;
241             doVirtualEquilibration = false;
242             for (iDidVirtualSkips=0; iDidVirtualSkips < (NEQUI-openFileDesc.nequi)/openFileDesc.nskip; iDidVirtualSkips++) {
243               if( readFullBlock(tmpConfig, configSize) != configSize || ! inFile.is_open() )
244                 break;
245               else if ( (NEQUI-openFileDesc.nequi) - (iDidVirtualSkips+1)*openFileDesc.nskip < nequileft_internal ) {
246                 memcpy(configMem, tmpConfig, configSize);
247                 nequileft_internal = NEQUI - openFileDesc.nequi - (iDidVirtualSkips+1)*openFileDesc.nskip;
248                 *readnewconfig = true;
249                 firstUsedConfig = false;
250               }
251             }
252           }
253
254           if( readFullBlock(tmpConfig, configSize) == configSize && inFile.is_open() )
255             {
256               memcpy(configMem, tmpConfig, configSize);
257               *readnewconfig = true;
258               if (firstUsedConfig) {
259                 firstUsedConfig = false;
260                 if (openFileDesc.nequi < NEQUI)
261                   nequileft_internal = NEQUI - openFileDesc.nequi - iDidVirtualSkips*openFileDesc.nskip;
262                 else
263                   nequileft_internal = NEQUI - openFileDesc.nequi;
264               }
265               nequileft_internal -= openFileDesc.nskip;
266               *nequileft = nequileft_internal;
267               return;
268             }
269           else {
270             if(log) *log << "CCACHE: Could not read configuration. Closing dat-file: " << openFileDesc.filename << endl << flush;
271             inFile.close();
272           }
273         }
274     }
275 }
276
277 void configcache::openOutFile(int actnequi)
278
279   time_t secstamp = time(NULL);
280   int iseq=0;
281   
282   while (true) {
283     outFileName.str("");
284     outFileName << DATADIR << "/" << secstamp << "." << iseq << "_" << getFileId(actnequi) << "_.edat.tmp";
285
286     int tmpfd = open(outFileName.str().c_str(), O_CREAT | O_EXCL, 0644);
287
288     if ( tmpfd != -1 ) {
289       close(tmpfd);
290       break;
291     }
292     else if ( errno != EEXIST ) {
293       if(log) *log << "CCACHE: Could not create cachefile!" << endl << flush;
294       exit(1);
295     }
296
297     iseq++;
298   }
299   
300   outFile.open( outFileName.str().c_str(), std::ios::binary );
301
302   ioBuffers->out = new boost::iostreams::filtering_ostreambuf;
303   ioBuffers->out->push(boost::iostreams::bzip2_compressor());
304   ioBuffers->out->push(outFile);
305 }
306
307 void configcache::writeHeader(const string& headerid, const char *header, long unsigned int size, int actnequi) {
308   unsigned long headeridhash;
309
310   if( DATADIR == "" || !(MODE==CACHE_MODE_WO||MODE==CACHE_MODE_RW) ) return;
311
312   if(!outFile.is_open())
313     openOutFile(actnequi);
314
315   headeridhash = hash(headerid);
316
317   boost::iostreams::write(*ioBuffers->out, (char*)&size, sizeof(long unsigned int));
318   boost::iostreams::write(*ioBuffers->out, (char*)&headeridhash, sizeof(unsigned long));
319   boost::iostreams::write(*ioBuffers->out, header, size);
320 }
321
322 void configcache::writeConfig(int actnequi)
323 {
324   long unsigned int zeroheader=0;
325
326   if ( DATADIR == "" || !(MODE==CACHE_MODE_WO||MODE==CACHE_MODE_RW) ) return;
327
328   if ( ! outFile.is_open() )
329     openOutFile(actnequi);
330   
331   boost::iostreams::write(*ioBuffers->out, (char*)&zeroheader, sizeof(long unsigned int));
332
333   boost::iostreams::write(*ioBuffers->out, configMem, configSize);
334 }
335
336 void configcache::addPara(const string& parid, const double& val){
337   parameter newPara;
338   newPara.id = parid;
339   newPara.val = val;
340   Paras.push_back(newPara);
341 }
342
343 int configcache::getParIndex(const string& parid){
344   for(int ipara=0; ipara<Paras.size(); ipara++)
345     if(Paras[ipara].id == parid) return ipara;
346 }
347
348 void configcache::setPara(const string& parid, const double& value){
349   Paras[getParIndex(parid)].val = value;
350
351   finishOutFile();
352   if(ioBuffers->in != NULL) { delete ioBuffers->in; ioBuffers->in=NULL; } 
353   inFile.close();
354   inFiles.clear();
355
356   refetchDataFiles = true;
357   nequileft_internal = NEQUI;
358 }
359
360 configcache::~configcache()
361 {
362   finishOutFile();
363   delete ioBuffers->in;
364   ioBuffers->in = NULL;
365 }
366
367 void configcache::finishOutFile()
368 {
369   if( ioBuffers->out != NULL )
370     {
371       delete ioBuffers->out;
372       ioBuffers->out = NULL;
373     }
374
375   if( outFile.is_open() )
376     {
377       outFile.close();
378       rename( outFileName.str().c_str(), outFileName.str().substr(0, outFileName.str().size()-4).c_str() );
379     }
380 }
381
382 int configcache::readFullBlock(char *tmpData, long unsigned int dataSize)
383 {
384   /* try to read header */
385   if ( openFileDesc.extended )
386     if ( ! readAllHeaders() ) 
387       return -1;
388
389   /* read data */
390   return readDataToMem(tmpData, dataSize);
391 }
392
393 int configcache::readDataToMem(char *tmpData, long unsigned int dataSize)
394 {
395   int readturn = -1;
396
397   if ( dataSize == 0 ) return 0;
398
399   try { readturn = boost::iostreams::read(*ioBuffers->in, tmpData, dataSize); }
400   catch(boost::iostreams::bzip2_error& error) { 
401     if(log) *log << "CCACHE: Caught bzip2 exception with error code: " << error.error() << endl << flush;
402     inFile.close();
403   } 
404   catch (std::exception const& ex) {
405     if(log) *log << "CCACHE: Caught exception: " << ex.what() << endl << flush;
406     inFile.close();
407   }
408   catch( ... ) {
409     if(log) *log << "CCACHE: Caught unknown exception while reading." << endl << flush;
410     inFile.close();
411   }
412
413   return readturn;
414 }
415
416 unsigned long configcache::hash(const string& str)
417 {
418   unsigned long hash = 5381;
419
420   for(string::const_iterator it=str.begin();it!=str.end();it++) 
421     hash = ((hash << 5) + hash) + *it; /* hash * 33 + character */
422
423   return hash;
424 }
425
426 void configcache::deleteHeaderStore()
427 {
428   while ( headerStore.size() > 0 ) {
429     free(headerStore.back().second);
430     headerStore.pop_back();
431   }
432 }
433
434 vector<infiledesc>::iterator configcache::getNextInfile(vector<unsigned long> *excludeFileHashes) {
435   for (vector<infiledesc>::iterator init = inFiles.begin(); init != inFiles.end(); ++init) {
436     if (excludeFileHashes != NULL) {
437       bool excludethisfile = false;
438
439       for (vector<unsigned long>::iterator exit = excludeFileHashes->begin(); exit != excludeFileHashes->end(); ++exit)
440         if ( *exit == hash(init->filename) ) {
441           excludethisfile = true;
442           break;
443         }
444
445       if (excludethisfile)
446         continue;
447     }
448     return init;
449   }
450   return inFiles.end();
451 }