mirror of
				https://github.com/pbatard/rufus.git
				synced 2024-08-14 23:57:05 +00:00 
			
		
		
		
	[checksum] improve performance by switching to async I/O
Yes!!! We are finally *much* faster than 7-zip for SHA-256, even though we are also computing MD5 and SHA-1 in parallel. Here are some averaged comparative results, against the 5.71 GB Win10_20H2_EnglishInternational_x64.iso (SHA-256 = 08535b6dd0a4311f562e301c3c344b4aefd2e69a82168426b9971d6f8cab35e1): * Windows' PowerShell Get-FileHash: 48s * 7-zip's SHA-256 : 31s * Rufus (64-bit release version) : 23s
This commit is contained in:
		
							parent
							
								
									53b014781d
								
							
						
					
					
						commit
						d4db16a9ca
					
				
					 5 changed files with 219 additions and 47 deletions
				
			
		|  | @ -388,6 +388,7 @@ | ||||||
|     <ClInclude Include="..\src\dev.h" /> |     <ClInclude Include="..\src\dev.h" /> | ||||||
|     <ClInclude Include="..\src\ui.h" /> |     <ClInclude Include="..\src\ui.h" /> | ||||||
|     <ClInclude Include="..\src\ui_data.h" /> |     <ClInclude Include="..\src\ui_data.h" /> | ||||||
|  |     <ClInclude Include="..\src\winio.h" /> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Manifest Include="..\src\rufus.manifest" /> |     <Manifest Include="..\src\rufus.manifest" /> | ||||||
|  |  | ||||||
|  | @ -167,6 +167,9 @@ | ||||||
|     <ClInclude Include="..\src\gpt_types.h"> |     <ClInclude Include="..\src\gpt_types.h"> | ||||||
|       <Filter>Header Files</Filter> |       <Filter>Header Files</Filter> | ||||||
|     </ClInclude> |     </ClInclude> | ||||||
|  |     <ClInclude Include="..\src\winio.h"> | ||||||
|  |       <Filter>Header Files</Filter> | ||||||
|  |     </ClInclude> | ||||||
|   </ItemGroup> |   </ItemGroup> | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <None Include="..\res\rufus.ico"> |     <None Include="..\res\rufus.ico"> | ||||||
|  |  | ||||||
|  | @ -5,7 +5,7 @@ | ||||||
|  * Copyright © 2004-2019 Tom St Denis |  * Copyright © 2004-2019 Tom St Denis | ||||||
|  * Copyright © 2004 g10 Code GmbH |  * Copyright © 2004 g10 Code GmbH | ||||||
|  * Copyright © 2002-2015 Wei Dai & Igor Pavlov |  * Copyright © 2002-2015 Wei Dai & Igor Pavlov | ||||||
|  * Copyright © 2015-2020 Pete Batard <pete@akeo.ie> |  * Copyright © 2015-2021 Pete Batard <pete@akeo.ie> | ||||||
|  * |  * | ||||||
|  * This program is free software: you can redistribute it and/or modify |  * This program is free software: you can redistribute it and/or modify | ||||||
|  * it under the terms of the GNU General Public License as published by |  * it under the terms of the GNU General Public License as published by | ||||||
|  | @ -62,6 +62,7 @@ | ||||||
| 
 | 
 | ||||||
| #include "db.h" | #include "db.h" | ||||||
| #include "rufus.h" | #include "rufus.h" | ||||||
|  | #include "winio.h" | ||||||
| #include "missing.h" | #include "missing.h" | ||||||
| #include "resource.h" | #include "resource.h" | ||||||
| #include "msapi_utf8.h" | #include "msapi_utf8.h" | ||||||
|  | @ -86,13 +87,17 @@ | ||||||
| #define SHA512_HASHSIZE     64 | #define SHA512_HASHSIZE     64 | ||||||
| #define MAX_HASHSIZE        SHA512_HASHSIZE | #define MAX_HASHSIZE        SHA512_HASHSIZE | ||||||
| 
 | 
 | ||||||
|  | /* Number of buffers we work with */ | ||||||
|  | #define NUM_BUFFERS         3   // 2 + 1 as a mere double buffered async I/O
 | ||||||
|  |                                 // would modify the buffer being processed.
 | ||||||
|  | 
 | ||||||
| /* Globals */ | /* Globals */ | ||||||
| char sum_str[CHECKSUM_MAX][150]; | char sum_str[CHECKSUM_MAX][150]; | ||||||
| uint32_t bufnum, sum_count[CHECKSUM_MAX] = { MD5_HASHSIZE, SHA1_HASHSIZE, SHA256_HASHSIZE, SHA512_HASHSIZE }; | uint32_t proc_bufnum, sum_count[CHECKSUM_MAX] = { MD5_HASHSIZE, SHA1_HASHSIZE, SHA256_HASHSIZE, SHA512_HASHSIZE }; | ||||||
| HANDLE data_ready[CHECKSUM_MAX] = { 0 }, thread_ready[CHECKSUM_MAX] = { 0 }; | HANDLE data_ready[CHECKSUM_MAX] = { 0 }, thread_ready[CHECKSUM_MAX] = { 0 }; | ||||||
| DWORD read_size[2]; | DWORD read_size[NUM_BUFFERS]; | ||||||
| BOOL enable_extra_hashes = FALSE; | BOOL enable_extra_hashes = FALSE; | ||||||
| uint8_t ALIGNED(64) buffer[2][BUFFER_SIZE]; | uint8_t ALIGNED(64) buffer[NUM_BUFFERS][BUFFER_SIZE]; | ||||||
| extern int default_thread_priority; | extern int default_thread_priority; | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -1095,8 +1100,8 @@ DWORD WINAPI IndividualSumThread(void* param) | ||||||
| 			uprintf("Failed to wait for event for checksum thread #%d: %s", i, WindowsErrorString()); | 			uprintf("Failed to wait for event for checksum thread #%d: %s", i, WindowsErrorString()); | ||||||
| 			return 1; | 			return 1; | ||||||
| 		} | 		} | ||||||
| 		if (read_size[bufnum] != 0) { | 		if (read_size[proc_bufnum] != 0) { | ||||||
| 			sum_write[i](&sum_ctx, buffer[bufnum], (size_t)read_size[bufnum]); | 			sum_write[i](&sum_ctx, buffer[proc_bufnum], (size_t)read_size[proc_bufnum]); | ||||||
| 			if (!SetEvent(thread_ready[i])) | 			if (!SetEvent(thread_ready[i])) | ||||||
| 				goto error; | 				goto error; | ||||||
| 		} else { | 		} else { | ||||||
|  | @ -1121,9 +1126,10 @@ DWORD WINAPI SumThread(void* param) | ||||||
| { | { | ||||||
| 	DWORD_PTR* thread_affinity = (DWORD_PTR*)param; | 	DWORD_PTR* thread_affinity = (DWORD_PTR*)param; | ||||||
| 	HANDLE sum_thread[CHECKSUM_MAX] = { NULL, NULL, NULL, NULL }; | 	HANDLE sum_thread[CHECKSUM_MAX] = { NULL, NULL, NULL, NULL }; | ||||||
| 	HANDLE h = INVALID_HANDLE_VALUE; | 	DWORD wr; | ||||||
| 	uint64_t rb; | 	VOID* fd = NULL; | ||||||
| 	int i, _bufnum, r = -1; | 	uint64_t processed_bytes; | ||||||
|  | 	int i, read_bufnum, r = -1; | ||||||
| 	int num_checksums = CHECKSUM_MAX - (enable_extra_hashes ? 0 : 1); | 	int num_checksums = CHECKSUM_MAX - (enable_extra_hashes ? 0 : 1); | ||||||
| 
 | 
 | ||||||
| 	if ((image_path == NULL) || (thread_affinity == NULL)) | 	if ((image_path == NULL) || (thread_affinity == NULL)) | ||||||
|  | @ -1158,29 +1164,54 @@ DWORD WINAPI SumThread(void* param) | ||||||
| 			SetThreadAffinityMask(sum_thread[i], thread_affinity[i+1]); | 			SetThreadAffinityMask(sum_thread[i], thread_affinity[i+1]); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	h = CreateFileU(image_path, GENERIC_READ, FILE_SHARE_READ, NULL, | 	fd = CreateFileAsync(image_path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN); | ||||||
| 		OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); | 	if (fd == NULL) { | ||||||
| 	if (h == INVALID_HANDLE_VALUE) { |  | ||||||
| 		uprintf("Could not open file: %s", WindowsErrorString()); | 		uprintf("Could not open file: %s", WindowsErrorString()); | ||||||
| 		FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; | 		FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_OPEN_FAILED; | ||||||
| 		goto out; | 		goto out; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	bufnum = 0; | 	read_bufnum = 0; | ||||||
| 	_bufnum = 0; | 	proc_bufnum = 1; | ||||||
| 	read_size[0] = 1;	// Don't trigger the first loop break
 | 	read_size[proc_bufnum] = 1;	// To avoid early loop exit
 | ||||||
| 	UpdateProgressWithInfoInit(hMainDialog, FALSE); | 	UpdateProgressWithInfoInit(hMainDialog, FALSE); | ||||||
| 	for (rb = 0; ;rb += read_size[_bufnum]) { | 
 | ||||||
| 		// Update the progress and check for cancel
 | 	// Start the initial read
 | ||||||
| 		UpdateProgressWithInfo(OP_NOOP_WITH_TASKBAR, MSG_271, rb, img_report.image_size); | 	ReadFileAsync(fd, buffer[read_bufnum], BUFFER_SIZE); | ||||||
|  | 
 | ||||||
|  | 	for (processed_bytes = 0; read_size[proc_bufnum] != 0; processed_bytes += read_size[proc_bufnum]) { | ||||||
|  | 		// 0. Update the progress and check for cancel
 | ||||||
|  | 		UpdateProgressWithInfo(OP_NOOP_WITH_TASKBAR, MSG_271, processed_bytes, img_report.image_size); | ||||||
| 		CHECK_FOR_USER_CANCEL; | 		CHECK_FOR_USER_CANCEL; | ||||||
| 
 | 
 | ||||||
| 		// Signal the threads that we have data to process
 | 		// 1. Wait for the current read operation to complete (and update the read size)
 | ||||||
| 		if (rb != 0) { | 		if ((!WaitFileAsync(fd, DRIVE_ACCESS_TIMEOUT)) || | ||||||
| 			bufnum = _bufnum; | 			(!GetSizeAsync(fd, &read_size[read_bufnum]))) { | ||||||
| 			// Toggle the read buffer
 | 			uprintf("Read error: %s", WindowsErrorString()); | ||||||
| 			_bufnum = (bufnum + 1) % 2; | 			FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; | ||||||
| 			// Signal the waiting threads
 | 			goto out; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// 2. Switch to the next reading buffer
 | ||||||
|  | 		read_bufnum = (read_bufnum + 1) % NUM_BUFFERS; | ||||||
|  | 
 | ||||||
|  | 		// 3. Launch the next asynchronous read operation
 | ||||||
|  | 		ReadFileAsync(fd, buffer[read_bufnum], BUFFER_SIZE); | ||||||
|  | 
 | ||||||
|  | 		// 4. Wait for all the sum threads to indicate that they are ready to process data
 | ||||||
|  | 		wr = WaitForMultipleObjects(num_checksums, thread_ready, TRUE, WAIT_TIME); | ||||||
|  | 		if (wr != WAIT_OBJECT_0) { | ||||||
|  | 			if (wr == STATUS_TIMEOUT) | ||||||
|  | 				SetLastError(ERROR_TIMEOUT); | ||||||
|  | 			uprintf("Checksum threads failed to signal: %s", WindowsErrorString()); | ||||||
|  | 			goto out; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// 5. Set the target buffer we want to process to the buffer we just read data into
 | ||||||
|  | 		// Note that this variable should only be updated AFTER all the threads have signalled.
 | ||||||
|  | 		proc_bufnum = (read_bufnum + NUM_BUFFERS - 1) % NUM_BUFFERS; | ||||||
|  | 
 | ||||||
|  | 		// 6. Signal the waiting threads that there is data available
 | ||||||
| 		for (i = 0; i < num_checksums; i++) { | 		for (i = 0; i < num_checksums; i++) { | ||||||
| 			if (!SetEvent(data_ready[i])) { | 			if (!SetEvent(data_ready[i])) { | ||||||
| 				uprintf("Could not signal checksum thread %d: %s", i, WindowsErrorString()); | 				uprintf("Could not signal checksum thread %d: %s", i, WindowsErrorString()); | ||||||
|  | @ -1189,24 +1220,6 @@ DWORD WINAPI SumThread(void* param) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 		// Break the loop when data has been exhausted
 |  | ||||||
| 		if (read_size[bufnum] == 0) |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		// Read data (double buffered)
 |  | ||||||
| 		if (!ReadFile(h, buffer[_bufnum], BUFFER_SIZE, &read_size[_bufnum], NULL)) { |  | ||||||
| 			FormatStatus = ERROR_SEVERITY_ERROR | FAC(FACILITY_STORAGE) | ERROR_READ_FAULT; |  | ||||||
| 			uprintf("Read error: %s", WindowsErrorString()); |  | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		// Wait for the thread to signal they are ready to process data
 |  | ||||||
| 		if (WaitForMultipleObjects(num_checksums, thread_ready, TRUE, WAIT_TIME) != WAIT_OBJECT_0) { |  | ||||||
| 			uprintf("Checksum threads failed to signal: %s", WindowsErrorString()); |  | ||||||
| 			goto out; |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// Our last event with read_size=0 signaled the threads to exit - wait for that to happen
 | 	// Our last event with read_size=0 signaled the threads to exit - wait for that to happen
 | ||||||
| 	if (WaitForMultipleObjects(num_checksums, sum_thread, TRUE, WAIT_TIME) != WAIT_OBJECT_0) { | 	if (WaitForMultipleObjects(num_checksums, sum_thread, TRUE, WAIT_TIME) != WAIT_OBJECT_0) { | ||||||
| 		uprintf("Checksum threads did not finalize: %s", WindowsErrorString()); | 		uprintf("Checksum threads did not finalize: %s", WindowsErrorString()); | ||||||
|  | @ -1232,7 +1245,7 @@ out: | ||||||
| 		safe_closehandle(data_ready[i]); | 		safe_closehandle(data_ready[i]); | ||||||
| 		safe_closehandle(thread_ready[i]); | 		safe_closehandle(thread_ready[i]); | ||||||
| 	} | 	} | ||||||
| 	safe_closehandle(h); | 	CloseFileAsync(fd); | ||||||
| 	PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); | 	PostMessage(hMainDialog, UM_FORMAT_COMPLETED, (WPARAM)FALSE, 0); | ||||||
| 	if (r == 0) | 	if (r == 0) | ||||||
| 		MyDialogBox(hMainInstance, IDD_CHECKSUM, hMainDialog, ChecksumCallback); | 		MyDialogBox(hMainInstance, IDD_CHECKSUM, hMainDialog, ChecksumCallback); | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								src/rufus.rc
									
										
									
									
									
								
							
							
						
						
									
										10
									
								
								src/rufus.rc
									
										
									
									
									
								
							|  | @ -35,7 +35,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL | ||||||
| IDD_DIALOG DIALOGEX 12, 12, 232, 326 | IDD_DIALOG DIALOGEX 12, 12, 232, 326 | ||||||
| STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | ||||||
| EXSTYLE WS_EX_ACCEPTFILES | EXSTYLE WS_EX_ACCEPTFILES | ||||||
| CAPTION "Rufus 3.14.1735" | CAPTION "Rufus 3.14.1736" | ||||||
| FONT 9, "Segoe UI Symbol", 400, 0, 0x0 | FONT 9, "Segoe UI Symbol", 400, 0, 0x0 | ||||||
| BEGIN | BEGIN | ||||||
|     LTEXT           "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP |     LTEXT           "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP | ||||||
|  | @ -397,8 +397,8 @@ END | ||||||
| // | // | ||||||
| 
 | 
 | ||||||
| VS_VERSION_INFO VERSIONINFO | VS_VERSION_INFO VERSIONINFO | ||||||
|  FILEVERSION 3,14,1735,0 |  FILEVERSION 3,14,1736,0 | ||||||
|  PRODUCTVERSION 3,14,1735,0 |  PRODUCTVERSION 3,14,1736,0 | ||||||
|  FILEFLAGSMASK 0x3fL |  FILEFLAGSMASK 0x3fL | ||||||
| #ifdef _DEBUG | #ifdef _DEBUG | ||||||
|  FILEFLAGS 0x1L |  FILEFLAGS 0x1L | ||||||
|  | @ -416,13 +416,13 @@ BEGIN | ||||||
|             VALUE "Comments", "https://rufus.ie" |             VALUE "Comments", "https://rufus.ie" | ||||||
|             VALUE "CompanyName", "Akeo Consulting" |             VALUE "CompanyName", "Akeo Consulting" | ||||||
|             VALUE "FileDescription", "Rufus" |             VALUE "FileDescription", "Rufus" | ||||||
|             VALUE "FileVersion", "3.14.1735" |             VALUE "FileVersion", "3.14.1736" | ||||||
|             VALUE "InternalName", "Rufus" |             VALUE "InternalName", "Rufus" | ||||||
|             VALUE "LegalCopyright", "© 2011-2021 Pete Batard (GPL v3)" |             VALUE "LegalCopyright", "© 2011-2021 Pete Batard (GPL v3)" | ||||||
|             VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" |             VALUE "LegalTrademarks", "https://www.gnu.org/licenses/gpl-3.0.html" | ||||||
|             VALUE "OriginalFilename", "rufus-3.14.exe" |             VALUE "OriginalFilename", "rufus-3.14.exe" | ||||||
|             VALUE "ProductName", "Rufus" |             VALUE "ProductName", "Rufus" | ||||||
|             VALUE "ProductVersion", "3.14.1735" |             VALUE "ProductVersion", "3.14.1736" | ||||||
|         END |         END | ||||||
|     END |     END | ||||||
|     BLOCK "VarFileInfo" |     BLOCK "VarFileInfo" | ||||||
|  |  | ||||||
							
								
								
									
										155
									
								
								src/winio.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								src/winio.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,155 @@ | ||||||
|  | /*
 | ||||||
|  | * Rufus: The Reliable USB Formatting Utility | ||||||
|  | * Windows I/O redefinitions, that would be totally unnecessary had | ||||||
|  | * Microsoft done a proper job with their asynchronous APIs. | ||||||
|  | * Copyright © 2021 Pete Batard <pete@akeo.ie> | ||||||
|  | * | ||||||
|  | * This program is free software: you can redistribute it and/or modify | ||||||
|  | * it under the terms of the GNU General Public License as published by | ||||||
|  | * the Free Software Foundation, either version 3 of the License, or | ||||||
|  | * (at your option) any later version. | ||||||
|  | * | ||||||
|  | * This program is distributed in the hope that it will be useful, | ||||||
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | * GNU General Public License for more details. | ||||||
|  | * | ||||||
|  | * You should have received a copy of the GNU General Public License | ||||||
|  | * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include <windows.h> | ||||||
|  | #include "msapi_utf8.h" | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | // https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped
 | ||||||
|  | // See Microsoft? It's not THAT hard to define an OVERLAPPED struct in a manner that
 | ||||||
|  | // doesn't qualify as an example of "Crimes against humanity" for the Geneva convention.
 | ||||||
|  | typedef struct { | ||||||
|  | 	ULONG_PTR                           Internal[2]; | ||||||
|  | 	ULONG64                             Offset; | ||||||
|  | 	HANDLE                              hEvent; | ||||||
|  | 	BOOL                                bOffsetUpdated; | ||||||
|  | } NOW_THATS_WHAT_I_CALL_AN_OVERLAPPED; | ||||||
|  | 
 | ||||||
|  | // File Descriptor for asynchronous accesses.
 | ||||||
|  | // The status field is a threestate value reflecting the result
 | ||||||
|  | // of the current asynchronous read operation:
 | ||||||
|  | //  1: Read was successful and completed synchronously
 | ||||||
|  | // -1: Read is pending asynchronously
 | ||||||
|  | //  0: Read Error
 | ||||||
|  | typedef struct { | ||||||
|  | 	HANDLE                              hFile; | ||||||
|  | 	INT                                 iStatus; | ||||||
|  | 	NOW_THATS_WHAT_I_CALL_AN_OVERLAPPED Overlapped; | ||||||
|  | } ASYNC_FD; | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Open a file for asynchronous access. The values for the flags are the same as the ones
 | ||||||
|  | /// for the native CreateFile() call. Note that FILE_FLAG_OVERLAPPED will always be added
 | ||||||
|  | /// to dwFlagsAndAttributes before the file is instantiated, and that an internal
 | ||||||
|  | /// OVERLAPPED structure with its associated wait event is also created.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="lpFileName">The name of the file or device to be created or opened</param>
 | ||||||
|  | /// <param name="dwDesiredAccess">The requested access to the file or device</param>
 | ||||||
|  | /// <param name="dwShareMode">The requested sharing mode of the file or device</param>
 | ||||||
|  | /// <param name="dwCreationDisposition">Action to take on a file or device that exists or does not exist</param>
 | ||||||
|  | /// <param name="dwFlagsAndAttributes">The file or device attributes and flags</param>
 | ||||||
|  | /// <returns>Non NULL on success</returns>
 | ||||||
|  | static __inline VOID* CreateFileAsync(LPCSTR lpFileName, DWORD dwDesiredAccess, | ||||||
|  | 	DWORD dwShareMode, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes) | ||||||
|  | { | ||||||
|  | 	ASYNC_FD* fd = calloc(sizeof(ASYNC_FD), 1); | ||||||
|  | 	if (fd == NULL) { | ||||||
|  | 		SetLastError(ERROR_NOT_ENOUGH_MEMORY); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	fd->Overlapped.hEvent = CreateEventA(NULL, TRUE, FALSE, NULL); | ||||||
|  | 	fd->hFile = CreateFileU(lpFileName, dwDesiredAccess, dwShareMode, NULL, | ||||||
|  | 		dwCreationDisposition, FILE_FLAG_OVERLAPPED | dwFlagsAndAttributes, NULL); | ||||||
|  | 	if (fd->hFile == INVALID_HANDLE_VALUE) { | ||||||
|  | 		CloseHandle(fd->Overlapped.hEvent); | ||||||
|  | 		free(fd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	return fd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Close a previously opened asynchronous file
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="fd">The file descriptor</param>
 | ||||||
|  | static __inline VOID CloseFileAsync(VOID* fd) | ||||||
|  | { | ||||||
|  | 	ASYNC_FD* _fd = (ASYNC_FD*)fd; | ||||||
|  | 	if (_fd == NULL) | ||||||
|  | 		return; | ||||||
|  | 	CloseHandle(_fd->hFile); | ||||||
|  | 	CloseHandle(_fd->Overlapped.hEvent); | ||||||
|  | 	free(_fd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Initiate a read operation for asynchronous I/O.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="fd">The file descriptor</param>
 | ||||||
|  | /// <param name="lpBuffer">The buffer that receives the data</param>
 | ||||||
|  | /// <param name="nNumberOfBytesToRead">Number of bytes requested</param>
 | ||||||
|  | /// <returns>TRUE on success, FALSE on error</returns>
 | ||||||
|  | static __inline BOOL ReadFileAsync(VOID* fd, LPVOID lpBuffer, DWORD nNumberOfBytesToRead) | ||||||
|  | { | ||||||
|  | 	ASYNC_FD* _fd = (ASYNC_FD*)fd; | ||||||
|  | 	_fd->Overlapped.bOffsetUpdated = FALSE; | ||||||
|  | 	if (!ReadFile(_fd->hFile, lpBuffer, nNumberOfBytesToRead, NULL, | ||||||
|  | 		(OVERLAPPED*)&_fd->Overlapped)) | ||||||
|  | 		// TODO: Is it possible to get ERROR_HANDLE_EOF here?
 | ||||||
|  | 		_fd->iStatus = (GetLastError() == ERROR_IO_PENDING) ? -1 : 0; | ||||||
|  | 	else | ||||||
|  | 		_fd->iStatus = 1; | ||||||
|  | 	return (_fd->iStatus != 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Wait for an asynchronous operation to complete, with timeout.
 | ||||||
|  | /// This function also succeeds if the I/O already completed synchronously.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="fd">The file descriptor</param>
 | ||||||
|  | /// <param name="dwTimeout">A timeout value, in ms</param>
 | ||||||
|  | /// <returns>TRUE on success, FALSE on error</returns>
 | ||||||
|  | static __inline BOOL WaitFileAsync(VOID* fd, DWORD dwTimeout) | ||||||
|  | { | ||||||
|  | 	ASYNC_FD* _fd = (ASYNC_FD*)fd; | ||||||
|  | 	if (_fd->iStatus > 0)	// Read completed synchronously
 | ||||||
|  | 		return TRUE; | ||||||
|  | 	return (WaitForSingleObject(_fd->Overlapped.hEvent, dwTimeout) == WAIT_OBJECT_0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// <summary>
 | ||||||
|  | /// Return the number of bytes read and keep track/update the current offset
 | ||||||
|  | /// for an asynchronous read operation.
 | ||||||
|  | /// </summary>
 | ||||||
|  | /// <param name="fd">The file descriptor</param>
 | ||||||
|  | /// <param name="lpNumberOfBytesRead">A pointer that receives the number of bytes read.</param>
 | ||||||
|  | /// <returns>TRUE on success, FALSE on error</returns>
 | ||||||
|  | static __inline BOOL GetSizeAsync(VOID* fd, LPDWORD lpNumberOfBytesRead) | ||||||
|  | { | ||||||
|  | 	ASYNC_FD* _fd = (ASYNC_FD*)fd; | ||||||
|  | 	// Previous call to ReadFileAsync() failed
 | ||||||
|  | 	if (_fd->iStatus == 0) { | ||||||
|  | 		*lpNumberOfBytesRead = 0; | ||||||
|  | 		return FALSE; | ||||||
|  | 	} | ||||||
|  | 	// Detect if we already read the size and updated the offset
 | ||||||
|  | 	if (_fd->Overlapped.bOffsetUpdated) { | ||||||
|  | 		SetLastError(ERROR_NO_MORE_ITEMS); | ||||||
|  | 		return FALSE; | ||||||
|  | 	} | ||||||
|  | 	// TODO: Use a timeout and call GetOverlappedResultEx() on Windows 8 and later
 | ||||||
|  | 	if (!GetOverlappedResult(_fd->hFile, (OVERLAPPED*)&_fd->Overlapped, | ||||||
|  | 		lpNumberOfBytesRead, (_fd->iStatus < 0))) | ||||||
|  | 		return (GetLastError() == ERROR_HANDLE_EOF); | ||||||
|  | 	_fd->Overlapped.Offset += *lpNumberOfBytesRead; | ||||||
|  | 	_fd->Overlapped.bOffsetUpdated = TRUE; | ||||||
|  | 	return TRUE; | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue