Пример #1
0
void DiskCachingFileLoaderCache::InitCache(const std::string &path) {
	cacheSize_ = 0;
	indexCount_ = 0;
	oldestGeneration_ = 0;
	maxBlocks_ = MAX_BLOCKS_LOWER_BOUND;
	flags_ = 0;
	generation_ = 0;

	const std::string cacheFilePath = MakeCacheFilePath(path);
	bool fileLoaded = LoadCacheFile(cacheFilePath);

	// We do some basic locking to protect against two things: crashes and concurrency.
	// Concurrency will break the file.  Crashes will probably leave it inconsistent.
	if (fileLoaded && !LockCacheFile(true)) {
		if (RemoveCacheFile(cacheFilePath)) {
			// Create a new one.
			fileLoaded = false;
		} else {
			// Couldn't remove, in use?  Give up on caching.
			CloseFileHandle();
		}
	}
	if (!fileLoaded) {
		CreateCacheFile(cacheFilePath);

		if (!LockCacheFile(true)) {
			CloseFileHandle();
		}
	}
}
Пример #2
0
bool DiskCachingFileLoaderCache::LockCacheFile(bool lockStatus) {
	if (!f_) {
		return false;
	}

	u32 offset = (u32)offsetof(FileHeader, flags);

	bool failed = false;
	if (fseek(f_, offset, SEEK_SET) != 0) {
		failed = true;
	} else if (fread(&flags_, sizeof(u32), 1, f_) != 1) {
		failed = true;
	}

	if (failed) {
		ERROR_LOG(LOADER, "Unable to read current flags during disk cache locking");
		CloseFileHandle();
		return false;
	}

	// TODO: Also use flock where supported?
	if (lockStatus) {
		if ((flags_ & FLAG_LOCKED) != 0) {
			ERROR_LOG(LOADER, "Could not lock disk cache file for %s", origPath_.c_str());
			return false;
		}
		flags_ |= FLAG_LOCKED;
	} else {
		if ((flags_ & FLAG_LOCKED) == 0) {
			ERROR_LOG(LOADER, "Could not unlock disk cache file for %s", origPath_.c_str());
			return false;
		}
		flags_ &= ~FLAG_LOCKED;
	}

	if (fseek(f_, offset, SEEK_SET) != 0) {
		failed = true;
	} else if (fwrite(&flags_, sizeof(u32), 1, f_) != 1) {
		failed = true;
	} else if (fflush(f_) != 0) {
		failed = true;
	}

	if (failed) {
		ERROR_LOG(LOADER, "Unable to write updated flags during disk cache locking");
		CloseFileHandle();
		return false;
	}

	if (lockStatus) {
		INFO_LOG(LOADER, "Locked disk cache file for %s", origPath_.c_str());
	} else {
		INFO_LOG(LOADER, "Unlocked disk cache file for %s", origPath_.c_str());
	}
	return true;
}
Пример #3
0
void DiskCachingFileLoaderCache::CreateCacheFile(const std::string &path) {
	maxBlocks_ = DetermineMaxBlocks();
	if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) {
		GarbageCollectCacheFiles(MAX_BLOCKS_LOWER_BOUND * DEFAULT_BLOCK_SIZE);
		maxBlocks_ = DetermineMaxBlocks();
	}
	if (maxBlocks_ < MAX_BLOCKS_LOWER_BOUND) {
		// There's not enough free space to cache, disable.
		f_ = nullptr;
		ERROR_LOG(LOADER, "Not enough free space; disabling disk cache");
		return;
	}
	flags_ = 0;

	f_ = File::OpenCFile(path, "wb+");
	if (!f_) {
		ERROR_LOG(LOADER, "Could not create disk cache file");
		return;
	}
#ifdef __ANDROID__
	// Android NDK does not support 64-bit file I/O using C streams
	fd_ = fileno(f_);
#endif

	blockSize_ = DEFAULT_BLOCK_SIZE;

	FileHeader header;
	memcpy(header.magic, CACHEFILE_MAGIC, sizeof(header.magic));
	header.version = CACHE_VERSION;
	header.blockSize = blockSize_;
	header.filesize = filesize_;
	header.maxBlocks = maxBlocks_;
	header.flags = flags_;

	if (fwrite(&header, sizeof(header), 1, f_) != 1) {
		CloseFileHandle();
		return;
	}

	indexCount_ = (filesize_ + blockSize_ - 1) / blockSize_;
	index_.clear();
	index_.resize(indexCount_);
	blockIndexLookup_.resize(maxBlocks_);
	memset(&blockIndexLookup_[0], INVALID_INDEX, maxBlocks_ * sizeof(blockIndexLookup_[0]));

	if (fwrite(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) {
		CloseFileHandle();
		return;
	}
	if (fflush(f_) != 0) {
		CloseFileHandle();
		return;
	}

	INFO_LOG(LOADER, "Created new disk cache file for %s", origPath_.c_str());
}
Пример #4
0
void DiskCachingFileLoaderCache::WriteBlockData(BlockInfo &info, u8 *src) {
	if (!f_) {
		return;
	}
	s64 blockOffset = GetBlockOffset(info.block);

	bool failed = false;
#ifdef __ANDROID__
	if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) {
		failed = true;
	} else if (write(fd_, src, blockSize_) != (ssize_t)blockSize_) {
		failed = true;
	}
#else
	if (fseeko(f_, blockOffset, SEEK_SET) != 0) {
		failed = true;
	} else if (fwrite(src, blockSize_, 1, f_) != 1) {
		failed = true;
	}
#endif

	if (failed) {
		ERROR_LOG(LOADER, "Unable to write disk cache data entry.");
		CloseFileHandle();
	}
}
Пример #5
0
bool DiskCachingFileLoaderCache::ReadBlockData(u8 *dest, BlockInfo &info, size_t offset, size_t size) {
	if (!f_) {
		return false;
	}
	s64 blockOffset = GetBlockOffset(info.block);

	// Before we read, make sure the buffers are flushed.
	// We might be trying to read an area we've recently written.
	fflush(f_);

	bool failed = false;
#ifdef __ANDROID__
	if (lseek64(fd_, blockOffset, SEEK_SET) != blockOffset) {
		failed = true;
	} else if (read(fd_, dest + offset, size) != (ssize_t)size) {
		failed = true;
	}
#else
	if (fseeko(f_, blockOffset, SEEK_SET) != 0) {
		failed = true;
	} else if (fread(dest + offset, size, 1, f_) != 1) {
		failed = true;
	}
#endif

	if (failed) {
		ERROR_LOG(LOADER, "Unable to read disk cache data entry.");
		CloseFileHandle();
	}
	return !failed;
}
Пример #6
0
void DiskCachingFileLoaderCache::LoadCacheIndex() {
	if (fseek(f_, sizeof(FileHeader), SEEK_SET) != 0) {
		CloseFileHandle();
		return;
	}

	indexCount_ = (filesize_ + blockSize_ - 1) / blockSize_;
	index_.resize(indexCount_);
	blockIndexLookup_.resize(maxBlocks_);
	memset(&blockIndexLookup_[0], INVALID_INDEX, maxBlocks_ * sizeof(blockIndexLookup_[0]));

	if (fread(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) {
		CloseFileHandle();
		return;
	}

	// Now let's set some values we need.
	oldestGeneration_ = std::numeric_limits<u16>::max();
	generation_ = 0;
	cacheSize_ = 0;

	for (size_t i = 0; i < index_.size(); ++i) {
		if (index_[i].block > maxBlocks_) {
			index_[i].block = INVALID_BLOCK;
		}
		if (index_[i].block == INVALID_BLOCK) {
			continue;
		}

		if (index_[i].generation < oldestGeneration_) {
			oldestGeneration_ = index_[i].generation;
		}
		if (index_[i].generation > generation_) {
			generation_ = index_[i].generation;
		}
		++cacheSize_;

		blockIndexLookup_[index_[i].block] = (u32)i;
	}
}
Пример #7
0
void DiskCachingFileLoaderCache::WriteIndexData(u32 indexPos, BlockInfo &info) {
	if (!f_) {
		return;
	}

	u32 offset = (u32)sizeof(FileHeader) + indexPos * (u32)sizeof(BlockInfo);

	bool failed = false;
	if (fseek(f_, offset, SEEK_SET) != 0) {
		failed = true;
	} else if (fwrite(&info, sizeof(BlockInfo), 1, f_) != 1) {
		failed = true;
	}

	if (failed) {
		ERROR_LOG(LOADER, "Unable to write disk cache index entry.");
		CloseFileHandle();
	}
}
Пример #8
0
void DiskCachingFileLoaderCache::ShutdownCache() {
	if (f_) {
		bool failed = false;
		if (fseek(f_, sizeof(FileHeader), SEEK_SET) != 0) {
			failed = true;
		} else if (fwrite(&index_[0], sizeof(BlockInfo), indexCount_, f_) != indexCount_) {
			failed = true;
		} else if (fflush(f_) != 0) {
			failed = true;
		}
		if (failed) {
			// Leave it locked, it's broken.
			ERROR_LOG(LOADER, "Unable to flush disk cache.");
		} else {
			LockCacheFile(false);
		}
		CloseFileHandle();
	}

	index_.clear();
	blockIndexLookup_.clear();
	cacheSize_ = 0;
}
Пример #9
0
/**
 * @brief Close file view pane and close file handle.
 * @param hwnd - parent window handle.
 */
inline static void CloseViewPane(HWND hwnd)
{
	CloseFileView(hwnd);
	CloseFileHandle();
}
Пример #10
0
bool MAPFAM::OpenTableFile(PGLOBAL g)
  {
  char    filename[_MAX_PATH];
  int     len;
  MODE    mode = Tdbp->GetMode();
  PFBLOCK fp;
  PDBUSER dbuserp = (PDBUSER)g->Activityp->Aptr;

#if defined(_DEBUG)
  // Insert mode is no more handled using file mapping
  assert(mode != MODE_INSERT);
#endif   // _DEBUG

  /*********************************************************************/
  /*  We used the file name relative to recorded datapath.             */
  /*********************************************************************/
  PlugSetPath(filename, To_File, Tdbp->GetPath());

  /*********************************************************************/
  /*  Under Win32 the whole file will be mapped so we can use it as    */
  /*  if it were entirely read into virtual memory.                    */
  /*  Firstly we check whether this file have been already mapped.     */
  /*********************************************************************/
  if (mode == MODE_READ) {
    for (fp = dbuserp->Openlist; fp; fp = fp->Next)
      if (fp->Type == TYPE_FB_MAP && !stricmp(fp->Fname, filename)
                     && fp->Count && fp->Mode == mode)
        break;

    if (trace)
      htrc("Mapping file, fp=%p\n", fp);

  } else
    fp = NULL;

  if (fp) {
    /*******************************************************************/
    /*  File already mapped. Just increment use count and get pointer. */
    /*******************************************************************/
    fp->Count++;
    Memory = fp->Memory;
    len = fp->Length;
  } else {
    /*******************************************************************/
    /*  If required, delete the whole file if no filtering is implied. */
    /*******************************************************************/
    bool   del;
    HANDLE hFile;
    MEMMAP mm;

    del = mode == MODE_DELETE && !Tdbp->GetNext();

    if (del)
      DelRows = Cardinality(g);

    /*******************************************************************/
    /*  Create the mapping file object.                                */
    /*******************************************************************/
    hFile = CreateFileMap(g, filename, &mm, mode, del);

    if (hFile == INVALID_HANDLE_VALUE) {
      DWORD rc = GetLastError();

      if (!(*g->Message))
        sprintf(g->Message, MSG(OPEN_MODE_ERROR),
                "map", (int) rc, filename);

      if (trace)
        htrc("CreateFileMap: %s\n", g->Message);

      return (mode == MODE_READ && rc == ENOENT)
              ? PushWarning(g, Tdbp) : true;
      } // endif hFile

    /*******************************************************************/
    /*  Get the file size (assuming file is smaller than 4 GB)         */
    /*******************************************************************/
    len = mm.lenL;
    Memory = (char *)mm.memory;

    if (!len) {              // Empty or deleted file
      CloseFileHandle(hFile);
      Tdbp->ResetSize();
      return false;
      } // endif len

    if (!Memory) {
      CloseFileHandle(hFile);
      sprintf(g->Message, MSG(MAP_VIEW_ERROR),
                          filename, GetLastError());
      return true;
      } // endif Memory

#if defined(WIN32)
    if (mode != MODE_DELETE) {
#else   // !WIN32
    if (mode == MODE_READ) {
#endif  // !WIN32
      CloseFileHandle(hFile);                    // Not used anymore
      hFile = INVALID_HANDLE_VALUE;              // For Fblock
      } // endif Mode

    /*******************************************************************/
    /*  Link a Fblock. This make possible to reuse already opened maps */
    /*  and also to automatically unmap them in case of error g->jump. */
    /*  Note: block can already exist for previously closed file.      */
    /*******************************************************************/
    fp = (PFBLOCK)PlugSubAlloc(g, NULL, sizeof(FBLOCK));
    fp->Type = TYPE_FB_MAP;
    fp->Fname = (char*)PlugSubAlloc(g, NULL, strlen(filename) + 1);
    strcpy((char*)fp->Fname, filename);
    fp->Next = dbuserp->Openlist;
    dbuserp->Openlist = fp;
    fp->Count = 1;
    fp->Length = len;
    fp->Memory = Memory;
    fp->Mode = mode;
    fp->File = NULL;
    fp->Handle = hFile;                // Used for Delete
  } // endif fp

  To_Fb = fp;                               // Useful when closing

  /*********************************************************************/
  /*  The pseudo "buffer" is here the entire file mapping view.        */
  /*********************************************************************/
  Fpos = Mempos = Memory;
  Top = Memory + len;

  if (trace)
    htrc("fp=%p count=%d MapView=%p len=%d Top=%p\n",
          fp, fp->Count, Memory, len, Top);

  return AllocateBuffer(g);          // Useful for DBF files
  } // end of OpenTableFile

/***********************************************************************/
/*  GetRowID: return the RowID of last read record.                    */
/***********************************************************************/
int MAPFAM::GetRowID(void)
  {
  return Rows;
  } // end of GetRowID

/***********************************************************************/
/*  GetPos: return the position of last read record.                   */
/***********************************************************************/
int MAPFAM::GetPos(void)
  {
  return Fpos - Memory;
  } // end of GetPos

/***********************************************************************/
/*  GetNextPos: return the position of next record.                    */
/***********************************************************************/
int MAPFAM::GetNextPos(void)
  {
  return Mempos - Memory;
  } // end of GetNextPos

/***********************************************************************/
/*  SetPos: Replace the table at the specified position.               */
/***********************************************************************/
bool MAPFAM::SetPos(PGLOBAL g, int pos)
  {
  Fpos = Mempos = Memory + pos;

  if (Mempos >= Top || Mempos < Memory) {
    strcpy(g->Message, MSG(INV_MAP_POS));
    return true;
    } // endif Mempos

  Placed = true;
  return false;
  } // end of SetPos
Пример #11
0
bool DiskCachingFileLoaderCache::RemoveCacheFile(const std::string &path) {
	// Note that some platforms, you can't delete open files.  So we check.
	CloseFileHandle();
	return File::Delete(path);
}