filemap_ut.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. #include <library/cpp/testing/unittest/registar.h>
  2. #ifdef _unix_
  3. #include <sys/resource.h>
  4. #endif
  5. #include "filemap.h"
  6. #include <util/system/fs.h>
  7. #include <cstring>
  8. #include <cstdio>
  9. Y_UNIT_TEST_SUITE(TFileMapTest) {
  10. static const char* FileName_("./mappped_file");
  11. void BasicTest(TMemoryMapCommon::EOpenMode mode) {
  12. char data[] = "abcdefgh";
  13. TFile file(FileName_, CreateAlways | WrOnly);
  14. file.Write(static_cast<void*>(data), sizeof(data));
  15. file.Close();
  16. {
  17. TFileMap mappedFile(FileName_, mode);
  18. mappedFile.Map(0, mappedFile.Length());
  19. UNIT_ASSERT(mappedFile.MappedSize() == sizeof(data) && mappedFile.Length() == sizeof(data));
  20. UNIT_ASSERT(mappedFile.IsOpen());
  21. for (size_t i = 0; i < sizeof(data); ++i) {
  22. UNIT_ASSERT(static_cast<char*>(mappedFile.Ptr())[i] == data[i]);
  23. static_cast<char*>(mappedFile.Ptr())[i] = data[i] + 1;
  24. }
  25. mappedFile.Flush();
  26. TFileMap::TMapResult mapResult = mappedFile.Map(2, 2);
  27. UNIT_ASSERT(mapResult.MappedSize() == 2);
  28. UNIT_ASSERT(mapResult.MappedData() == mappedFile.Ptr());
  29. UNIT_ASSERT(mappedFile.MappedSize() == 2);
  30. UNIT_ASSERT(static_cast<char*>(mappedFile.Ptr())[0] == 'd' && static_cast<char*>(mappedFile.Ptr())[1] == 'e');
  31. mappedFile.Unmap();
  32. UNIT_ASSERT(mappedFile.MappedSize() == 0);
  33. FILE* f = fopen(FileName_, "rb");
  34. TFileMap mappedFile2(f);
  35. mappedFile2.Map(0, mappedFile2.Length());
  36. UNIT_ASSERT(mappedFile2.MappedSize() == sizeof(data));
  37. UNIT_ASSERT(static_cast<char*>(mappedFile2.Ptr())[0] == data[0] + 1);
  38. fclose(f);
  39. }
  40. NFs::Remove(FileName_);
  41. }
  42. Y_UNIT_TEST(TestFileMap) {
  43. BasicTest(TMemoryMapCommon::oRdWr);
  44. }
  45. Y_UNIT_TEST(TestFileMapPopulate) {
  46. BasicTest(TMemoryMapCommon::oRdWr | TMemoryMapCommon::oPopulate);
  47. }
  48. Y_UNIT_TEST(TestFileRemap) {
  49. const char data1[] = "01234";
  50. const char data2[] = "abcdefg";
  51. const char data3[] = "COPY";
  52. const char dataFinal[] = "012abcdefg";
  53. const size_t data2Shift = 3;
  54. TFile file(FileName_, CreateAlways | WrOnly);
  55. file.Write(static_cast<const void*>(data1), sizeof(data1));
  56. file.Close();
  57. {
  58. TFileMap mappedFile(FileName_, TMemoryMapCommon::oRdWr);
  59. mappedFile.Map(0, mappedFile.Length());
  60. UNIT_ASSERT(mappedFile.MappedSize() == sizeof(data1) &&
  61. mappedFile.Length() == sizeof(data1));
  62. mappedFile.ResizeAndRemap(data2Shift, sizeof(data2));
  63. memcpy(mappedFile.Ptr(), data2, sizeof(data2));
  64. }
  65. {
  66. TFileMap mappedFile(FileName_, TMemoryMapCommon::oCopyOnWr);
  67. mappedFile.Map(0, mappedFile.Length());
  68. UNIT_ASSERT(mappedFile.MappedSize() == sizeof(dataFinal) &&
  69. mappedFile.Length() == sizeof(dataFinal));
  70. char* data = static_cast<char*>(mappedFile.Ptr());
  71. UNIT_ASSERT(data[0] == '0');
  72. UNIT_ASSERT(data[3] == 'a');
  73. memcpy(data, data3, sizeof(data3));
  74. UNIT_ASSERT(data[0] == 'C');
  75. UNIT_ASSERT(data[3] == 'Y');
  76. }
  77. TFile resFile(FileName_, RdOnly);
  78. UNIT_ASSERT(resFile.GetLength() == sizeof(dataFinal));
  79. char buf[sizeof(dataFinal)];
  80. resFile.Read(buf, sizeof(dataFinal));
  81. UNIT_ASSERT(0 == memcmp(buf, dataFinal, sizeof(dataFinal)));
  82. resFile.Close();
  83. NFs::Remove(FileName_);
  84. }
  85. Y_UNIT_TEST(TestFileMapDbgName) {
  86. // This test checks that dbgName passed to the TFileMap constructor is saved inside the object and appears
  87. // in subsequent error messages.
  88. const char* const dbgName = "THIS_IS_A_TEST";
  89. FILE* f = fopen(FileName_, "w+");
  90. UNIT_ASSERT(f);
  91. {
  92. TFileMap mappedFile(f, TFileMap::oRdWr, dbgName);
  93. bool gotException = false;
  94. try {
  95. // trying to map an empty file to force an exception and check the message
  96. mappedFile.Map(0, 1000);
  97. } catch (const yexception& e) {
  98. gotException = true;
  99. UNIT_ASSERT_STRING_CONTAINS(e.what(), dbgName);
  100. }
  101. UNIT_ASSERT(gotException);
  102. }
  103. fclose(f);
  104. NFs::Remove(FileName_);
  105. }
  106. #if defined(_asan_enabled_) || defined(_msan_enabled_)
  107. // setrlimit incompatible with asan runtime
  108. #elif defined(_cygwin_)
  109. // cygwin is not real unix :(
  110. #else
  111. Y_UNIT_TEST(TestNotGreedy) {
  112. unsigned page[4096 / sizeof(unsigned)];
  113. #if defined(_unix_)
  114. // Temporary limit allowed virtual memory size to 1Gb
  115. struct rlimit rlim;
  116. if (getrlimit(RLIMIT_AS, &rlim)) {
  117. throw TSystemError() << "Cannot get rlimit for virtual memory";
  118. }
  119. rlim_t Limit = 1 * 1024 * 1024 * 1024;
  120. if (rlim.rlim_cur > Limit) {
  121. rlim.rlim_cur = Limit;
  122. if (setrlimit(RLIMIT_AS, &rlim)) {
  123. throw TSystemError() << "Cannot set rlimit for virtual memory to 1Gb";
  124. }
  125. }
  126. #endif
  127. // Make a 128M test file
  128. try {
  129. TFile file(FileName_, CreateAlways | WrOnly);
  130. for (unsigned pages = 128 * 1024 * 1024 / sizeof(page), i = 0; pages--; i++) {
  131. std::fill(page, page + sizeof(page) / sizeof(*page), i);
  132. file.Write(page, sizeof(page));
  133. }
  134. file.Close();
  135. // Make 16 maps of our file, which would require 16*128M = 2Gb and exceed our 1Gb limit
  136. TVector<THolder<TFileMap>> maps;
  137. for (int i = 0; i < 16; ++i) {
  138. maps.emplace_back(MakeHolder<TFileMap>(FileName_, TMemoryMapCommon::oRdOnly | TMemoryMapCommon::oNotGreedy));
  139. maps.back()->Map(i * sizeof(page), sizeof(page));
  140. }
  141. // Oh, good, we're not dead yet
  142. for (int i = 0; i < 16; ++i) {
  143. TFileMap& map = *maps[i];
  144. UNIT_ASSERT_EQUAL(map.Length(), 128 * 1024 * 1024);
  145. UNIT_ASSERT_EQUAL(map.MappedSize(), sizeof(page));
  146. const int* mappedPage = (const int*)map.Ptr();
  147. for (size_t j = 0; j < sizeof(page) / sizeof(*page); ++j) {
  148. UNIT_ASSERT_EQUAL(mappedPage[j], i);
  149. }
  150. }
  151. #if defined(_unix_)
  152. // Restore limits and cleanup
  153. rlim.rlim_cur = rlim.rlim_max;
  154. if (setrlimit(RLIMIT_AS, &rlim)) {
  155. throw TSystemError() << "Cannot restore rlimit for virtual memory";
  156. }
  157. #endif
  158. maps.clear();
  159. NFs::Remove(FileName_);
  160. } catch (...) {
  161. // TODO: RAII'ize all this stuff
  162. #if defined(_unix_)
  163. rlim.rlim_cur = rlim.rlim_max;
  164. if (setrlimit(RLIMIT_AS, &rlim)) {
  165. throw TSystemError() << "Cannot restore rlimit for virtual memory";
  166. }
  167. #endif
  168. NFs::Remove(FileName_);
  169. throw;
  170. }
  171. }
  172. #endif
  173. Y_UNIT_TEST(TestFileMappedArray) {
  174. {
  175. TFileMappedArray<ui32> mappedArray;
  176. ui32 data[] = {123, 456, 789, 10};
  177. size_t sz = sizeof(data) / sizeof(data[0]);
  178. TFile file(FileName_, CreateAlways | WrOnly);
  179. file.Write(static_cast<void*>(data), sizeof(data));
  180. file.Close();
  181. mappedArray.Init(FileName_);
  182. // actual test begin
  183. UNIT_ASSERT(mappedArray.Size() == sz);
  184. for (size_t i = 0; i < sz; ++i) {
  185. UNIT_ASSERT(mappedArray[i] == data[i]);
  186. }
  187. UNIT_ASSERT(mappedArray.GetAt(mappedArray.Size()) == 0);
  188. UNIT_ASSERT(*mappedArray.Begin() == data[0]);
  189. UNIT_ASSERT(size_t(mappedArray.End() - mappedArray.Begin()) == sz);
  190. UNIT_ASSERT(!mappedArray.Empty());
  191. // actual test end
  192. mappedArray.Term();
  193. // Init array via file mapping
  194. TFileMap fileMap(FileName_);
  195. fileMap.Map(0, fileMap.Length());
  196. mappedArray.Init(fileMap);
  197. // actual test begin
  198. UNIT_ASSERT(mappedArray.Size() == sz);
  199. for (size_t i = 0; i < sz; ++i) {
  200. UNIT_ASSERT(mappedArray[i] == data[i]);
  201. }
  202. UNIT_ASSERT(mappedArray.GetAt(mappedArray.Size()) == 0);
  203. UNIT_ASSERT(*mappedArray.Begin() == data[0]);
  204. UNIT_ASSERT(size_t(mappedArray.End() - mappedArray.Begin()) == sz);
  205. UNIT_ASSERT(!mappedArray.Empty());
  206. // actual test end
  207. file = TFile(FileName_, WrOnly);
  208. file.Seek(0, sEnd);
  209. file.Write("x", 1);
  210. file.Close();
  211. bool caught = false;
  212. try {
  213. mappedArray.Init(FileName_);
  214. } catch (const yexception&) {
  215. caught = true;
  216. }
  217. UNIT_ASSERT(caught);
  218. }
  219. NFs::Remove(FileName_);
  220. }
  221. Y_UNIT_TEST(TestMappedArray) {
  222. ui32 sz = 10;
  223. TMappedArray<ui32> mappedArray;
  224. ui32* ptr = mappedArray.Create(sz);
  225. UNIT_ASSERT(ptr != nullptr);
  226. UNIT_ASSERT(mappedArray.size() == sz);
  227. UNIT_ASSERT(mappedArray.begin() + sz == mappedArray.end());
  228. for (size_t i = 0; i < sz; ++i) {
  229. mappedArray[i] = (ui32)i;
  230. }
  231. for (size_t i = 0; i < sz; ++i) {
  232. UNIT_ASSERT(mappedArray[i] == i);
  233. }
  234. TMappedArray<ui32> mappedArray2(1000);
  235. mappedArray.swap(mappedArray2);
  236. UNIT_ASSERT(mappedArray.size() == 1000 && mappedArray2.size() == sz);
  237. }
  238. Y_UNIT_TEST(TestMemoryMap) {
  239. TFile file(FileName_, CreateAlways | WrOnly);
  240. file.Close();
  241. FILE* f = fopen(FileName_, "rb");
  242. UNIT_ASSERT(f != nullptr);
  243. try {
  244. TMemoryMap mappedMem(f);
  245. mappedMem.Map(mappedMem.Length() / 2, mappedMem.Length() + 100); // overflow
  246. UNIT_ASSERT(0); // should not go here
  247. } catch (yexception& exc) {
  248. TString text = exc.what(); // exception should contain failed file name
  249. UNIT_ASSERT(text.find(TMemoryMapCommon::UnknownFileName()) != TString::npos);
  250. fclose(f);
  251. }
  252. TFile fileForMap(FileName_, OpenExisting);
  253. try {
  254. TMemoryMap mappedMem(fileForMap);
  255. mappedMem.Map(mappedMem.Length() / 2, mappedMem.Length() + 100); // overflow
  256. UNIT_ASSERT(0); // should not go here
  257. } catch (yexception& exc) {
  258. TString text = exc.what(); // exception should contain failed file name
  259. UNIT_ASSERT(text.find(FileName_) != TString::npos);
  260. }
  261. NFs::Remove(FileName_);
  262. }
  263. Y_UNIT_TEST(TestMemoryMapIsWritable) {
  264. TFile file(FileName_, CreateAlways | WrOnly);
  265. file.Close();
  266. {
  267. TMemoryMap mappedMem(FileName_, TMemoryMap::oRdOnly);
  268. UNIT_ASSERT(!mappedMem.IsWritable());
  269. }
  270. {
  271. TMemoryMap mappedMem(FileName_, TMemoryMap::oRdWr);
  272. UNIT_ASSERT(mappedMem.IsWritable());
  273. }
  274. NFs::Remove(FileName_);
  275. }
  276. Y_UNIT_TEST(TestFileMapIsWritable) {
  277. TFile file(FileName_, CreateAlways | WrOnly);
  278. file.Close();
  279. {
  280. TMemoryMap mappedMem(FileName_, TMemoryMap::oRdOnly);
  281. TFileMap fileMap(mappedMem);
  282. UNIT_ASSERT(!fileMap.IsWritable());
  283. }
  284. {
  285. TMemoryMap mappedMem(FileName_, TMemoryMap::oRdWr);
  286. TFileMap fileMap(mappedMem);
  287. UNIT_ASSERT(fileMap.IsWritable());
  288. }
  289. {
  290. TFileMap fileMap(FileName_, TFileMap::oRdOnly);
  291. UNIT_ASSERT(!fileMap.IsWritable());
  292. }
  293. {
  294. TFileMap fileMap(FileName_, TFileMap::oRdWr);
  295. UNIT_ASSERT(fileMap.IsWritable());
  296. }
  297. NFs::Remove(FileName_);
  298. }
  299. } // Y_UNIT_TEST_SUITE(TFileMapTest)