openMSX
PreCacheFile.cc
Go to the documentation of this file.
1 #include "PreCacheFile.hh"
2 #include "FileOperations.hh"
3 #include "statp.hh"
4 #include <cstdio>
5 #include <sys/types.h>
6 
7 namespace openmsx {
8 
9 PreCacheFile::PreCacheFile(std::string name_)
10  : name(std::move(name_)), exitLoop(false)
11 {
12  thread = std::thread([this]() { run(); });
13 }
14 
16 {
17  exitLoop = true;
18  thread.join();
19 }
20 
21 void PreCacheFile::run()
22 {
23  struct stat st;
24  if (stat(name.c_str(), &st)) return;
25  if (!S_ISREG(st.st_mode)) {
26  // don't pre-cache non regular files (e.g. /dev/fd0)
27  return;
28  }
29 
30  auto file = FileOperations::openFile(name, "rb");
31  if (!file) return;
32 
33  fseek(file.get(), 0, SEEK_END);
34  auto size = ftell(file.get());
35  if (size < 1024 * 1024) {
36  // only pre-cache small files
37 
38  const size_t BLOCK_SIZE = 4096;
39  unsigned block = 0;
40  unsigned repeat = 0;
41  while (true) {
42  if (exitLoop) break;
43 
44  char buf[BLOCK_SIZE];
45  if (fseek(file.get(), block * BLOCK_SIZE, SEEK_SET)) break;
46  size_t read = fread(buf, 1, BLOCK_SIZE, file.get());
47  if (read != BLOCK_SIZE) {
48  // error or end-of-file reached,
49  // in both cases stop pre-caching
50  break;
51  }
52 
53  // Just reading a file linearly from front to back
54  // makes Linux classify the read as a 'streaming read'.
55  // Linux doesn't cache those. To avoid this we read
56  // some of the blocks twice.
57  if (repeat != 0) {
58  --repeat; ++block;
59  } else {
60  repeat = 5;
61  }
62  }
63  }
64 }
65 
66 } // namespace openmsx
STL namespace.
size_t size(std::string_view utf8)
constexpr size_t BLOCK_SIZE
Definition: TigerTree.cc:11
Thanks to enen for testing this on a real cartridge:
Definition: Autofire.cc:5
PreCacheFile(std::string name)
Definition: PreCacheFile.cc:9
FILE_t openFile(const std::string &filename, const std::string &mode)
Call fopen() in a platform-independent manner.