暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

MemoryMappedFile.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. #include "il2cpp-config.h"
  2. #if !IL2CPP_USE_GENERIC_MEMORY_MAPPED_FILE && IL2CPP_TARGET_WINDOWS
  3. #include <map>
  4. #include <limits>
  5. #include "WindowsHelpers.h"
  6. #include "os/MemoryMappedFile.h"
  7. #include "utils/StringUtils.h"
  8. namespace il2cpp
  9. {
  10. namespace os
  11. {
  12. static DWORD ConvertMappedFileAccessToWindowsPageAccess(MemoryMappedFileAccess access)
  13. {
  14. switch (access)
  15. {
  16. case MMAP_FILE_ACCESS_READ:
  17. return PAGE_READONLY;
  18. case MMAP_FILE_ACCESS_READ_WRITE:
  19. return PAGE_READWRITE;
  20. case MMAP_FILE_ACCESS_COPY_ON_WRITE:
  21. return PAGE_WRITECOPY;
  22. case MMAP_FILE_ACCESS_READ_EXECUTE:
  23. return PAGE_EXECUTE_READ;
  24. case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE:
  25. return PAGE_EXECUTE_READWRITE;
  26. default:
  27. IL2CPP_ASSERT("unknown MemoryMappedFileAccess");
  28. }
  29. return MMAP_FILE_ACCESS_READ;
  30. }
  31. static int ConvertMappedFileAccessToWindowsFileAccess(MemoryMappedFileAccess access)
  32. {
  33. switch (access)
  34. {
  35. case MMAP_FILE_ACCESS_READ:
  36. return FILE_MAP_READ;
  37. case MMAP_FILE_ACCESS_WRITE:
  38. return FILE_MAP_WRITE;
  39. case MMAP_FILE_ACCESS_READ_WRITE:
  40. return FILE_MAP_READ | FILE_MAP_WRITE;
  41. case MMAP_FILE_ACCESS_COPY_ON_WRITE:
  42. return FILE_MAP_COPY;
  43. case MMAP_FILE_ACCESS_READ_EXECUTE:
  44. return FILE_MAP_EXECUTE | FILE_MAP_READ;
  45. case MMAP_FILE_ACCESS_READ_WRITE_EXECUTE:
  46. return FILE_MAP_EXECUTE | FILE_MAP_READ | FILE_MAP_WRITE;
  47. default:
  48. IL2CPP_ASSERT("unknown MemoryMappedFileAccess");
  49. }
  50. return MMAP_FILE_ACCESS_READ;
  51. }
  52. static MemoryMappedFileError ConvertWindowsErrorToMemoryMappedFileError(DWORD error, MemoryMappedFileError defaultError)
  53. {
  54. switch (error)
  55. {
  56. case ERROR_FILE_NOT_FOUND:
  57. return FILE_NOT_FOUND;
  58. case ERROR_FILE_EXISTS:
  59. case ERROR_ALREADY_EXISTS:
  60. return FILE_ALREADY_EXISTS;
  61. case ERROR_ACCESS_DENIED:
  62. return ACCESS_DENIED;
  63. }
  64. return defaultError;
  65. }
  66. void MemoryMappedFile::AllocateStaticData()
  67. {
  68. }
  69. void MemoryMappedFile::FreeStaticData()
  70. {
  71. }
  72. FileHandle* MemoryMappedFile::Create(FileHandle* file, const char* mapName, int32_t mode, int64_t *capacity, MemoryMappedFileAccess access, int32_t options, MemoryMappedFileError* error)
  73. {
  74. HANDLE result = NULL;
  75. HANDLE handle = file != NULL ? file : INVALID_HANDLE_VALUE;
  76. if (handle == INVALID_HANDLE_VALUE)
  77. {
  78. if (*capacity <= 0 && mode != os::FILE_MODE_OPEN)
  79. {
  80. *error = CAPACITY_MUST_BE_POSITIVE;
  81. return NULL;
  82. }
  83. #if IL2CPP_SIZEOF_VOID_P == 4
  84. if (*capacity > UINT32_MAX)
  85. {
  86. *error = CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE;
  87. return NULL;
  88. }
  89. #endif
  90. if (!(mode == FILE_MODE_CREATE_NEW || mode == FILE_MODE_OPEN_OR_CREATE || mode == FILE_MODE_OPEN))
  91. {
  92. *error = INVALID_FILE_MODE;
  93. return NULL;
  94. }
  95. }
  96. else
  97. {
  98. FILE_STANDARD_INFO info;
  99. if (!GetFileInformationByHandleEx(handle, FileStandardInfo, &info, sizeof(FILE_STANDARD_INFO)))
  100. {
  101. *error = ConvertWindowsErrorToMemoryMappedFileError(GetLastError(), COULD_NOT_OPEN);
  102. return NULL;
  103. }
  104. if (*capacity == 0)
  105. {
  106. if (info.EndOfFile.QuadPart == 0)
  107. {
  108. *error = CAPACITY_SMALLER_THAN_FILE_SIZE;
  109. return NULL;
  110. }
  111. }
  112. else if (*capacity < info.EndOfFile.QuadPart)
  113. {
  114. *error = CAPACITY_SMALLER_THAN_FILE_SIZE;
  115. return NULL;
  116. }
  117. }
  118. UTF16String utf16MapNameString = mapName != NULL ? il2cpp::utils::StringUtils::Utf8ToUtf16(mapName) : UTF16String();
  119. LPCWSTR utf16MapName = mapName != NULL ? utf16MapNameString.c_str() : NULL;
  120. if (mode == FILE_MODE_CREATE_NEW || handle != INVALID_HANDLE_VALUE)
  121. {
  122. result = CreateFileMapping(handle, NULL, ConvertMappedFileAccessToWindowsPageAccess(access) | options, (DWORD)(((uint64_t)*capacity) >> 32), (DWORD)*capacity, utf16MapName);
  123. if (result && GetLastError() == ERROR_ALREADY_EXISTS)
  124. {
  125. CloseHandle(result);
  126. result = NULL;
  127. *error = FILE_ALREADY_EXISTS;
  128. }
  129. else if (!result && GetLastError() != NO_ERROR)
  130. {
  131. *error = ConvertWindowsErrorToMemoryMappedFileError(GetLastError(), COULD_NOT_OPEN);
  132. }
  133. }
  134. else if (mode == FILE_MODE_OPEN || mode == FILE_MODE_OPEN_OR_CREATE && access == MMAP_FILE_ACCESS_WRITE)
  135. {
  136. result = OpenFileMappingW(ConvertMappedFileAccessToWindowsFileAccess(access), FALSE, utf16MapName);
  137. if (!result)
  138. {
  139. if (mode == FILE_MODE_OPEN_OR_CREATE && GetLastError() == ERROR_FILE_NOT_FOUND)
  140. {
  141. *error = INVALID_FILE_MODE;
  142. }
  143. else
  144. {
  145. *error = ConvertWindowsErrorToMemoryMappedFileError(GetLastError(), COULD_NOT_OPEN);
  146. }
  147. }
  148. }
  149. else if (mode == FILE_MODE_OPEN_OR_CREATE)
  150. {
  151. // This replicates how CoreFX does MemoryMappedFile.CreateOrOpen ().
  152. /// Try to open the file if it exists -- this requires a bit more work. Loop until we can
  153. /// either create or open a memory mapped file up to a timeout. CreateFileMapping may fail
  154. /// if the file exists and we have non-null security attributes, in which case we need to
  155. /// use OpenFileMapping. But, there exists a race condition because the memory mapped file
  156. /// may have closed between the two calls -- hence the loop.
  157. ///
  158. /// The retry/timeout logic increases the wait time each pass through the loop and times
  159. /// out in approximately 1.4 minutes. If after retrying, a MMF handle still hasn't been opened,
  160. /// throw an InvalidOperationException.
  161. uint32_t waitRetries = 14; //((2^13)-1)*10ms == approximately 1.4mins
  162. uint32_t waitSleep = 0;
  163. while (waitRetries > 0)
  164. {
  165. result = CreateFileMapping(handle, NULL, ConvertMappedFileAccessToWindowsPageAccess(access) | options, (DWORD)(((uint64_t)*capacity) >> 32), (DWORD)*capacity, utf16MapName);
  166. if (result)
  167. break;
  168. if (GetLastError() != ERROR_ACCESS_DENIED)
  169. {
  170. *error = ConvertWindowsErrorToMemoryMappedFileError(GetLastError(), COULD_NOT_OPEN);
  171. break;
  172. }
  173. result = OpenFileMapping(ConvertMappedFileAccessToWindowsFileAccess(access), FALSE, utf16MapName);
  174. if (result)
  175. break;
  176. if (GetLastError() != ERROR_FILE_NOT_FOUND)
  177. {
  178. *error = ConvertWindowsErrorToMemoryMappedFileError(GetLastError(), COULD_NOT_OPEN);
  179. break;
  180. }
  181. // increase wait time
  182. --waitRetries;
  183. if (waitSleep == 0)
  184. {
  185. waitSleep = 10;
  186. }
  187. else
  188. {
  189. Sleep(waitSleep);
  190. waitSleep *= 2;
  191. }
  192. }
  193. if (!result)
  194. *error = COULD_NOT_OPEN;
  195. }
  196. return (os::FileHandle*)result;
  197. }
  198. MemoryMappedFile::MemoryMappedFileHandle MemoryMappedFile::View(FileHandle* mappedFileHandle, int64_t* length, int64_t offset, MemoryMappedFileAccess access, int64_t* actualOffset, MemoryMappedFileError* error)
  199. {
  200. IL2CPP_ASSERT(actualOffset != NULL);
  201. IL2CPP_ASSERT(offset <= std::numeric_limits<DWORD>::max());
  202. IL2CPP_ASSERT(*length <= std::numeric_limits<DWORD>::max());
  203. static DWORD allocationGranularity = 0;
  204. if (allocationGranularity == 0)
  205. {
  206. SYSTEM_INFO info;
  207. GetSystemInfo(&info);
  208. allocationGranularity = info.dwAllocationGranularity;
  209. }
  210. int64_t extraMemNeeded = offset % allocationGranularity;
  211. uint64_t newOffset = offset - extraMemNeeded;
  212. uint64_t nativeSize = (*length != 0) ? *length + extraMemNeeded : 0;
  213. *actualOffset = newOffset;
  214. void* address = MapViewOfFile((HANDLE)mappedFileHandle, ConvertMappedFileAccessToWindowsFileAccess(access), (DWORD)(newOffset >> 32), (DWORD)newOffset, (SIZE_T)nativeSize);
  215. if (address == NULL)
  216. {
  217. if (error != NULL)
  218. *error = ConvertWindowsErrorToMemoryMappedFileError(GetLastError(), COULD_NOT_MAP_MEMORY);
  219. CloseHandle(mappedFileHandle);
  220. return NULL;
  221. }
  222. // Query the view for its size and allocation type
  223. MEMORY_BASIC_INFORMATION viewInfo;
  224. VirtualQuery(address, &viewInfo, sizeof(MEMORY_BASIC_INFORMATION));
  225. uint64_t viewSize = (uint64_t)viewInfo.RegionSize;
  226. // Allocate the pages if we were using the MemoryMappedFileOptions.DelayAllocatePages option
  227. // OR check if the allocated view size is smaller than the expected native size
  228. // If multiple overlapping views are created over the file mapping object, the pages in a given region
  229. // could have different attributes(MEM_RESERVE OR MEM_COMMIT) as MapViewOfFile preserves coherence between
  230. // views created on a mapping object backed by same file.
  231. // In which case, the viewSize will be smaller than nativeSize required and viewState could be MEM_COMMIT
  232. // but more pages may need to be committed in the region.
  233. // This is because, VirtualQuery function(that internally invokes VirtualQueryEx function) returns the attributes
  234. // and size of the region of pages with matching attributes starting from base address.
  235. // VirtualQueryEx: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366907(v=vs.85).aspx
  236. if (((viewInfo.State & MEM_RESERVE) != 0) || viewSize < (uint64_t)nativeSize)
  237. {
  238. void *tempAddress = VirtualAlloc(address, (SIZE_T)(nativeSize != 0 ? nativeSize : viewSize), MEM_COMMIT, ConvertMappedFileAccessToWindowsPageAccess(access));
  239. if (!tempAddress)
  240. {
  241. if (error != NULL)
  242. *error = ConvertWindowsErrorToMemoryMappedFileError(GetLastError(), COULD_NOT_MAP_MEMORY);
  243. return NULL;
  244. }
  245. // again query the view for its new size
  246. VirtualQuery(address, &viewInfo, sizeof(MEMORY_BASIC_INFORMATION));
  247. viewSize = (uint64_t)viewInfo.RegionSize;
  248. }
  249. if (*length == 0)
  250. *length = viewSize - extraMemNeeded;
  251. return address;
  252. }
  253. void MemoryMappedFile::Flush(MemoryMappedFileHandle memoryMappedFileData, int64_t length)
  254. {
  255. BOOL success = FlushViewOfFile(memoryMappedFileData, (SIZE_T)length);
  256. if (success)
  257. return;
  258. // This replicates how CoreFX does MemoryMappedView.Flush ().
  259. // It is a known issue within the NTFS transaction log system that
  260. // causes FlushViewOfFile to intermittently fail with ERROR_LOCK_VIOLATION
  261. // As a workaround, we catch this particular error and retry the flush operation
  262. // a few milliseconds later. If it does not work, we give it a few more tries with
  263. // increasing intervals. Eventually, however, we need to give up. In ad-hoc tests
  264. // this strategy successfully flushed the view after no more than 3 retries.
  265. if (GetLastError() != ERROR_LOCK_VIOLATION)
  266. // TODO: Propagate error to caller
  267. return;
  268. // These control the retry behaviour when lock violation errors occur during Flush:
  269. const int MAX_FLUSH_WAITS = 15; // must be <=30
  270. const int MAX_FLUSH_RETIRES_PER_WAIT = 20;
  271. for (int w = 0; w < MAX_FLUSH_WAITS; w++)
  272. {
  273. int pause = (1 << w); // MaxFlushRetries should never be over 30
  274. Sleep(pause);
  275. for (int r = 0; r < MAX_FLUSH_RETIRES_PER_WAIT; r++)
  276. {
  277. if (FlushViewOfFile(memoryMappedFileData, (SIZE_T)length))
  278. return;
  279. if (GetLastError() != ERROR_LOCK_VIOLATION)
  280. // TODO: Propagate error to caller
  281. return;
  282. SwitchToThread();
  283. }
  284. }
  285. // We got to here, so there was no success
  286. IL2CPP_ASSERT(false);
  287. }
  288. bool MemoryMappedFile::UnmapView(MemoryMappedFileHandle memoryMappedFileData, int64_t length)
  289. {
  290. if (memoryMappedFileData != NULL)
  291. {
  292. BOOL success = UnmapViewOfFile(memoryMappedFileData);
  293. IL2CPP_ASSERT(success);
  294. if (!success)
  295. return false;
  296. }
  297. return true;
  298. }
  299. bool MemoryMappedFile::Close(FileHandle* file)
  300. {
  301. BOOL success = CloseHandle(file);
  302. IL2CPP_ASSERT(success);
  303. return success;
  304. }
  305. void MemoryMappedFile::ConfigureHandleInheritability(FileHandle* file, bool inheritability)
  306. {
  307. #if IL2CPP_TARGET_WINDOWS_DESKTOP
  308. BOOL success = SetHandleInformation((HANDLE)file, HANDLE_FLAG_INHERIT, inheritability ? HANDLE_FLAG_INHERIT : 0);
  309. IL2CPP_ASSERT(success);
  310. #endif
  311. }
  312. bool MemoryMappedFile::OwnsDuplicatedFileHandle(FileHandle* file)
  313. {
  314. return true;
  315. }
  316. }
  317. }
  318. #endif