annotate common.cpp @ 395:bc7a821004bb api-inversion

Invert audioDB::status / audiodb_status(). To do that without breaking abstractions, we actually need a new field in the status structure, storing the size of the data region. Previously, this was computed in the audioDB::status request from the database header, but I'm assuming that "user" code doesn't have access to such internals. While we're at it, name some intermediate values in audioDB::status() so that I don't get confused. Here's the thing, though: we need to make sure that the adb_t * that we have from audiodb_open() or audiodb_create() is propagated all the way through into the C++ routines that implement library functions -- in particular those which actually write to the database; otherwise we won't have a consistent view in memory of the header on-disk (as the adb header that will have been written to disk won't be the same as the one in memory). We can do that, by altering the "API" audioDB constructors to take the adb_t * argument, and setting the adb field in the audioDB object that we've already introduced to that. But now we need to be careful a couple of times: if we have one, then audioDB::initTables() mustn't stomp on it; also, if we're only constructing an audioDB instance to fulfil an API request, we mustn't audiodb_close() the one we have when we destroy the audioDB object, because the adb_t * is the one we have passed in and are going to reuse in later calls to the API. The good news is that we can be careful in just these ways with minimal code. The really good news is that once the inversion is complete, all of this horribleness will automatically go away (as there will be no code which constructs audioDB objects to fulfil API functions). Hooray! It's almost like it was all planned this way.
author mas01cr
date Tue, 25 Nov 2008 16:41:01 +0000
parents 78fed0d4c108
children 58b88ab69424
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 if(!adb) {
mas01cr@395 102 adb = audiodb_open(dbName, forWrite ? O_RDWR : O_RDONLY);
mas01cr@395 103 if(!adb) {
mas01cr@395 104 error("Failed to open database", dbName);
mas01cr@395 105 }
mas01cr@239 106 }
mas01cr@392 107 dbfid = adb->fd;
mas01cr@392 108 dbH = adb->header;
mas01cr@239 109
mas01cr@239 110 CHECKED_MMAP(char *, db, 0, getpagesize());
mas01cr@239 111
mas01cr@239 112 // Make some handy tables with correct types
mas01cr@239 113 if(forWrite || (dbH->length > 0)) {
mas01cr@239 114 if(forWrite) {
mas01cr@239 115 fileTableLength = dbH->trackTableOffset - dbH->fileTableOffset;
mas01cr@239 116 trackTableLength = dbH->dataOffset - dbH->trackTableOffset;
mas01cr@239 117 dataBufLength = dbH->timesTableOffset - dbH->dataOffset;
mas01cr@239 118 timesTableLength = dbH->powerTableOffset - dbH->timesTableOffset;
mas01cr@239 119 powerTableLength = dbH->l2normTableOffset - dbH->powerTableOffset;
mas01cr@239 120 l2normTableLength = dbH->dbSize - dbH->l2normTableOffset;
mas01cr@239 121 } else {
mas01cr@256 122 fileTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01cr@256 123 trackTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_TRACKTABLE_ENTRY_SIZE);
mas01mc@324 124 if( dbH->flags & O2_FLAG_LARGE_ADB ){
mas01mc@324 125 dataBufLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01mc@324 126 timesTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01mc@324 127 powerTableLength = ALIGN_PAGE_UP(dbH->numFiles * O2_FILETABLE_ENTRY_SIZE);
mas01mc@324 128 l2normTableLength = 0;
mas01mc@324 129 }
mas01mc@324 130 else{
mas01mc@324 131 dataBufLength = ALIGN_PAGE_UP(dbH->length);
mas01mc@324 132 timesTableLength = ALIGN_PAGE_UP(2*(dbH->length / dbH->dim));
mas01mc@324 133 powerTableLength = ALIGN_PAGE_UP(dbH->length / dbH->dim);
mas01mc@324 134 l2normTableLength = ALIGN_PAGE_UP(dbH->length / dbH->dim);
mas01mc@324 135 }
mas01cr@239 136 }
mas01cr@239 137 CHECKED_MMAP(char *, fileTable, dbH->fileTableOffset, fileTableLength);
mas01cr@239 138 CHECKED_MMAP(unsigned *, trackTable, dbH->trackTableOffset, trackTableLength);
mas01cr@239 139 /*
mas01cr@239 140 * No more mmap() for dataBuf
mas01cr@239 141 *
mas01cr@239 142 * FIXME: Actually we do do the mmap() in the two cases where it's
mas01cr@239 143 * still "needed": in pointQuery and in l2norm if dbH->length is
mas01cr@239 144 * non-zero. Removing those cases too (and deleting the dataBuf
mas01cr@239 145 * variable completely) would be cool. -- CSR, 2007-11-19
mas01cr@239 146 *
mas01cr@239 147 * CHECKED_MMAP(double *, dataBuf, dbH->dataOffset, dataBufLength);
mas01cr@239 148 */
mas01mc@324 149 if( dbH->flags & O2_FLAG_LARGE_ADB ){
mas01mc@324 150 CHECKED_MMAP(char *, featureFileNameTable, dbH->dataOffset, fileTableLength);
mas01mc@324 151 if( dbH->flags & O2_FLAG_TIMES )
mas01mc@324 152 CHECKED_MMAP(char *, timesFileNameTable, dbH->timesTableOffset, fileTableLength);
mas01mc@324 153 if( dbH->flags & O2_FLAG_POWER )
mas01mc@324 154 CHECKED_MMAP(char *, powerFileNameTable, dbH->powerTableOffset, fileTableLength);
mas01mc@324 155 }
mas01mc@324 156 else{
mas01mc@324 157 CHECKED_MMAP(double *, timesTable, dbH->timesTableOffset, timesTableLength);
mas01mc@324 158 CHECKED_MMAP(double *, powerTable, dbH->powerTableOffset, powerTableLength);
mas01mc@324 159 CHECKED_MMAP(double *, l2normTable, dbH->l2normTableOffset, l2normTableLength);
mas01mc@324 160 }
mas01cr@239 161 }
mas01mc@292 162
mas01mc@292 163 // build track offset table
mas01mc@292 164 trackOffsetTable = new off_t[dbH->numFiles];
mas01mc@292 165 Uns32T cumTrack=0;
mas01mc@292 166 for(Uns32T k = 0; k < dbH->numFiles; k++){
mas01mc@292 167 trackOffsetTable[k] = cumTrack;
mas01mc@292 168 cumTrack += trackTable[k] * dbH->dim;
mas01mc@324 169 }
mas01mc@324 170
mas01mc@324 171 // Assign correct number of point bits per track in LSH indexing / retrieval
mas01mc@324 172 lsh_n_point_bits = dbH->flags >> 28;
mas01mc@324 173 if( !lsh_n_point_bits )
mas01mc@324 174 lsh_n_point_bits = O2_DEFAULT_LSH_N_POINT_BITS;
mas01cr@239 175 }
mas01cr@239 176
mas01mc@324 177 void audioDB::initInputFile (const char *inFile, bool loadData) {
mas01cr@239 178 if (inFile) {
mas01cr@239 179 if ((infid = open(inFile, O_RDONLY)) < 0) {
mas01cr@239 180 error("can't open input file for reading", inFile, "open");
mas01cr@239 181 }
mas01cr@239 182
mas01cr@239 183 if (fstat(infid, &statbuf) < 0) {
mas01cr@239 184 error("fstat error finding size of input", inFile, "fstat");
mas01cr@239 185 }
mas01cr@239 186
mas01cr@239 187 if(dbH->dim == 0 && dbH->length == 0) { // empty database
mas01cr@239 188 // initialize with input dimensionality
mas01cr@239 189 if(read(infid, &dbH->dim, sizeof(unsigned)) != sizeof(unsigned)) {
mas01cr@239 190 error("short read of input file", inFile);
mas01cr@239 191 }
mas01cr@239 192 if(dbH->dim == 0) {
mas01cr@239 193 error("dimensionality of zero in input file", inFile);
mas01cr@239 194 }
mas01cr@239 195 } else {
mas01cr@239 196 unsigned test;
mas01cr@239 197 if(read(infid, &test, sizeof(unsigned)) != sizeof(unsigned)) {
mas01cr@239 198 error("short read of input file", inFile);
mas01cr@239 199 }
mas01cr@239 200 if(dbH->dim == 0) {
mas01cr@239 201 error("dimensionality of zero in input file", inFile);
mas01cr@239 202 }
mas01cr@239 203 if(dbH->dim != test) {
mas01cr@239 204 std::cerr << "error: expected dimension: " << dbH->dim << ", got : " << test <<std::endl;
mas01cr@239 205 error("feature dimensions do not match database table dimensions", inFile);
mas01cr@239 206 }
mas01cr@239 207 }
mas01cr@239 208
mas01mc@324 209 if (loadData && ((indata = (char *) mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, infid, 0)) == (caddr_t) -1)) {
mas01cr@239 210 error("mmap error for input", inFile, "mmap");
mas01cr@239 211 }
mas01cr@239 212 }
mas01cr@239 213 }
mas01cr@239 214
mas01mc@292 215 void audioDB::initTables(const char* dbName, const char* inFile) {
mas01cr@284 216 /* FIXME: initRNG() really logically belongs in the audioDB
mas01cr@284 217 contructor. However, there are of the order of four constructors
mas01cr@284 218 at the moment, and more to come from API implementation. Given
mas01cr@284 219 that duplication, I think this is the least worst place to put
mas01cr@284 220 it; the assumption is that nothing which doesn't look at a
mas01cr@284 221 database will need an RNG. -- CSR, 2008-07-02 */
mas01cr@284 222 initRNG();
mas01cr@239 223 initDBHeader(dbName);
mas01mc@292 224 if(inFile)
mas01mc@292 225 initInputFile(inFile);
mas01cr@239 226 }
mas01mc@292 227
mas01mc@324 228 // If name is relative path, side effect name with prefix/name
mas01mc@324 229 // Do not free original pointer
mas01mc@324 230 void audioDB::prefix_name(char** const name, const char* prefix){
mas01mc@324 231 // No prefix if prefix is empty
mas01mc@324 232 if(!prefix)
mas01mc@324 233 return;
mas01mc@324 234 // Allocate new memory, keep old memory
mas01mc@324 235 assert(name && *name);
mas01mc@324 236 if (strlen(*name) + strlen(prefix) + 1 > O2_MAXFILESTR)
mas01mc@324 237 error("error: path prefix + filename too long",prefix);
mas01mc@324 238 // Do not prefix absolute path+filename
mas01mc@324 239 if(**name=='/')
mas01mc@324 240 return;
mas01mc@324 241 // OK to prefix relative path+filename
mas01mc@324 242 char* prefixedName = (char*) malloc(O2_MAXFILESTR);
mas01mc@324 243 sprintf(prefixedName, "%s/%s", prefix, *name);
mas01mc@324 244 *name = prefixedName; // side effect new name to old name
mas01mc@324 245 }