libdap++  Updated for version 3.11.7
DAPCache3.cc
Go to the documentation of this file.
1 // DAPCache3.cc
2 
3 // This file was originally part of bes, A C++ back-end server
4 // implementation framework for the OPeNDAP Data Access Protocol.
5 // Copied to libdap. This is used to cache responses built from
6 // functional CE expressions.
7 
8 // Copyright (c) 2012 OPeNDAP, Inc
9 // Author: James Gallagher <jgallagher@opendap.org>
10 // Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
11 //
12 // This library is free software; you can redistribute it and/or
13 // modify it under the terms of the GNU Lesser General Public
14 // License as published by the Free Software Foundation; either
15 // version 2.1 of the License, or (at your option) any later version.
16 //
17 // This library is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 // Lesser General Public License for more details.
21 //
22 // You should have received a copy of the GNU Lesser General Public
23 // License along with this library; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 //
26 // You can contact University Corporation for Atmospheric Research at
27 // 3080 Center Green Drive, Boulder, CO 80301
28 
29 #include "config.h"
30 
31 #include <sys/file.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34 #include <dirent.h>
35 #include <fcntl.h>
36 
37 #include <string>
38 #include <sstream>
39 #include <vector>
40 #include <cstring>
41 #include <cerrno>
42 
43 #include "DAPCache3.h"
44 
45 //#define DODS_DEBUG
46 
47 #include "InternalErr.h"
48 #include "DapIndent.h"
49 #include "debug.h"
50 
51 #if 0
52 #include "BESSyntaxUserError.h"
53 #include "BESInternalError.h"
54 
55 #include "TheBESKeys.h"
56 #include "BESDebug.h"
57 #include "BESLog.h"
58 #endif
59 using namespace std;
60 using namespace libdap;
61 
62 // conversion factor
63 static const unsigned long long BYTES_PER_MEG = 1048576ULL;
64 
65 // Max cache size in megs, so we can check the user input and warn.
66 // 2^64 / 2^20 == 2^44
67 static const unsigned long long MAX_CACHE_SIZE_IN_MEGABYTES = (1ULL << 44);
68 
69 DAPCache3 *DAPCache3::d_instance = 0;
70 #if 0
71 // The BESCache3 code is a singleton that assumes it's running in the absence of threads but that
72 // the cache is shared by several processes, each of which have their own instance of BESCache3.
84 BESCache3 *
85 BESCache3::get_instance(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key)
86 {
87  if (d_instance == 0)
88  d_instance = new BESCache3(keys, cache_dir_key, prefix_key, size_key);
89 
90  return d_instance;
91 }
92 #endif
93 
104 DAPCache3 *
105 DAPCache3::get_instance(const string &cache_dir, const string &prefix, unsigned long long size)
106 {
107  if (d_instance == 0)
108  d_instance = new DAPCache3(cache_dir, prefix, size);
109 
110  return d_instance;
111 }
112 
116 DAPCache3 *
118 {
119  if (d_instance == 0)
120  throw InternalErr(__FILE__, __LINE__, "Tried to get the DAPCache3 instance, but it hasn't been created yet");
121 
122  return d_instance;
123 }
124 
125 static inline string get_errno() {
126  char *s_err = strerror(errno);
127  if (s_err)
128  return s_err;
129  else
130  return "Unknown error.";
131 }
132 
133 // Build a lock of a certain type.
134 static inline struct flock *lock(int type) {
135  static struct flock lock;
136  lock.l_type = type;
137  lock.l_whence = SEEK_SET;
138  lock.l_start = 0;
139  lock.l_len = 0;
140  lock.l_pid = getpid();
141 
142  return &lock;
143 }
144 
145 inline void DAPCache3::m_record_descriptor(const string &file, int fd) {
146  DBG(cerr << "DAP Cache: recording descriptor: " << file << ", " << fd << endl);
147  d_locks.insert(std::pair<string, int>(file, fd));
148 }
149 
150 inline int DAPCache3::m_get_descriptor(const string &file) {
151  FilesAndLockDescriptors::iterator i = d_locks.find(file);
152  int fd = i->second;
153  DBG(cerr << "DAP Cache: getting descriptor: " << file << ", " << fd << endl);
154  d_locks.erase(i);
155  return fd;
156 }
157 
163 static void unlock(int fd)
164 {
165  if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
166  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to unlock the file" + get_errno());
167  }
168 
169  if (close(fd) == -1)
170  throw InternalErr(__FILE__, __LINE__, "Could not close the (just) unlocked file.");
171 }
172 
185 static bool getSharedLock(const string &file_name, int &ref_fd)
186 {
187  DBG(cerr << "getSharedLock: " << file_name <<endl);
188 
189  int fd;
190  if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
191  switch (errno) {
192  case ENOENT:
193  return false;
194 
195  default:
196  throw InternalErr(__FILE__, __LINE__, get_errno());
197  }
198  }
199 
200  struct flock *l = lock(F_RDLCK);
201  if (fcntl(fd, F_SETLKW, l) == -1) {
202  close(fd);
203  ostringstream oss;
204  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
205  throw InternalErr(__FILE__, __LINE__, oss.str());
206  }
207 
208  DBG(cerr << "getSharedLock exit: " << file_name <<endl);
209 
210  // Success
211  ref_fd = fd;
212  return true;
213 }
214 
227 static bool getExclusiveLock(string file_name, int &ref_fd)
228 {
229  DBG(cerr << "getExclusiveLock: " << file_name <<endl);
230 
231  int fd;
232  if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
233  switch (errno) {
234  case ENOENT:
235  return false;
236 
237  default:
238  throw InternalErr(__FILE__, __LINE__, get_errno());
239  }
240  }
241 
242  struct flock *l = lock(F_WRLCK);
243  if (fcntl(fd, F_SETLKW, l) == -1) {
244  close(fd);
245  ostringstream oss;
246  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
247  throw InternalErr(__FILE__, __LINE__, oss.str());
248  }
249 
250  DBG(cerr << "getExclusiveLock exit: " << file_name <<endl);
251 
252  // Success
253  ref_fd = fd;
254  return true;
255 }
256 
268 static bool getExclusiveLockNB(string file_name, int &ref_fd)
269 {
270  DBG(cerr << "getExclusiveLock_nonblocking: " << file_name <<endl);
271 
272  int fd;
273  if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
274  switch (errno) {
275  case ENOENT:
276  return false;
277 
278  default:
279  throw InternalErr(__FILE__, __LINE__, get_errno());
280  }
281  }
282 
283  struct flock *l = lock(F_WRLCK);
284  if (fcntl(fd, F_SETLK, l) == -1) {
285  switch (errno) {
286  case EAGAIN:
287  DBG(cerr << "getExclusiveLock_nonblocking exit (false): " << file_name << " by: " << l->l_pid << endl);
288  close(fd);
289  return false;
290 
291  default: {
292  close(fd);
293  ostringstream oss;
294  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
295  throw InternalErr(__FILE__, __LINE__, oss.str());
296  }
297  }
298  }
299 
300  DBG(cerr << "getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
301 
302  // Success
303  ref_fd = fd;
304  return true;
305 }
306 
320 static bool createLockedFile(string file_name, int &ref_fd)
321 {
322  DBG(cerr << "createLockedFile: " << file_name <<endl);
323 
324  int fd;
325  if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
326  switch (errno) {
327  case EEXIST:
328  return false;
329 
330  default:
331  throw InternalErr(__FILE__, __LINE__, get_errno());
332  }
333  }
334 
335  struct flock *l = lock(F_WRLCK);
336  if (fcntl(fd, F_SETLKW, l) == -1) {
337  close(fd);
338  ostringstream oss;
339  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
340  throw InternalErr(__FILE__, __LINE__, oss.str());
341  }
342 
343  DBG(cerr << "createLockedFile exit: " << file_name <<endl);
344 
345  // Success
346  ref_fd = fd;
347  return true;
348 }
349 
351 void DAPCache3::m_check_ctor_params()
352 {
353  if (d_cache_dir.empty()) {
354  string err = "The cache directory was not specified, must be non-empty";
355  throw InternalErr(__FILE__, __LINE__, err);
356  }
357 
358  // TODO New feature: Makes the directory f it does not exist
359  struct stat buf;
360  int statret = stat(d_cache_dir.c_str(), &buf);
361  if (statret != 0 || !S_ISDIR(buf.st_mode)) {
362  // Try to make the directory
363  int status = mkdir(d_cache_dir.c_str(), 0775);
364  if (status != 0) {
365  string err = "The cache directory " + d_cache_dir + " does not exist or could not be created.";
366  throw InternalErr(__FILE__, __LINE__, err);
367  }
368  }
369 
370  if (d_prefix.empty()) {
371  string err = "The cache file prefix was not specified, must not be empty";
372  throw InternalErr(__FILE__, __LINE__, err);
373  }
374 
375  if (d_max_cache_size_in_bytes <= 0) {
376  string err = "The cache size was not specified, must be greater than zero";
377  throw InternalErr(__FILE__, __LINE__, err);
378  }
379 #if 0
380  // redundant check
381 
382  // If the user specifies a cache that is too large,
383  // it is a user exception and we should tell them.
384  if (d_max_cache_size_in_bytes > MAX_CACHE_SIZE_IN_MEGABYTES) {
385  std::ostringstream msg;
386  msg << "The specified cache size was larger than the max cache size of: " << MAX_CACHE_SIZE_IN_MEGABYTES
387  << " (was " << d_max_cache_size_in_bytes << ").";
388  throw InternalErr(__FILE__, __LINE__, msg.str());
389  }
390 #endif
391  DBG(cerr << "DAP Cache: directory " << d_cache_dir << ", prefix " << d_prefix
392  << ", max size " << d_max_cache_size_in_bytes << endl );
393 }
394 
396 void DAPCache3::m_initialize_cache_info()
397 {
398 #if 0
399  // In the libdap version of this caching code, there is not a way to read key-value
400  // pairs from the bes.conf file (yet) so punt on this message. And there is no BES
401  // Log
402 
403  // TODO Fix this so that some sort of configuration file is used. jhrg 10/22/12
404  if (d_max_cache_size_in_bytes > MAX_CACHE_SIZE_IN_MEGABYTES)
405  *(BESLog::TheLog()) << "Cache size too big in configuration file, set to max limit." << endl ;
406 #endif
407  // The value set in configuration files, etc., is the size in megabytes. The private
408  // variable holds the size in bytes (converted below).
409  d_max_cache_size_in_bytes = min(d_max_cache_size_in_bytes, MAX_CACHE_SIZE_IN_MEGABYTES);
410  d_max_cache_size_in_bytes *= BYTES_PER_MEG;
411  d_target_size = d_max_cache_size_in_bytes * 0.8;
412 
413  m_check_ctor_params(); // Throws InternalErr on error.
414 
415  d_cache_info = d_cache_dir + "/dap.cache.info";
416 
417  // See if we can create it. If so, that means it doesn't exist. So make it and
418  // set the cache initial size to zero.
419  if (createLockedFile(d_cache_info, d_cache_info_fd)) {
420  // initialize the cache size to zero
421  unsigned long long size = 0;
422  if (write(d_cache_info_fd, &size, sizeof(unsigned long long)) != sizeof(unsigned long long))
423  throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file in startup!");
424 
425  // This leaves the d_cache_info_fd file descriptor open
426  unlock_cache();
427  }
428  else {
429  if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
430  throw InternalErr(__FILE__, __LINE__, get_errno());
431  }
432  }
433 
434  DBG(cerr << "d_cache_info_fd: " << d_cache_info_fd << endl);
435 }
436 #if 0
437 
451 BESCache3::BESCache3(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key) :
452  d_max_cache_size_in_bytes(0)
453 {
454  bool found = false;
455  keys->get_value(cache_dir_key, d_cache_dir, found);
456  if (!found)
457  throw BESSyntaxUserError("The cache directory key " + cache_dir_key + " was not found in the BES configuration file", __FILE__, __LINE__);
458 
459  found = false;
460  keys->get_value(prefix_key, d_prefix, found);
461  if (!found)
462  throw BESSyntaxUserError("The prefix key " + prefix_key + " was not found in the BES configuration file", __FILE__, __LINE__);
463 
464  found = false;
465  string cache_size_str;
466  keys->get_value(size_key, cache_size_str, found);
467  if (!found)
468  throw BESSyntaxUserError("The size key " + size_key + " was not found in the BES configuration file", __FILE__, __LINE__);
469 
470  std::istringstream is(cache_size_str);
471  is >> d_max_cache_size_in_bytes;
472 
473  m_initialize_cache_info();
474 }
475 #endif
476 
489 DAPCache3::DAPCache3(const string &cache_dir, const string &prefix, unsigned long long size) :
490  d_cache_dir(cache_dir), d_prefix(prefix), d_max_cache_size_in_bytes(size)
491 {
492  m_initialize_cache_info();
493 }
494 
510 string DAPCache3::get_cache_file_name(const string &src, bool mangle)
511 {
512  string target = src;
513 
514  if (mangle) {
515  if (target.at(0) == '/') {
516  target = src.substr(1, target.length() - 1);
517  }
518  string::size_type slash = 0;
519  while ((slash = target.find('/')) != string::npos) {
520  target.replace(slash, 1, 1, DAPCache3::DAP_CACHE_CHAR);
521  }
522  string::size_type last_dot = target.rfind('.');
523  if (last_dot != string::npos) {
524  target = target.substr(0, last_dot);
525  }
526  }
527 
528  return d_cache_dir + "/" + d_prefix + DAPCache3::DAP_CACHE_CHAR + target;
529 }
530 
548 bool DAPCache3::get_read_lock(const string &target, int &fd)
549 {
550  lock_cache_read();
551 
552  bool status = getSharedLock(target, fd);
553 
554  DBG(cerr << "DAP Cache: read_lock: " << target << "(" << status << ")" << endl);
555 
556  if (status)
557  m_record_descriptor(target, fd);
558 
559  unlock_cache();
560 
561  return status;
562 }
563 
576 bool DAPCache3::create_and_lock(const string &target, int &fd)
577 {
579 
580  bool status = createLockedFile(target, fd);
581 
582  DBG(cerr << "DAP Cache: create_and_lock: " << target << "(" << status << ")" << endl);
583 
584  if (status)
585  m_record_descriptor(target, fd);
586 
587  unlock_cache();
588 
589  return status;
590 
591 }
592 
607 {
608  struct flock lock;
609  lock.l_type = F_RDLCK;
610  lock.l_whence = SEEK_SET;
611  lock.l_start = 0;
612  lock.l_len = 0;
613  lock.l_pid = getpid();
614 
615  if (fcntl(fd, F_SETLKW, &lock) == -1) {
616  throw InternalErr(__FILE__, __LINE__, get_errno());
617  }
618 }
619 
629 {
630  DBG(cerr << "lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
631 
632  if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
633  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to lock the cache-control file" + get_errno());
634  }
635 }
636 
641 {
642  DBG(cerr << "lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
643 
644  if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
645  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to lock the cache-control file" + get_errno());
646  }
647 }
648 
655 {
656  DBG(cerr << "DAP Cache: unlock: cache_info (fd: " << d_cache_info_fd << ")" << endl);
657 
658  if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
659  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to unlock the cache-control file" + get_errno());
660  }
661 }
662 
674 void DAPCache3::unlock_and_close(const string &file_name)
675 {
676  DBG(cerr << "DAP Cache: unlock file: " << file_name << endl);
677 
678  unlock(m_get_descriptor(file_name));
679 }
680 
687 {
688  DBG(cerr << "DAP Cache: unlock fd: " << fd << endl);
689 
690  unlock(fd);
691 
692  DBG(cerr << "DAP Cache: unlock " << fd << " Success" << endl);
693 }
694 
705 unsigned long long DAPCache3::update_cache_info(const string &target)
706 {
707  try {
709 
710  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
711  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
712 
713  // read the size from the cache info file
714  unsigned long long current_size;
715  if (read(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
716  throw InternalErr(__FILE__, __LINE__, "Could not get read size info from the cache info file!");
717 
718  struct stat buf;
719  int statret = stat(target.c_str(), &buf);
720  if (statret == 0)
721  current_size += buf.st_size;
722  else
723  throw InternalErr(__FILE__, __LINE__, "Could not read the size of the new file: " + target + " : " + get_errno());
724 
725  DBG(cerr << "DAP Cache: cache size updated to: " << current_size << endl);
726 
727  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
728  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
729 
730  if(write(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
731  throw InternalErr(__FILE__, __LINE__, "Could not write size info from the cache info file!");
732 
733  unlock_cache();
734  return current_size;
735  }
736  catch (...) {
737  unlock_cache();
738  throw;
739  }
740 }
741 
746 bool DAPCache3::cache_too_big(unsigned long long current_size) const
747 {
748  return current_size > d_max_cache_size_in_bytes;
749 }
750 
758 unsigned long long DAPCache3::get_cache_size()
759 {
760  try {
761  lock_cache_read();
762 
763  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
764  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
765  // read the size from the cache info file
766  unsigned long long current_size;
767  if(read(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
768  throw InternalErr(__FILE__, __LINE__, "Could not get read size info from the cache info file!");
769 
770  unlock_cache();
771  return current_size;
772  }
773  catch(...) {
774  unlock_cache();
775  throw;
776  }
777 }
778 
779 
780 static bool entry_op(cache_entry &e1, cache_entry &e2)
781 {
782  return e1.time < e2.time;
783 }
784 
786 unsigned long long DAPCache3::m_collect_cache_dir_info(CacheFiles &contents)
787 {
788  DIR *dip = opendir(d_cache_dir.c_str());
789  if (!dip)
790  throw InternalErr(__FILE__, __LINE__, "Unable to open cache directory " + d_cache_dir);
791 
792  struct dirent *dit;
793  vector<string> files;
794  // go through the cache directory and collect all of the files that
795  // start with the matching prefix
796  while ((dit = readdir(dip)) != NULL) {
797  string dirEntry = dit->d_name;
798  if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0) {
799  files.push_back(d_cache_dir + "/" + dirEntry);
800  }
801  }
802 
803  closedir(dip);
804 
805  unsigned long long current_size = 0;
806  struct stat buf;
807  for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
808  if (stat(file->c_str(), &buf) == 0) {
809  current_size += buf.st_size;
810  cache_entry entry;
811  entry.name = *file;
812  entry.size = buf.st_size;
813  entry.time = buf.st_atime;
814  // Sanity check; Removed after initial testing since some files might be zero bytes
815 #if 0
816  if (entry.size == 0)
817  throw InternalErr(__FILE__, __LINE__, "Zero-byte file found in cache. " + *file);
818 #endif
819  contents.push_back(entry);
820  }
821  }
822 
823  // Sort so smaller (older) times are first.
824  contents.sort(entry_op);
825 
826  return current_size;
827 }
828 
840 void DAPCache3::update_and_purge(const string &new_file)
841 {
842  DBG(cerr << "purge - starting the purge" << endl);
843 
844  try {
846 
847  CacheFiles contents;
848  unsigned long long computed_size = m_collect_cache_dir_info(contents);
849 #if 0
850  if (BESISDEBUG( "cache_contents" )) {
851  DBG(endl << "BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
852  CacheFiles::iterator ti = contents.begin();
853  CacheFiles::iterator te = contents.end();
854  for (; ti != te; ti++) {
855  DBG((*ti).time << ": " << (*ti).name << ": size " << (*ti).size/BYTES_PER_MEG << endl );
856  }
857  }
858 #endif
859  DBG(cerr << "purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG << ", " << d_target_size/BYTES_PER_MEG << endl );
860 
861  // This deletes files and updates computed_size
862  if (cache_too_big(computed_size)) {
863 
864  // d_target_size is 80% of the maximum cache size.
865  // Grab the first which is the oldest in terms of access time.
866  CacheFiles::iterator i = contents.begin();
867  while (i != contents.end() && computed_size > d_target_size) {
868  // Grab an exclusive lock but do not block - if another process has the file locked
869  // just move on to the next file. Also test to see if the current file is the file
870  // this process just added to the cache - don't purge that!
871  int cfile_fd;
872  if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
873  DBG(cerr << "purge: " << i->name << " removed." << endl );
874 
875  if (unlink(i->name.c_str()) != 0)
876  throw InternalErr(__FILE__, __LINE__, "Unable to purge the file " + i->name + " from the cache: " + get_errno());
877 
878  unlock(cfile_fd);
879  computed_size -= i->size;
880  }
881 #if 0
882  else {
883  // This information is useful when debugging... Might comment out for production
884  DBG(cerr << "purge: " << i->name << " is in use." << endl );
885  }
886 #endif
887  ++i;
888 
889  DBG(cerr << "purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG << ", " << d_target_size/BYTES_PER_MEG << endl );
890  }
891 
892  }
893 
894  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
895  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
896 
897  if(write(d_cache_info_fd, &computed_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
898  throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file!");
899 #if 0
900  if (BESISDEBUG( "cache_contents" )) {
901  contents.clear();
902  computed_size = m_collect_cache_dir_info(contents);
903  DBG(endl << "AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
904  CacheFiles::iterator ti = contents.begin();
905  CacheFiles::iterator te = contents.end();
906  for (; ti != te; ti++) {
907  DBG((*ti).time << ": " << (*ti).name << ": size " << (*ti).size/BYTES_PER_MEG << endl );
908  }
909  }
910 #endif
911  unlock_cache();
912  }
913  catch(...) {
914  unlock_cache();
915  throw;
916  }
917 }
918 
930 void DAPCache3::purge_file(const string &file)
931 {
932  DBG(cerr << "purge_file - starting the purge" << endl);
933 
934  try {
936 
937  // Grab an exclusive lock on the file
938  int cfile_fd;
939  if (getExclusiveLock(file, cfile_fd)) {
940  // Get the file's size
941  unsigned long long size = 0;
942  struct stat buf;
943  if (stat(file.c_str(), &buf) == 0) {
944  size = buf.st_size;
945  }
946 
947  DBG(cerr << "purge_file: " << file << " removed." << endl );
948 
949  if (unlink(file.c_str()) != 0)
950  throw InternalErr(__FILE__, __LINE__,
951  "Unable to purge the file " + file + " from the cache: " + get_errno());
952 
953  unlock(cfile_fd);
954 
955  unsigned long long cache_size = get_cache_size() - size;
956 
957  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
958  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
959 
960  if (write(d_cache_info_fd, &cache_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
961  throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file!");
962  }
963 
964  unlock_cache();
965  }
966  catch (...) {
967  unlock_cache();
968  throw;
969  }
970 }
971 
979 void DAPCache3::dump(ostream &strm) const
980 {
981  strm << DapIndent::LMarg << "DAPCache3::dump - (" << (void *) this << ")" << endl;
982  DapIndent::Indent();
983  strm << DapIndent::LMarg << "cache dir: " << d_cache_dir << endl;
984  strm << DapIndent::LMarg << "prefix: " << d_prefix << endl;
985  strm << DapIndent::LMarg << "size (bytes): " << d_max_cache_size_in_bytes << endl;
986  DapIndent::UnIndent();
987 }
988