+ g_assert (handle != NULL);
+
+ wchar_t *w_mapName = NULL;
+ HANDLE result = NULL;
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (*capacity <= 0 && mode != FILE_MODE_OPEN) {
+ *error = CAPACITY_MUST_BE_POSITIVE;
+ return NULL;
+ }
+#if SIZEOF_VOID_P == 4
+ if (*capacity > UINT32_MAX) {
+ *error = CAPACITY_LARGER_THAN_LOGICAL_ADDRESS_SPACE;
+ return NULL;
+ }
+#endif
+ if (!(mode == FILE_MODE_CREATE_NEW || mode == FILE_MODE_OPEN_OR_CREATE || mode == FILE_MODE_OPEN)) {
+ *error = INVALID_FILE_MODE;
+ return NULL;
+ }
+ } else {
+ FILE_STANDARD_INFO info;
+ if (!GetFileInformationByHandleEx ((HANDLE) handle, FileStandardInfo, &info, sizeof (FILE_STANDARD_INFO))) {
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
+ return NULL;
+ }
+ if (*capacity == 0) {
+ if (info.EndOfFile.QuadPart == 0) {
+ *error = CAPACITY_SMALLER_THAN_FILE_SIZE;
+ return NULL;
+ }
+ } else if (*capacity < info.EndOfFile.QuadPart) {
+ *error = CAPACITY_SMALLER_THAN_FILE_SIZE;
+ return NULL;
+ }
+ }
+
+ w_mapName = mapName ? mono_string_to_utf16 (mapName) : NULL;
+
+ if (mode == FILE_MODE_CREATE_NEW || handle != INVALID_HANDLE_VALUE) {
+ result = CreateFileMappingW ((HANDLE)handle, NULL, get_page_access (access) | options, (DWORD)(((guint64)*capacity) >> 32), (DWORD)*capacity, w_mapName);
+ if (result && GetLastError () == ERROR_ALREADY_EXISTS) {
+ CloseHandle (result);
+ result = NULL;
+ *error = FILE_ALREADY_EXISTS;
+ } else if (!result && GetLastError () != NO_ERROR) {
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
+ }
+ } else if (mode == FILE_MODE_OPEN || mode == FILE_MODE_OPEN_OR_CREATE && access == MMAP_FILE_ACCESS_WRITE) {
+ result = OpenFileMappingW (get_file_map_access (access), FALSE, w_mapName);
+ if (!result) {
+ if (mode == FILE_MODE_OPEN_OR_CREATE && GetLastError () == ERROR_FILE_NOT_FOUND) {
+ *error = INVALID_FILE_MODE;
+ } else {
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
+ }
+ }
+ } else if (mode == FILE_MODE_OPEN_OR_CREATE) {
+
+ // This replicates how CoreFX does MemoryMappedFile.CreateOrOpen ().
+
+ /// Try to open the file if it exists -- this requires a bit more work. Loop until we can
+ /// either create or open a memory mapped file up to a timeout. CreateFileMapping may fail
+ /// if the file exists and we have non-null security attributes, in which case we need to
+ /// use OpenFileMapping. But, there exists a race condition because the memory mapped file
+ /// may have closed between the two calls -- hence the loop.
+ ///
+ /// The retry/timeout logic increases the wait time each pass through the loop and times
+ /// out in approximately 1.4 minutes. If after retrying, a MMF handle still hasn't been opened,
+ /// throw an InvalidOperationException.
+
+ guint32 waitRetries = 14; //((2^13)-1)*10ms == approximately 1.4mins
+ guint32 waitSleep = 0;
+
+ while (waitRetries > 0) {
+ result = CreateFileMappingW ((HANDLE)handle, NULL, get_page_access (access) | options, (DWORD)(((guint64)*capacity) >> 32), (DWORD)*capacity, w_mapName);
+ if (result)
+ break;
+ if (GetLastError() != ERROR_ACCESS_DENIED) {
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
+ break;
+ }
+ result = OpenFileMappingW (get_file_map_access (access), FALSE, w_mapName);
+ if (result)
+ break;
+ if (GetLastError () != ERROR_FILE_NOT_FOUND) {
+ *error = convert_win32_error (GetLastError (), COULD_NOT_OPEN);
+ break;
+ }
+ // increase wait time
+ --waitRetries;
+ if (waitSleep == 0) {
+ waitSleep = 10;
+ } else {
+ mono_thread_info_sleep (waitSleep, NULL);
+ waitSleep *= 2;
+ }
+ }
+
+ if (!result) {
+ *error = COULD_NOT_OPEN;
+ }
+ }
+
+ if (w_mapName)
+ g_free (w_mapName);
+ return result;