libdap  Updated for version 3.20.6
libdap4 is an implementation of OPeNDAP's DAP protocol.
DAPCache3.cc
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 #ifdef HAVE_STDLIB_H
38 #include <stdlib.h>
39 #endif
40 
41 #include <string>
42 #include <sstream>
43 #include <vector>
44 #include <cstring>
45 #include <cerrno>
46 
47 #include "DAPCache3.h"
48 
49 //#define DODS_DEBUG
50 
51 #include "InternalErr.h"
52 #include "DapIndent.h"
53 #include "debug.h"
54 
55 #if 0
56 #include "BESSyntaxUserError.h"
57 #include "BESInternalError.h"
58 
59 #include "TheBESKeys.h"
60 #include "BESDebug.h"
61 #include "BESLog.h"
62 #endif
63 using namespace std;
64 using namespace libdap;
65 
66 namespace libdap {
67 
68 // conversion factor
69 static const unsigned long long BYTES_PER_MEG = 1048576ULL;
70 
71 // Max cache size in megs, so we can check the user input and warn.
72 // 2^64 / 2^20 == 2^44
73 static const unsigned long long MAX_CACHE_SIZE_IN_MEGABYTES = (1ULL << 44);
74 
75 DAPCache3 *DAPCache3::d_instance = 0;
76 
77 
91 DAPCache3::DAPCache3(const string &cache_dir, const string &prefix, unsigned long long size) :
92  d_cache_dir(cache_dir), d_prefix(prefix), d_max_cache_size_in_bytes(size)
93 {
94  m_initialize_cache_info();
95 }
96 
97 void DAPCache3::delete_instance() {
98  DBG(cerr << "DAPCache3::delete_instance() - Deleting singleton DAPCache3 instance." << endl);
99  delete d_instance;
100  d_instance = 0;
101 }
102 
103 #if 0
104 // The BESCache3 code is a singleton that assumes it's running in the absence of threads but that
105 // the cache is shared by several processes, each of which have their own instance of BESCache3.
117 BESCache3 *
118 BESCache3::get_instance(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key)
119 {
120  if (d_instance == 0)
121  d_instance = new BESCache3(keys, cache_dir_key, prefix_key, size_key);
122 
123  return d_instance;
124 }
125 #endif
126 
137 DAPCache3 *
138 DAPCache3::get_instance(const string &cache_dir, const string &prefix, unsigned long long size)
139 {
140  if (d_instance == 0){
141  d_instance = new DAPCache3(cache_dir, prefix, size);
142 #if HAVE_ATEXIT
143  atexit(delete_instance);
144 #endif
145  }
146  return d_instance;
147 }
148 
152 DAPCache3 *
154 {
155  if (d_instance == 0)
156  throw InternalErr(__FILE__, __LINE__, "Tried to get the DAPCache3 instance, but it hasn't been created yet");
157 
158  return d_instance;
159 }
160 
161 static inline string get_errno() {
162  char *s_err = strerror(errno);
163  if (s_err)
164  return s_err;
165  else
166  return "Unknown error.";
167 }
168 
169 // Build a lock of a certain type.
170 static inline struct flock *lock(int type) {
171  static struct flock lock;
172  lock.l_type = type;
173  lock.l_whence = SEEK_SET;
174  lock.l_start = 0;
175  lock.l_len = 0;
176  lock.l_pid = getpid();
177 
178  return &lock;
179 }
180 
181 inline void DAPCache3::m_record_descriptor(const string &file, int fd) {
182  DBG(cerr << "DAP Cache: recording descriptor: " << file << ", " << fd << endl);
183  d_locks.insert(std::pair<string, int>(file, fd));
184 }
185 
186 inline int DAPCache3::m_get_descriptor(const string &file) {
187  FilesAndLockDescriptors::iterator i = d_locks.find(file);
188  int fd = i->second;
189  DBG(cerr << "DAP Cache: getting descriptor: " << file << ", " << fd << endl);
190  d_locks.erase(i);
191  return fd;
192 }
193 
199 static void unlock(int fd)
200 {
201  if (fcntl(fd, F_SETLK, lock(F_UNLCK)) == -1) {
202  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to unlock the file" + get_errno());
203  }
204 
205  if (close(fd) == -1)
206  throw InternalErr(__FILE__, __LINE__, "Could not close the (just) unlocked file.");
207 }
208 
221 static bool getSharedLock(const string &file_name, int &ref_fd)
222 {
223  DBG(cerr << "getSharedLock: " << file_name <<endl);
224 
225  int fd;
226  if ((fd = open(file_name.c_str(), O_RDONLY)) < 0) {
227  switch (errno) {
228  case ENOENT:
229  return false;
230 
231  default:
232  throw InternalErr(__FILE__, __LINE__, get_errno());
233  }
234  }
235 
236  struct flock *l = lock(F_RDLCK);
237  if (fcntl(fd, F_SETLKW, l) == -1) {
238  close(fd);
239  ostringstream oss;
240  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
241  throw InternalErr(__FILE__, __LINE__, oss.str());
242  }
243 
244  DBG(cerr << "getSharedLock exit: " << file_name <<endl);
245 
246  // Success
247  ref_fd = fd;
248  return true;
249 }
250 
263 static bool getExclusiveLock(string file_name, int &ref_fd)
264 {
265  DBG(cerr << "getExclusiveLock: " << file_name <<endl);
266 
267  int fd;
268  if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
269  switch (errno) {
270  case ENOENT:
271  return false;
272 
273  default:
274  throw InternalErr(__FILE__, __LINE__, get_errno());
275  }
276  }
277 
278  struct flock *l = lock(F_WRLCK);
279  if (fcntl(fd, F_SETLKW, l) == -1) {
280  close(fd);
281  ostringstream oss;
282  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
283  throw InternalErr(__FILE__, __LINE__, oss.str());
284  }
285 
286  DBG(cerr << "getExclusiveLock exit: " << file_name <<endl);
287 
288  // Success
289  ref_fd = fd;
290  return true;
291 }
292 
304 static bool getExclusiveLockNB(string file_name, int &ref_fd)
305 {
306  DBG(cerr << "getExclusiveLock_nonblocking: " << file_name <<endl);
307 
308  int fd;
309  if ((fd = open(file_name.c_str(), O_RDWR)) < 0) {
310  switch (errno) {
311  case ENOENT:
312  return false;
313 
314  default:
315  throw InternalErr(__FILE__, __LINE__, get_errno());
316  }
317  }
318 
319  struct flock *l = lock(F_WRLCK);
320  if (fcntl(fd, F_SETLK, l) == -1) {
321  switch (errno) {
322  case EAGAIN:
323  DBG(cerr << "getExclusiveLock_nonblocking exit (false): " << file_name << " by: " << l->l_pid << endl);
324  close(fd);
325  return false;
326 
327  default: {
328  close(fd);
329  ostringstream oss;
330  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
331  throw InternalErr(__FILE__, __LINE__, oss.str());
332  }
333  }
334  }
335 
336  DBG(cerr << "getExclusiveLock_nonblocking exit (true): " << file_name <<endl);
337 
338  // Success
339  ref_fd = fd;
340  return true;
341 }
342 
356 static bool createLockedFile(string file_name, int &ref_fd)
357 {
358  DBG(cerr << "createLockedFile: " << file_name <<endl);
359 
360  int fd;
361  if ((fd = open(file_name.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666)) < 0) {
362  switch (errno) {
363  case EEXIST:
364  return false;
365 
366  default:
367  throw InternalErr(__FILE__, __LINE__, get_errno());
368  }
369  }
370 
371  struct flock *l = lock(F_WRLCK);
372  if (fcntl(fd, F_SETLKW, l) == -1) {
373  close(fd);
374  ostringstream oss;
375  oss << "cache process: " << l->l_pid << " triggered a locking error: " << get_errno();
376  throw InternalErr(__FILE__, __LINE__, oss.str());
377  }
378 
379  DBG(cerr << "createLockedFile exit: " << file_name <<endl);
380 
381  // Success
382  ref_fd = fd;
383  return true;
384 }
385 
387 void DAPCache3::m_check_ctor_params()
388 {
389  if (d_cache_dir.empty()) {
390  string err = "The cache directory was not specified, must be non-empty";
391  throw InternalErr(__FILE__, __LINE__, err);
392  }
393 
394  // TODO New feature: Makes the directory f it does not exist
395  struct stat buf;
396  int statret = stat(d_cache_dir.c_str(), &buf);
397  if (statret != 0 || !S_ISDIR(buf.st_mode)) {
398  // Try to make the directory
399  int status = mkdir(d_cache_dir.c_str(), 0775);
400  if (status != 0) {
401  string err = "The cache directory " + d_cache_dir + " does not exist or could not be created.";
402  throw InternalErr(__FILE__, __LINE__, err);
403  }
404  }
405 
406  if (d_prefix.empty()) {
407  string err = "The cache file prefix was not specified, must not be empty";
408  throw InternalErr(__FILE__, __LINE__, err);
409  }
410 
411  if (d_max_cache_size_in_bytes <= 0) {
412  string err = "The cache size was not specified, must be greater than zero";
413  throw InternalErr(__FILE__, __LINE__, err);
414  }
415 #if 0
416  // redundant check
417 
418  // If the user specifies a cache that is too large,
419  // it is a user exception and we should tell them.
420  if (d_max_cache_size_in_bytes > MAX_CACHE_SIZE_IN_MEGABYTES) {
421  std::ostringstream msg;
422  msg << "The specified cache size was larger than the max cache size of: " << MAX_CACHE_SIZE_IN_MEGABYTES
423  << " (was " << d_max_cache_size_in_bytes << ").";
424  throw InternalErr(__FILE__, __LINE__, msg.str());
425  }
426 #endif
427  DBG(cerr << "DAP Cache: directory " << d_cache_dir << ", prefix " << d_prefix
428  << ", max size " << d_max_cache_size_in_bytes << endl );
429 }
430 
432 void DAPCache3::m_initialize_cache_info()
433 {
434  // The value set in configuration files, etc., is the size in megabytes. The private
435  // variable holds the size in bytes (converted below).
436  d_max_cache_size_in_bytes = min(d_max_cache_size_in_bytes, MAX_CACHE_SIZE_IN_MEGABYTES);
437  d_max_cache_size_in_bytes *= BYTES_PER_MEG;
438  d_target_size = d_max_cache_size_in_bytes * 0.8;
439 
440  m_check_ctor_params(); // Throws InternalErr on error.
441 
442  d_cache_info = d_cache_dir + "/dap.cache.info";
443 
444  // See if we can create it. If so, that means it doesn't exist. So make it and
445  // set the cache initial size to zero.
446  if (createLockedFile(d_cache_info, d_cache_info_fd)) {
447  // initialize the cache size to zero
448  unsigned long long size = 0;
449  if (write(d_cache_info_fd, &size, sizeof(unsigned long long)) != sizeof(unsigned long long))
450  throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file in startup!");
451 
452  // This leaves the d_cache_info_fd file descriptor open
453  unlock_cache();
454  }
455  else {
456  if ((d_cache_info_fd = open(d_cache_info.c_str(), O_RDWR)) == -1) {
457  throw InternalErr(__FILE__, __LINE__, get_errno());
458  }
459  }
460 
461  DBG(cerr << "d_cache_info_fd: " << d_cache_info_fd << endl);
462 }
463 
464 #if 0
465 
479 BESCache3::BESCache3(BESKeys *keys, const string &cache_dir_key, const string &prefix_key, const string &size_key) :
480  d_max_cache_size_in_bytes(0)
481 {
482  bool found = false;
483  keys->get_value(cache_dir_key, d_cache_dir, found);
484  if (!found)
485  throw BESSyntaxUserError("The cache directory key " + cache_dir_key + " was not found in the BES configuration file", __FILE__, __LINE__);
486 
487  found = false;
488  keys->get_value(prefix_key, d_prefix, found);
489  if (!found)
490  throw BESSyntaxUserError("The prefix key " + prefix_key + " was not found in the BES configuration file", __FILE__, __LINE__);
491 
492  found = false;
493  string cache_size_str;
494  keys->get_value(size_key, cache_size_str, found);
495  if (!found)
496  throw BESSyntaxUserError("The size key " + size_key + " was not found in the BES configuration file", __FILE__, __LINE__);
497 
498  std::istringstream is(cache_size_str);
499  is >> d_max_cache_size_in_bytes;
500 
501  m_initialize_cache_info();
502 }
503 #endif
504 
505 
521 string DAPCache3::get_cache_file_name(const string &src, bool mangle)
522 {
523  string target = src;
524 
525  if (mangle) {
526  if (target.at(0) == '/') {
527  target = src.substr(1, target.length() - 1);
528  }
529  string::size_type slash = 0;
530  while ((slash = target.find('/')) != string::npos) {
531  target.replace(slash, 1, 1, DAPCache3::DAP_CACHE_CHAR);
532  }
533  string::size_type last_dot = target.rfind('.');
534  if (last_dot != string::npos) {
535  target = target.substr(0, last_dot);
536  }
537  }
538  DBG(cerr << " d_cache_dir: '" << d_cache_dir << "'" << endl);
539  DBG(cerr << " d_prefix: '" << d_prefix << "'" << endl);
540  DBG(cerr << " target: '" << target << "'" << endl);
541 
542  return d_cache_dir + "/" + d_prefix + DAPCache3::DAP_CACHE_CHAR + target;
543 }
544 
562 bool DAPCache3::get_read_lock(const string &target, int &fd)
563 {
564  lock_cache_read();
565 
566  bool status = getSharedLock(target, fd);
567 
568  DBG(cerr << "DAP Cache: read_lock: " << target << "(" << status << ")" << endl);
569 
570  if (status)
571  m_record_descriptor(target, fd);
572 
573  unlock_cache();
574 
575  return status;
576 }
577 
590 bool DAPCache3::create_and_lock(const string &target, int &fd)
591 {
592  lock_cache_write();
593 
594  bool status = createLockedFile(target, fd);
595 
596  DBG(cerr << "DAP Cache: create_and_lock: " << target << "(" << status << ")" << endl);
597 
598  if (status)
599  m_record_descriptor(target, fd);
600 
601  unlock_cache();
602 
603  return status;
604 
605 }
606 
621 {
622  struct flock lock;
623  lock.l_type = F_RDLCK;
624  lock.l_whence = SEEK_SET;
625  lock.l_start = 0;
626  lock.l_len = 0;
627  lock.l_pid = getpid();
628 
629  if (fcntl(fd, F_SETLKW, &lock) == -1) {
630  throw InternalErr(__FILE__, __LINE__, get_errno());
631  }
632 }
633 
643 {
644  DBG(cerr << "lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
645 
646  if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_WRLCK)) == -1) {
647  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to lock the cache-control file" + get_errno());
648  }
649 }
650 
655 {
656  DBG(cerr << "lock_cache - d_cache_info_fd: " << d_cache_info_fd << endl);
657 
658  if (fcntl(d_cache_info_fd, F_SETLKW, lock(F_RDLCK)) == -1) {
659  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to lock the cache-control file" + get_errno());
660  }
661 }
662 
669 {
670  DBG(cerr << "DAP Cache: unlock: cache_info (fd: " << d_cache_info_fd << ")" << endl);
671 
672  if (fcntl(d_cache_info_fd, F_SETLK, lock(F_UNLCK)) == -1) {
673  throw InternalErr(__FILE__, __LINE__, "An error occurred trying to unlock the cache-control file" + get_errno());
674  }
675 }
676 
688 void DAPCache3::unlock_and_close(const string &file_name)
689 {
690  DBG(cerr << "DAP Cache: unlock file: " << file_name << endl);
691 
692  unlock(m_get_descriptor(file_name));
693 }
694 
700 void DAPCache3::unlock_and_close(int fd)
701 {
702  DBG(cerr << "DAP Cache: unlock fd: " << fd << endl);
703 
704  unlock(fd);
705 
706  DBG(cerr << "DAP Cache: unlock " << fd << " Success" << endl);
707 }
708 
719 unsigned long long DAPCache3::update_cache_info(const string &target)
720 {
721  try {
722  lock_cache_write();
723 
724  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
725  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
726 
727  // read the size from the cache info file
728  unsigned long long current_size;
729  if (read(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
730  throw InternalErr(__FILE__, __LINE__, "Could not get read size info from the cache info file!");
731 
732  struct stat buf;
733  int statret = stat(target.c_str(), &buf);
734  if (statret == 0)
735  current_size += buf.st_size;
736  else
737  throw InternalErr(__FILE__, __LINE__, "Could not read the size of the new file: " + target + " : " + get_errno());
738 
739  DBG(cerr << "DAP Cache: cache size updated to: " << current_size << endl);
740 
741  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
742  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
743 
744  if(write(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
745  throw InternalErr(__FILE__, __LINE__, "Could not write size info from the cache info file!");
746 
747  unlock_cache();
748  return current_size;
749  }
750  catch (...) {
751  unlock_cache();
752  throw;
753  }
754 }
755 
760 bool DAPCache3::cache_too_big(unsigned long long current_size) const
761 {
762  return current_size > d_max_cache_size_in_bytes;
763 }
764 
772 unsigned long long DAPCache3::get_cache_size()
773 {
774  try {
775  lock_cache_read();
776 
777  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
778  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
779  // read the size from the cache info file
780  unsigned long long current_size;
781  if(read(d_cache_info_fd, &current_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
782  throw InternalErr(__FILE__, __LINE__, "Could not get read size info from the cache info file!");
783 
784  unlock_cache();
785  return current_size;
786  }
787  catch(...) {
788  unlock_cache();
789  throw;
790  }
791 }
792 
793 
794 static bool entry_op(cache_entry &e1, cache_entry &e2)
795 {
796  return e1.time < e2.time;
797 }
798 
800 unsigned long long DAPCache3::m_collect_cache_dir_info(CacheFiles &contents)
801 {
802  DIR *dip = opendir(d_cache_dir.c_str());
803  if (!dip)
804  throw InternalErr(__FILE__, __LINE__, "Unable to open cache directory " + d_cache_dir);
805 
806  struct dirent *dit;
807  vector<string> files;
808  // go through the cache directory and collect all of the files that
809  // start with the matching prefix
810  while ((dit = readdir(dip)) != NULL) {
811  string dirEntry = dit->d_name;
812  if (dirEntry.compare(0, d_prefix.length(), d_prefix) == 0) {
813  files.push_back(d_cache_dir + "/" + dirEntry);
814  }
815  }
816 
817  closedir(dip);
818 
819  unsigned long long current_size = 0;
820  struct stat buf;
821  for (vector<string>::iterator file = files.begin(); file != files.end(); ++file) {
822  if (stat(file->c_str(), &buf) == 0) {
823  current_size += buf.st_size;
824  cache_entry entry;
825  entry.name = *file;
826  entry.size = buf.st_size;
827  entry.time = buf.st_atime;
828  // Sanity check; Removed after initial testing since some files might be zero bytes
829 #if 0
830  if (entry.size == 0)
831  throw InternalErr(__FILE__, __LINE__, "Zero-byte file found in cache. " + *file);
832 #endif
833  contents.push_back(entry);
834  }
835  }
836 
837  // Sort so smaller (older) times are first.
838  contents.sort(entry_op);
839 
840  return current_size;
841 }
842 
854 void DAPCache3::update_and_purge(const string &new_file)
855 {
856  DBG(cerr << "purge - starting the purge" << endl);
857 
858  try {
859  lock_cache_write();
860 
861  CacheFiles contents;
862  unsigned long long computed_size = m_collect_cache_dir_info(contents);
863 #if 0
864  if (BESISDEBUG( "cache_contents" )) {
865  DBG(endl << "BEFORE Purge " << computed_size/BYTES_PER_MEG << endl );
866  CacheFiles::iterator ti = contents.begin();
867  CacheFiles::iterator te = contents.end();
868  for (; ti != te; ti++) {
869  DBG((*ti).time << ": " << (*ti).name << ": size " << (*ti).size/BYTES_PER_MEG << endl );
870  }
871  }
872 #endif
873  DBG(cerr << "purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG << ", " << d_target_size/BYTES_PER_MEG << endl );
874 
875  // This deletes files and updates computed_size
876  if (cache_too_big(computed_size)) {
877 
878  // d_target_size is 80% of the maximum cache size.
879  // Grab the first which is the oldest in terms of access time.
880  CacheFiles::iterator i = contents.begin();
881  while (i != contents.end() && computed_size > d_target_size) {
882  // Grab an exclusive lock but do not block - if another process has the file locked
883  // just move on to the next file. Also test to see if the current file is the file
884  // this process just added to the cache - don't purge that!
885  int cfile_fd;
886  if (i->name != new_file && getExclusiveLockNB(i->name, cfile_fd)) {
887  DBG(cerr << "purge: " << i->name << " removed." << endl );
888 
889  if (unlink(i->name.c_str()) != 0)
890  throw InternalErr(__FILE__, __LINE__, "Unable to purge the file " + i->name + " from the cache: " + get_errno());
891 
892  unlock(cfile_fd);
893  computed_size -= i->size;
894  }
895 #if 0
896  else {
897  // This information is useful when debugging... Might comment out for production
898  DBG(cerr << "purge: " << i->name << " is in use." << endl );
899  }
900 #endif
901  ++i;
902 
903  DBG(cerr << "purge - current and target size (in MB) " << computed_size/BYTES_PER_MEG << ", " << d_target_size/BYTES_PER_MEG << endl );
904  }
905 
906  }
907 
908  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
909  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
910 
911  if(write(d_cache_info_fd, &computed_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
912  throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file!");
913 #if 0
914  if (BESISDEBUG( "cache_contents" )) {
915  contents.clear();
916  computed_size = m_collect_cache_dir_info(contents);
917  DBG(endl << "AFTER Purge " << computed_size/BYTES_PER_MEG << endl );
918  CacheFiles::iterator ti = contents.begin();
919  CacheFiles::iterator te = contents.end();
920  for (; ti != te; ti++) {
921  DBG((*ti).time << ": " << (*ti).name << ": size " << (*ti).size/BYTES_PER_MEG << endl );
922  }
923  }
924 #endif
925  unlock_cache();
926  }
927  catch(...) {
928  unlock_cache();
929  throw;
930  }
931 }
932 
944 void DAPCache3::purge_file(const string &file)
945 {
946  DBG(cerr << "purge_file - starting the purge" << endl);
947 
948  try {
949  lock_cache_write();
950 
951  // Grab an exclusive lock on the file
952  int cfile_fd;
953  if (getExclusiveLock(file, cfile_fd)) {
954  // Get the file's size
955  unsigned long long size = 0;
956  struct stat buf;
957  if (stat(file.c_str(), &buf) == 0) {
958  size = buf.st_size;
959  }
960 
961  DBG(cerr << "purge_file: " << file << " removed." << endl );
962 
963  if (unlink(file.c_str()) != 0)
964  throw InternalErr(__FILE__, __LINE__,
965  "Unable to purge the file " + file + " from the cache: " + get_errno());
966 
967  unlock(cfile_fd);
968 
969  unsigned long long cache_size = get_cache_size() - size;
970 
971  if (lseek(d_cache_info_fd, 0, SEEK_SET) == -1)
972  throw InternalErr(__FILE__, __LINE__, "Could not rewind to front of cache info file.");
973 
974  if (write(d_cache_info_fd, &cache_size, sizeof(unsigned long long)) != sizeof(unsigned long long))
975  throw InternalErr(__FILE__, __LINE__, "Could not write size info to the cache info file!");
976  }
977 
978  unlock_cache();
979  }
980  catch (...) {
981  unlock_cache();
982  throw;
983  }
984 }
985 
993 void DAPCache3::dump(ostream &strm) const
994 {
995  strm << DapIndent::LMarg << "DAPCache3::dump - (" << (void *) this << ")" << endl;
996  DapIndent::Indent();
997  strm << DapIndent::LMarg << "cache dir: " << d_cache_dir << endl;
998  strm << DapIndent::LMarg << "prefix: " << d_prefix << endl;
999  strm << DapIndent::LMarg << "size (bytes): " << d_max_cache_size_in_bytes << endl;
1000  DapIndent::UnIndent();
1001 }
1002 
1003 } // namespace libdap
virtual unsigned long long update_cache_info(const string &target)
Update the cache info file to include &#39;target&#39;.
Definition: DAPCache3.cc:719
STL namespace.
string get_cache_file_name(const string &src, bool mangle=true)
Definition: DAPCache3.cc:521
virtual void dump(ostream &strm) const
dumps information about this object
Definition: DAPCache3.cc:993
virtual unsigned long long get_cache_size()
Get the cache size. Read the size information from the cache info file and return it...
Definition: DAPCache3.cc:772
top level DAP object to house generic methods
Definition: AISConnect.cc:30
A class for software fault reporting.
Definition: InternalErr.h:64
virtual bool create_and_lock(const string &target, int &fd)
Create a file in the cache and lock it for write access. If the file does not exist, make it, open it for read-write access and get an exclusive lock on it. The locking operation blocks, although that should never happen.
Definition: DAPCache3.cc:590
virtual void lock_cache_write()
Definition: DAPCache3.cc:642
virtual bool cache_too_big(unsigned long long current_size) const
look at the cache size; is it too large? Look at the cache size and see if it is too big...
Definition: DAPCache3.cc:760
virtual void exclusive_to_shared_lock(int fd)
Transfer from an exclusive lock to a shared lock. If the file has an exclusive write lock on it...
Definition: DAPCache3.cc:620
virtual bool get_read_lock(const string &target, int &fd)
Get a read-only lock on the file if it exists.
Definition: DAPCache3.cc:562
virtual void lock_cache_read()
Definition: DAPCache3.cc:654
virtual void update_and_purge(const string &new_file)
Purge files from the cache.
Definition: DAPCache3.cc:854
Definition: DAPCache3.h:51
static DAPCache3 * get_instance()
Definition: DAPCache3.cc:153
Implementation of a caching mechanism for compressed data. This cache uses simple advisory locking fo...
Definition: DAPCache3.h:82
virtual void purge_file(const string &file)
Purge a single file from the cache.
Definition: DAPCache3.cc:944
virtual void unlock_cache()
Definition: DAPCache3.cc:668