filemap.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. #include "info.h"
  2. #include "madvise.h"
  3. #include "defaults.h"
  4. #include "hi_lo.h"
  5. #include <util/generic/yexception.h>
  6. #include <util/generic/singleton.h>
  7. #if defined(_win_)
  8. #include "winint.h"
  9. #elif defined(_unix_)
  10. #include <sys/types.h>
  11. #include <sys/mman.h>
  12. #if !defined(_linux_) && !defined(_emscripten_)
  13. #ifdef MAP_POPULATE
  14. #error unlisted platform supporting MAP_POPULATE
  15. #endif
  16. #define MAP_POPULATE 0
  17. #endif
  18. #if !defined(_freebsd_)
  19. #ifdef MAP_NOCORE
  20. #error unlisted platform supporting MAP_NOCORE
  21. #endif
  22. #define MAP_NOCORE 0
  23. #endif
  24. #else
  25. #error todo
  26. #endif
  27. #include <util/generic/utility.h>
  28. #include <util/system/sanitizers.h>
  29. #include "filemap.h"
  30. #undef PAGE_SIZE
  31. #undef GRANULARITY
  32. #ifdef _win_
  33. #define MAP_FAILED ((void*)(LONG_PTR)-1)
  34. #endif
  35. namespace {
  36. struct TSysInfo {
  37. inline TSysInfo()
  38. : GRANULARITY_(CalcGranularity())
  39. , PAGE_SIZE_(NSystemInfo::GetPageSize())
  40. {
  41. }
  42. static inline const TSysInfo& Instance() {
  43. return *Singleton<TSysInfo>();
  44. }
  45. static inline size_t CalcGranularity() noexcept {
  46. #if defined(_win_)
  47. SYSTEM_INFO sysInfo;
  48. GetSystemInfo(&sysInfo);
  49. return sysInfo.dwAllocationGranularity;
  50. #else
  51. return NSystemInfo::GetPageSize();
  52. #endif
  53. }
  54. const size_t GRANULARITY_;
  55. const size_t PAGE_SIZE_;
  56. };
  57. } // namespace
  58. #define GRANULARITY (TSysInfo::Instance().GRANULARITY_)
  59. #define PAGE_SIZE (TSysInfo::Instance().PAGE_SIZE_)
  60. const TString& TMemoryMapCommon::UnknownFileName() {
  61. static const TString unknownFileName = "Unknown_file_name";
  62. return unknownFileName;
  63. }
  64. static inline i64 DownToGranularity(i64 offset) noexcept {
  65. return offset & ~((i64)(GRANULARITY - 1));
  66. }
  67. #if defined(_unix_)
  68. static int ModeToMmapFlags(TMemoryMapCommon::EOpenMode mode) {
  69. int flags = MAP_NOCORE;
  70. if ((mode & TMemoryMapCommon::oAccessMask) == TMemoryMapCommon::oCopyOnWr) {
  71. flags |= MAP_PRIVATE;
  72. } else {
  73. flags |= MAP_SHARED;
  74. }
  75. if (mode & TMemoryMapCommon::oPopulate) {
  76. flags |= MAP_POPULATE;
  77. }
  78. return flags;
  79. }
  80. static int ModeToMmapProt(TMemoryMapCommon::EOpenMode mode) {
  81. int prot = PROT_READ;
  82. if ((mode & TMemoryMapCommon::oAccessMask) != TMemoryMapCommon::oRdOnly) {
  83. prot |= PROT_WRITE;
  84. }
  85. return prot;
  86. }
  87. #endif
  88. // maybe we should move this function to another .cpp file to avoid unwanted optimization?
  89. void NPrivate::Precharge(const void* data, size_t dataSize, size_t off, size_t size) {
  90. if (off > dataSize) {
  91. assert(false);
  92. return;
  93. }
  94. size_t endOff = (size == (size_t)-1 ? dataSize : off + size);
  95. if (endOff > dataSize) {
  96. assert(false);
  97. endOff = dataSize;
  98. }
  99. size = endOff - off;
  100. if (dataSize == 0 || size == 0) {
  101. return;
  102. }
  103. volatile const char *c = (const char*)data + off, *e = c + size;
  104. for (; c < e; c += 512) {
  105. *c;
  106. }
  107. }
  108. class TMemoryMap::TImpl: public TAtomicRefCount<TImpl> {
  109. public:
  110. inline void CreateMapping() {
  111. #if defined(_win_)
  112. Mapping_ = nullptr;
  113. if (Length_) {
  114. Mapping_ = CreateFileMapping(File_.GetHandle(), nullptr,
  115. (Mode_ & oAccessMask) == TFileMap::oRdWr ? PAGE_READWRITE : PAGE_READONLY,
  116. (DWORD)(Length_ >> 32), (DWORD)(Length_ & 0xFFFFFFFF), nullptr);
  117. if (Mapping_ == nullptr) {
  118. ythrow yexception() << "Can't create file mapping of '" << DbgName_ << "': " << LastSystemErrorText();
  119. }
  120. } else {
  121. Mapping_ = MAP_FAILED;
  122. }
  123. #elif defined(_unix_)
  124. if (!(Mode_ & oNotGreedy)) {
  125. PtrStart_ = mmap((caddr_t) nullptr, Length_, ModeToMmapProt(Mode_), ModeToMmapFlags(Mode_), File_.GetHandle(), 0);
  126. if ((MAP_FAILED == PtrStart_) && Length_) {
  127. ythrow yexception() << "Can't map " << (unsigned long)Length_ << " bytes of file '" << DbgName_ << "' at offset 0: " << LastSystemErrorText();
  128. }
  129. } else {
  130. PtrStart_ = nullptr;
  131. }
  132. #endif
  133. }
  134. void CheckFile() const {
  135. if (!File_.IsOpen()) {
  136. ythrow yexception() << "TMemoryMap: FILE '" << DbgName_ << "' is not open, " << LastSystemErrorText();
  137. }
  138. if (Length_ < 0) {
  139. ythrow yexception() << "'" << DbgName_ << "' is not a regular file";
  140. }
  141. }
  142. inline TImpl(FILE* f, EOpenMode om, TString dbgName)
  143. : File_(Duplicate(f))
  144. , DbgName_(std::move(dbgName))
  145. , Length_(File_.GetLength())
  146. , Mode_(om)
  147. {
  148. CheckFile();
  149. CreateMapping();
  150. }
  151. inline TImpl(const TString& name, EOpenMode om)
  152. : File_(name, (om & oRdWr) ? OpenExisting | RdWr : OpenExisting | RdOnly)
  153. , DbgName_(name)
  154. , Length_(File_.GetLength())
  155. , Mode_(om)
  156. {
  157. CheckFile();
  158. CreateMapping();
  159. }
  160. inline TImpl(const TString& name, i64 length, EOpenMode om)
  161. : File_(name, (om & oRdWr) ? OpenExisting | RdWr : OpenExisting | RdOnly)
  162. , DbgName_(name)
  163. , Length_(length)
  164. , Mode_(om)
  165. {
  166. CheckFile();
  167. if (File_.GetLength() < Length_) {
  168. File_.Resize(Length_);
  169. }
  170. CreateMapping();
  171. }
  172. inline TImpl(const TFile& file, EOpenMode om, const TString& dbgName)
  173. : File_(file)
  174. , DbgName_(File_.GetName() ? File_.GetName() : dbgName)
  175. , Length_(File_.GetLength())
  176. , Mode_(om)
  177. {
  178. CheckFile();
  179. CreateMapping();
  180. }
  181. inline bool IsOpen() const noexcept {
  182. return File_.IsOpen()
  183. #if defined(_win_)
  184. && Mapping_ != nullptr
  185. #endif
  186. ;
  187. }
  188. inline bool IsWritable() const noexcept {
  189. return (Mode_ & oRdWr || Mode_ & oCopyOnWr);
  190. }
  191. inline TMapResult Map(i64 offset, size_t size) {
  192. assert(File_.IsOpen());
  193. if (offset > Length_) {
  194. ythrow yexception() << "Can't map something at offset " << offset << " of '" << DbgName_ << "' with length " << Length_;
  195. }
  196. if (offset + (i64)size > Length_) {
  197. ythrow yexception() << "Can't map " << (unsigned long)size << " bytes at offset " << offset << " of '" << DbgName_ << "' with length " << Length_;
  198. }
  199. TMapResult result;
  200. i64 base = DownToGranularity(offset);
  201. result.Head = (i32)(offset - base);
  202. size += result.Head;
  203. #if defined(_win_)
  204. result.Ptr = MapViewOfFile(Mapping_,
  205. (Mode_ & oAccessMask) == oRdOnly ? FILE_MAP_READ : (Mode_ & oAccessMask) == oCopyOnWr ? FILE_MAP_COPY
  206. : FILE_MAP_WRITE,
  207. Hi32(base), Lo32(base), size);
  208. #else
  209. #if defined(_unix_)
  210. if (Mode_ & oNotGreedy) {
  211. #endif
  212. result.Ptr = mmap((caddr_t) nullptr, size, ModeToMmapProt(Mode_), ModeToMmapFlags(Mode_), File_.GetHandle(), base);
  213. if (result.Ptr == (char*)(-1)) {
  214. result.Ptr = nullptr;
  215. }
  216. #if defined(_unix_)
  217. } else {
  218. result.Ptr = PtrStart_ ? static_cast<caddr_t>(PtrStart_) + base : nullptr;
  219. }
  220. #endif
  221. #endif
  222. if (result.Ptr != nullptr || size == 0) { // allow map of size 0
  223. result.Size = size;
  224. } else {
  225. ythrow yexception() << "Can't map " << (unsigned long)size << " bytes at offset " << offset << " of '" << DbgName_ << "': " << LastSystemErrorText();
  226. }
  227. NSan::Unpoison(result.Ptr, result.Size);
  228. if (Mode_ & oPrecharge) {
  229. NPrivate::Precharge(result.Ptr, result.Size, 0, result.Size);
  230. }
  231. return result;
  232. }
  233. #if defined(_win_)
  234. inline bool Unmap(void* ptr, size_t) {
  235. return ::UnmapViewOfFile(ptr) != FALSE;
  236. }
  237. #else
  238. inline bool Unmap(void* ptr, size_t size) {
  239. #if defined(_unix_)
  240. if (Mode_ & oNotGreedy) {
  241. #endif
  242. return size == 0 || ::munmap(static_cast<caddr_t>(ptr), size) == 0;
  243. #if defined(_unix_)
  244. } else {
  245. return true;
  246. }
  247. #endif
  248. }
  249. #endif
  250. void SetSequential() {
  251. #if defined(_unix_)
  252. if (!(Mode_ & oNotGreedy) && Length_) {
  253. MadviseSequentialAccess(PtrStart_, Length_);
  254. }
  255. #endif
  256. }
  257. void Evict(void* ptr, size_t len) {
  258. MadviseEvict(ptr, len);
  259. }
  260. void Evict() {
  261. #if defined(_unix_)
  262. // Evict(PtrStart_, Length_);
  263. #endif
  264. }
  265. inline ~TImpl() {
  266. #if defined(_win_)
  267. if (Mapping_) {
  268. ::CloseHandle(Mapping_); // != FALSE
  269. Mapping_ = nullptr;
  270. }
  271. #elif defined(_unix_)
  272. if (PtrStart_) {
  273. munmap((caddr_t)PtrStart_, Length_);
  274. }
  275. #endif
  276. }
  277. inline i64 Length() const noexcept {
  278. return Length_;
  279. }
  280. inline TFile GetFile() const noexcept {
  281. return File_;
  282. }
  283. inline TString GetDbgName() const {
  284. return DbgName_;
  285. }
  286. inline EOpenMode GetMode() const noexcept {
  287. return Mode_;
  288. }
  289. private:
  290. TFile File_;
  291. TString DbgName_; // This string is never used to actually open a file, only in exceptions
  292. i64 Length_;
  293. EOpenMode Mode_;
  294. #if defined(_win_)
  295. void* Mapping_;
  296. #elif defined(_unix_)
  297. void* PtrStart_;
  298. #endif
  299. };
  300. TMemoryMap::TMemoryMap(const TString& name)
  301. : Impl_(new TImpl(name, EOpenModeFlag::oRdOnly))
  302. {
  303. }
  304. TMemoryMap::TMemoryMap(const TString& name, EOpenMode om)
  305. : Impl_(new TImpl(name, om))
  306. {
  307. }
  308. TMemoryMap::TMemoryMap(const TString& name, i64 length, EOpenMode om)
  309. : Impl_(new TImpl(name, length, om))
  310. {
  311. }
  312. TMemoryMap::TMemoryMap(FILE* f, TString dbgName)
  313. : Impl_(new TImpl(f, EOpenModeFlag::oRdOnly, std::move(dbgName)))
  314. {
  315. }
  316. TMemoryMap::TMemoryMap(FILE* f, EOpenMode om, TString dbgName)
  317. : Impl_(new TImpl(f, om, std::move(dbgName)))
  318. {
  319. }
  320. TMemoryMap::TMemoryMap(const TFile& file, const TString& dbgName)
  321. : Impl_(new TImpl(file, EOpenModeFlag::oRdOnly, dbgName))
  322. {
  323. }
  324. TMemoryMap::TMemoryMap(const TFile& file, EOpenMode om, const TString& dbgName)
  325. : Impl_(new TImpl(file, om, dbgName))
  326. {
  327. }
  328. TMemoryMap::~TMemoryMap() = default;
  329. TMemoryMap::TMapResult TMemoryMap::Map(i64 offset, size_t size) {
  330. return Impl_->Map(offset, size);
  331. }
  332. bool TMemoryMap::Unmap(void* ptr, size_t size) {
  333. return Impl_->Unmap(ptr, size);
  334. }
  335. bool TMemoryMap::Unmap(TMapResult region) {
  336. return Unmap(region.Ptr, region.Size);
  337. }
  338. void TMemoryMap::ResizeAndReset(i64 size) {
  339. EOpenMode om = Impl_->GetMode();
  340. TFile file = GetFile();
  341. file.Resize(size);
  342. Impl_.Reset(new TImpl(file, om, Impl_->GetDbgName()));
  343. }
  344. TMemoryMap::TMapResult TMemoryMap::ResizeAndRemap(i64 offset, size_t size) {
  345. ResizeAndReset(offset + (i64)size);
  346. return Map(offset, size);
  347. }
  348. void TMemoryMap::SetSequential() {
  349. Impl_->SetSequential();
  350. }
  351. void TMemoryMap::Evict(void* ptr, size_t len) {
  352. Impl_->Evict(ptr, len);
  353. }
  354. void TMemoryMap::Evict() {
  355. Impl_->Evict();
  356. }
  357. i64 TMemoryMap::Length() const noexcept {
  358. return Impl_->Length();
  359. }
  360. bool TMemoryMap::IsOpen() const noexcept {
  361. return Impl_->IsOpen();
  362. }
  363. bool TMemoryMap::IsWritable() const noexcept {
  364. return Impl_->IsWritable();
  365. }
  366. TMemoryMap::EOpenMode TMemoryMap::GetMode() const noexcept {
  367. return Impl_->GetMode();
  368. }
  369. TFile TMemoryMap::GetFile() const noexcept {
  370. return Impl_->GetFile();
  371. }
  372. TFileMap::TFileMap(const TMemoryMap& map) noexcept
  373. : Map_(map)
  374. {
  375. }
  376. TFileMap::TFileMap(const TString& name)
  377. : Map_(name)
  378. {
  379. }
  380. TFileMap::TFileMap(const TString& name, EOpenMode om)
  381. : Map_(name, om)
  382. {
  383. }
  384. TFileMap::TFileMap(const TString& name, i64 length, EOpenMode om)
  385. : Map_(name, length, om)
  386. {
  387. }
  388. TFileMap::TFileMap(FILE* f, EOpenMode om, TString dbgName)
  389. : Map_(f, om, std::move(dbgName))
  390. {
  391. }
  392. TFileMap::TFileMap(const TFile& file, EOpenMode om, const TString& dbgName)
  393. : Map_(file, om, dbgName)
  394. {
  395. }
  396. TFileMap::TFileMap(const TFileMap& fm) noexcept
  397. : Map_(fm.Map_)
  398. {
  399. }
  400. void TFileMap::Flush(void* ptr, size_t size, bool sync) {
  401. Y_ASSERT(ptr >= Ptr());
  402. Y_ASSERT(static_cast<char*>(ptr) + size <= static_cast<char*>(Ptr()) + MappedSize());
  403. if (!Region_.IsMapped()) {
  404. return;
  405. }
  406. #if defined(_win_)
  407. if (sync) {
  408. FlushViewOfFile(ptr, size);
  409. }
  410. #else
  411. msync(ptr, size, sync ? MS_SYNC : MS_ASYNC);
  412. #endif
  413. }
  414. TFileMap::TMapResult TFileMap::Map(i64 offset, size_t size) {
  415. Unmap();
  416. Region_ = Map_.Map(offset, size);
  417. return Region_;
  418. }
  419. TFileMap::TMapResult TFileMap::ResizeAndRemap(i64 offset, size_t size) {
  420. // explicit Unmap() is required because in oNotGreedy mode the Map_ object doesn't own the mapped area
  421. Unmap();
  422. Region_ = Map_.ResizeAndRemap(offset, size);
  423. return Region_;
  424. }
  425. void TFileMap::Unmap() {
  426. if (!Region_.IsMapped()) {
  427. return;
  428. }
  429. if (Map_.Unmap(Region_)) {
  430. Region_.Reset();
  431. } else {
  432. ythrow yexception() << "can't unmap file";
  433. }
  434. }
  435. TFileMap::~TFileMap() {
  436. try {
  437. // explicit Unmap() is required because in oNotGreedy mode the Map_ object doesn't own the mapped area
  438. Unmap();
  439. } catch (...) {
  440. // ¯\_(ツ)_/¯
  441. }
  442. }
  443. void TFileMap::Precharge(size_t pos, size_t size) const {
  444. NPrivate::Precharge(Ptr(), MappedSize(), pos, size);
  445. }
  446. TMappedAllocation::TMappedAllocation(size_t size, bool shared, void* addr)
  447. : Ptr_(nullptr)
  448. , Size_(0)
  449. , Shared_(shared)
  450. #if defined(_win_)
  451. , Mapping_(nullptr)
  452. #endif
  453. {
  454. if (size != 0) {
  455. Alloc(size, addr);
  456. }
  457. }
  458. void* TMappedAllocation::Alloc(size_t size, void* addr) {
  459. assert(Ptr_ == nullptr);
  460. #if defined(_win_)
  461. (void)addr;
  462. Mapping_ = CreateFileMapping((HANDLE)-1, nullptr, PAGE_READWRITE, 0, size ? size : 1, nullptr);
  463. Ptr_ = MapViewOfFile(Mapping_, FILE_MAP_WRITE, 0, 0, size ? size : 1);
  464. #else
  465. Ptr_ = mmap(addr, size, PROT_READ | PROT_WRITE, (Shared_ ? MAP_SHARED : MAP_PRIVATE) | MAP_ANON, -1, 0);
  466. if (Ptr_ == (void*)MAP_FAILED) {
  467. Ptr_ = nullptr;
  468. }
  469. #endif
  470. if (Ptr_ != nullptr) {
  471. Size_ = size;
  472. }
  473. return Ptr_;
  474. }
  475. void TMappedAllocation::Dealloc() {
  476. if (Ptr_ == nullptr) {
  477. return;
  478. }
  479. #if defined(_win_)
  480. UnmapViewOfFile(Ptr_);
  481. CloseHandle(Mapping_);
  482. Mapping_ = nullptr;
  483. #else
  484. munmap((caddr_t)Ptr_, Size_);
  485. #endif
  486. Ptr_ = nullptr;
  487. Size_ = 0;
  488. }
  489. void TMappedAllocation::swap(TMappedAllocation& with) noexcept {
  490. DoSwap(Ptr_, with.Ptr_);
  491. DoSwap(Size_, with.Size_);
  492. #if defined(_win_)
  493. DoSwap(Mapping_, with.Mapping_);
  494. #endif
  495. }