annotate common.cpp @ 392:78fed0d4c108 api-inversion

Include some necessary information in struct adb. Now the struct adb contains a database fd, the flags used to open that fd (so that we can later tell if it was for write or not) and a database header pointer. audiodb_open() is now responsible for filling in all of that information. To do that, it needs to take an open(2) flag; that's good, because it means that the call to open(2) is no longer invoking undefined behaviour. (Also, the previous version of audiodb_open() leaked an fd). Unfortunately, that means we have broken ABI and API compatibility. (Fortunately, we have fewer than 12 users). Use audiodb_open() in audioDB::initDBHeader(). We've temporarily(?) put acquire_lock(int, bool) in the API header; that means we need to include <stdbool.h> and compile C files with -std=c99. Do so. Make audiodb_close() free resources allocated by audiodb_open(). Include a struct adb * field in the audioDB C++ object... ... which lets us actually implement memory-correctness, by audiodb_close()ing the database in audioDB::cleanup(). [ The lock is, I think, correctly disposed of; man fcntl(2) on Linux says that the locks are released once any file descriptor relating to the file is closed, and we close the fd in audiodb_close(). ]
author mas01cr
date Mon, 24 Nov 2008 15:42:15 +0000
parents 4e68f7d4d524
children bc7a821004bb
rev   line source
mas01cr@239 1 #include "audioDB.h"
mas01cr@392 2 extern "C" {
mas01cr@392 3 #include "audioDB_API.h"
mas01cr@392 4 }
mas01cr@239 5
mas01cr@239 6 #if defined(O2_DEBUG)
mas01cr@239 7 void sigterm_action(int signal, siginfo_t *info, void *context) {
mas01cr@239 8 exit(128+signal);
mas01cr@239 9 }
mas01cr@239 10
mas01cr@239 11 void sighup_action(int signal, siginfo_t *info, void *context) {
mas01cr@239 12 // FIXME: reread any configuration files
mas01cr@239 13 }
mas01cr@239 14 #endif
mas01cr@239 15
mas01cr@385 16 int acquire_lock(int fd, bool exclusive) {
mas01cr@239 17 struct flock lock;
mas01cr@239 18 int status;
mas01cr@239 19
mas01cr@239 20 lock.l_type = exclusive ? F_WRLCK : F_RDLCK;
mas01cr@239 21 lock.l_whence = SEEK_SET;
mas01cr@239 22 lock.l_start = 0;
mas01cr@239 23 lock.l_len = 0; /* "the whole file" */
mas01cr@239 24
mas01cr@239 25 retry:
mas01cr@239 26 do {
mas01cr@239 27 status = fcntl(fd, F_SETLKW, &lock);
mas01cr@239 28 } while (status != 0 && errno == EINTR);
mas01cr@239 29
mas01cr@239 30 if (status) {
mas01cr@239 31 if (errno == EAGAIN) {
mas01cr@239 32 sleep(1);
mas01cr@239 33 goto retry;
mas01cr@239 34 } else {
mas01cr@385 35 return status;
mas01cr@239 36 }
mas01cr@239 37 }
mas01cr@385 38 return 0;
mas01cr@239 39 }
mas01cr@239 40
mas01cr@385 41 int divest_lock(int fd) {
mas01cr@239 42 struct flock lock;
mas01cr@239 43
mas01cr@239 44 lock.l_type = F_UNLCK;
mas01cr@239 45 lock.l_whence = SEEK_SET;
mas01cr@239 46 lock.l_start = 0;
mas01cr@239 47 lock.l_len = 0;
mas01cr@239 48
mas01cr@385 49 return fcntl(fd, F_SETLKW, &lock);
mas01cr@385 50 }
mas01cr@239 51
mas01cr@385 52 void audioDB::get_lock(int fd, bool exclusive) {
mas01cr@385 53 if(acquire_lock(fd, exclusive)) {
mas01cr@385 54 error("fcntl lock error", "", "fcntl");
mas01cr@385 55 }
mas01cr@385 56 }
mas01cr@385 57
mas01cr@385 58 void audioDB::release_lock(int fd) {
mas01cr@385 59 if (divest_lock(fd)) {
mas01cr@239 60 error("fcntl unlock error", "", "fcntl");
mas01cr@385 61 }
mas01cr@239 62 }
mas01cr@239 63
mas01cr@239 64 void audioDB::error(const char* a, const char* b, const char *sysFunc) {
mas01ik@355 65
mas01ik@355 66
mas01ik@355 67 if(isServer) {
mas01cr@370 68 /* FIXME: I think this is leaky -- we never delete err.
mas01cr@370 69 actually deleting it is tricky, though; it gets placed into
mas01cr@370 70 some soap-internal struct with uncertain extent... -- CSR,
mas01cr@370 71 2007-10-01 */
mas01cr@370 72 char *err = new char[256]; /* FIXME: overflows */
mas01cr@370 73 snprintf(err, 255, "%s: %s\n%s", a, b, sysFunc ? strerror(errno) : "");
mas01cr@370 74 /* FIXME: actually we could usefully do with a properly
mas01cr@370 75 structured type, so that we can throw separate faultstring
mas01cr@370 76 and details. -- CSR, 2007-10-01 */
mas01ik@355 77 throw(err);
mas01ik@355 78 } else if (UseApiError){
mas01ik@355 79 apierrortemp=-1;
mas01ik@355 80 throw(apierrortemp);
mas01ik@355 81 } else {
mas01ik@355 82 std::cerr << a << ": " << b << std::endl;
mas01ik@355 83 if (sysFunc) {
mas01ik@355 84 perror(sysFunc);
mas01ik@355 85 }
mas01ik@355 86 exit(1);
mas01cr@239 87 }
mas01ik@355 88
mas01cr@239 89 }
mas01cr@239 90
mas01cr@284 91 void audioDB::initRNG() {
mas01cr@284 92 rng = gsl_rng_alloc(gsl_rng_mt19937);
mas01cr@284 93 if(!rng) {
mas01cr@284 94 error("could not allocate Random Number Generator");
mas01cr@284 95 }
mas01cr@284 96 /* FIXME: maybe we should use a real source of entropy? */
mas01cr@284 97 gsl_rng_set(rng, time(NULL));
mas01cr@284 98 }
mas01cr@284 99
mas01cr@239 100 void audioDB::initDBHeader(const char* dbName) {
mas01cr@392 101 adb = audiodb_open(dbName, forWrite ? O_RDWR : O_RDONLY);
mas01cr@392 102 if(!adb) {
mas01cr@392 103 error("Failed to open database", dbName);
mas01cr@239 104 }
mas01cr@392 105 dbfid = adb->fd;
mas01cr@392 106 dbH = adb->header;
mas01cr@239 107
mas01cr@239 108 CHECKED_MMAP(char *, db, 0, getpagesize());
mas01cr@239 109
mas01cr@239 110 // Make some handy tables with correct types
mas01cr@239 111 if(forWrite || (dbH->length > 0)) {
mas01cr@239 112 if(forWrite) {
mas01cr@239 113 fileTableLength = dbH->trackTableOffset - dbH->fileTableOffset;
mas01cr@239 114 trackTableLength = dbH->dataOffset - dbH->trackTableOffset;
mas01cr@239 115 dataBufLength = dbH->timesTableOffset - dbH->dataOffset;
mas01cr@239 116 timesTableLength = dbH->powerTableOffset - dbH->timesTableOffset;
mas01cr@239 117 powerTableLength = dbH->l2normTableOffset - dbH->powerTableOffset;
mas01cr@239 118 l2normTableLength = dbH->dbSize - dbH->l2normTableOffset;
mas01cr@239 119 } else {
mas01cr@256 120 fileTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01cr@256 121 trackTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_TRACKTABLE_ENTRY_SIZE);
mas01mc@324 122 if( dbH->flags & O2_FLAG_LARGE_ADB ){
mas01mc@324 123 dataBufLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01mc@324 124 timesTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01mc@324 125 powerTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01mc@324 126 l2normTableLength = 0;
mas01mc@324 127 }
mas01mc@324 128 else{
mas01mc@324 129 dataBufLength = ALIGN_PAGE_UP(dbH->length);
mas01mc@324 130 timesTableLength = ALIGN_PAGE_UP(2*(dbH->length / dbH->dim));
mas01mc@324 131 powerTableLength = ALIGN_PAGE_UP(dbH->length / dbH->dim);
mas01mc@324 132 l2normTableLength = ALIGN_PAGE_UP(dbH->length / dbH->dim);
mas01mc@324 133 }
mas01cr@239 134 }
mas01cr@239 135 CHECKED_MMAP(char *, fileTable, dbH->fileTableOffset, fileTableLength);
mas01cr@239 136 CHECKED_MMAP(unsigned *, trackTable, dbH->trackTableOffset, trackTableLength);
mas01cr@239 137 /*
mas01cr@239 138 * No more mmap() for dataBuf
mas01cr@239 139 *
mas01cr@239 140 * FIXME: Actually we do do the mmap() in the two cases where it's
mas01cr@239 141 * still "needed": in pointQuery and in l2norm if dbH->length is
mas01cr@239 142 * non-zero. Removing those cases too (and deleting the dataBuf
mas01cr@239 143 * variable completely) would be cool. -- CSR, 2007-11-19
mas01cr@239 144 *
mas01cr@239 145 * CHECKED_MMAP(double *, dataBuf, dbH->dataOffset, dataBufLength);
mas01cr@239 146 */
mas01mc@324 147 if( dbH->flags & O2_FLAG_LARGE_ADB ){
mas01mc@324 148 CHECKED_MMAP(char *, featureFileNameTable, dbH->dataOffset, fileTableLength);
mas01mc@324 149 if( dbH->flags & O2_FLAG_TIMES )
mas01mc@324 150 CHECKED_MMAP(char *, timesFileNameTable, dbH->timesTableOffset, fileTableLength);
mas01mc@324 151 if( dbH->flags & O2_FLAG_POWER )
mas01mc@324 152 CHECKED_MMAP(char *, powerFileNameTable, dbH->powerTableOffset, fileTableLength);
mas01mc@324 153 }
mas01mc@324 154 else{
mas01mc@324 155 CHECKED_MMAP(double *, timesTable, dbH->timesTableOffset, timesTableLength);
mas01mc@324 156 CHECKED_MMAP(double *, powerTable, dbH->powerTableOffset, powerTableLength);
mas01mc@324 157 CHECKED_MMAP(double *, l2normTable, dbH->l2normTableOffset, l2normTableLength);
mas01mc@324 158 }
mas01cr@239 159 }
mas01mc@292 160
mas01mc@292 161 // build track offset table
mas01mc@292 162 trackOffsetTable = new off_t[dbH->numFiles];
mas01mc@292 163 Uns32T cumTrack=0;
mas01mc@292 164 for(Uns32T k = 0; k < dbH->numFiles; k++){
mas01mc@292 165 trackOffsetTable[k] = cumTrack;
mas01mc@292 166 cumTrack += trackTable[k] * dbH->dim;
mas01mc@324 167 }
mas01mc@324 168
mas01mc@324 169 // Assign correct number of point bits per track in LSH indexing / retrieval
mas01mc@324 170 lsh_n_point_bits = dbH->flags >> 28;
mas01mc@324 171 if( !lsh_n_point_bits )
mas01mc@324 172 lsh_n_point_bits = O2_DEFAULT_LSH_N_POINT_BITS;
mas01cr@239 173 }
mas01cr@239 174
mas01mc@324 175 void audioDB::initInputFile (const char *inFile, bool loadData) {
mas01cr@239 176 if (inFile) {
mas01cr@239 177 if ((infid = open(inFile, O_RDONLY)) < 0) {
mas01cr@239 178 error("can't open input file for reading", inFile, "open");
mas01cr@239 179 }
mas01cr@239 180
mas01cr@239 181 if (fstat(infid, &statbuf) < 0) {
mas01cr@239 182 error("fstat error finding size of input", inFile, "fstat");
mas01cr@239 183 }
mas01cr@239 184
mas01cr@239 185 if(dbH->dim == 0 && dbH->length == 0) { // empty database
mas01cr@239 186 // initialize with input dimensionality
mas01cr@239 187 if(read(infid, &dbH->dim, sizeof(unsigned)) != sizeof(unsigned)) {
mas01cr@239 188 error("short read of input file", inFile);
mas01cr@239 189 }
mas01cr@239 190 if(dbH->dim == 0) {
mas01cr@239 191 error("dimensionality of zero in input file", inFile);
mas01cr@239 192 }
mas01cr@239 193 } else {
mas01cr@239 194 unsigned test;
mas01cr@239 195 if(read(infid, &test, sizeof(unsigned)) != sizeof(unsigned)) {
mas01cr@239 196 error("short read of input file", inFile);
mas01cr@239 197 }
mas01cr@239 198 if(dbH->dim == 0) {
mas01cr@239 199 error("dimensionality of zero in input file", inFile);
mas01cr@239 200 }
mas01cr@239 201 if(dbH->dim != test) {
mas01cr@239 202 std::cerr << "error: expected dimension: " << dbH->dim << ", got : " << test <<std::endl;
mas01cr@239 203 error("feature dimensions do not match database table dimensions", inFile);
mas01cr@239 204 }
mas01cr@239 205 }
mas01cr@239 206
mas01mc@324 207 if (loadData && ((indata = (char *) mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, infid, 0)) == (caddr_t) -1)) {
mas01cr@239 208 error("mmap error for input", inFile, "mmap");
mas01cr@239 209 }
mas01cr@239 210 }
mas01cr@239 211 }
mas01cr@239 212
mas01mc@292 213 void audioDB::initTables(const char* dbName, const char* inFile) {
mas01cr@284 214 /* FIXME: initRNG() really logically belongs in the audioDB
mas01cr@284 215 contructor. However, there are of the order of four constructors
mas01cr@284 216 at the moment, and more to come from API implementation. Given
mas01cr@284 217 that duplication, I think this is the least worst place to put
mas01cr@284 218 it; the assumption is that nothing which doesn't look at a
mas01cr@284 219 database will need an RNG. -- CSR, 2008-07-02 */
mas01cr@284 220 initRNG();
mas01cr@239 221 initDBHeader(dbName);
mas01mc@292 222 if(inFile)
mas01mc@292 223 initInputFile(inFile);
mas01cr@239 224 }
mas01mc@292 225
mas01mc@324 226 // If name is relative path, side effect name with prefix/name
mas01mc@324 227 // Do not free original pointer
mas01mc@324 228 void audioDB::prefix_name(char** const name, const char* prefix){
mas01mc@324 229 // No prefix if prefix is empty
mas01mc@324 230 if(!prefix)
mas01mc@324 231 return;
mas01mc@324 232 // Allocate new memory, keep old memory
mas01mc@324 233 assert(name && *name);
mas01mc@324 234 if (strlen(*name) + strlen(prefix) + 1 > O2_MAXFILESTR)
mas01mc@324 235 error("error: path prefix + filename too long",prefix);
mas01mc@324 236 // Do not prefix absolute path+filename
mas01mc@324 237 if(**name=='/')
mas01mc@324 238 return;
mas01mc@324 239 // OK to prefix relative path+filename
mas01mc@324 240 char* prefixedName = (char*) malloc(O2_MAXFILESTR);
mas01mc@324 241 sprintf(prefixedName, "%s/%s", prefix, *name);
mas01mc@324 242 *name = prefixedName; // side effect new name to old name
mas01mc@324 243 }