Selaa lähdekoodia

implemented support of overlapped input/output in Kernel32;
use overlapped IO in V24 serial port; the reason: by default in Windows IO operations on a serial port are done in a synchronous way where read and write operations on the same port are mutually exclusive (explained in https://msdn.microsoft.com/en-us/library/windows/desktop/ms686358(v=vs.85).aspx and practically verified by me); that forbids simultaneous reading/writing from/to a serial port; overlapped IO allows to overcome the limitations of synchronous IO.

git-svn-id: https://svn.inf.ethz.ch/svn/lecturers/a2/trunk@7105 8c9fc860-2736-0410-a75d-ab315db34111

eth.morozova 8 vuotta sitten
vanhempi
commit
29fbd5bf4c
2 muutettua tiedostoa jossa 80 lisäystä ja 38 poistoa
  1. 22 5
      source/Generic.Win32.Kernel32.Mod
  2. 58 33
      source/Win32.V24.Mod

+ 22 - 5
source/Generic.Win32.Kernel32.Mod

@@ -39,6 +39,7 @@ CONST
 	(** file creation flags *)
 	FileFlagDeleteOnClose*=26;
 	FileFlagRandomAccess* = 28;
+	FileFlagOverlapped* = 30; (** The file or device is being opened or created for asynchronous I/O *)
 	FileFlagWriteThrough*= 31;
 
 	(** move method *)
@@ -152,7 +153,8 @@ CONST
 	(** GetLastError *)
 	ErrorSuccess* = 0;  ErrorFileNotFound* = 2;  ErrorAccessDenied* = 5;
 	ErrorInvalidParameter* = 87;
-
+	
+	ErrorIoPending* = 997; (** Overlapped I/O operation is in progress *)
 TYPE
 	(* OutputStringProc* = PROCEDURE (VAR str: ARRAY OF CHAR); *)
 
@@ -336,6 +338,15 @@ TYPE
 		DaylightDate*: SystemTime;
 		DaylightBias*: LONGINT;
 	END;
+	
+	(** Contains information used in asynchronous (or overlapped) input and output (I/O). *)
+	Overlapped* = RECORD
+		Internal*: LONGINT;
+		InternalHigh*: LONGINT;
+		Offset*: LONGINT;
+		OffsetHigh*: LONGINT;
+		hEvent*: HANDLE;
+	END;
 
 VAR
 	hInstance-: HINSTANCE;   (* init by linker/loader *)
@@ -516,6 +527,11 @@ VAR
 	(** The GetModuleHandle function returns a module handle for the specified module if the file has been mapped
 			into the address space of the calling process. *)
 	GetModuleHandle-: PROCEDURE {WINAPI} ( CONST lpModuleName: ARRAY   OF CHAR ): HMODULE;
+	
+	(** Retrieves the results of an overlapped operation on the specified file, named pipe, or communications device. 
+			To specify a timeout interval or wait on an alertable thread. *)
+	GetOverlappedResult-: PROCEDURE {WINAPI} ( hFile: HANDLE; VAR lpOverlapped: Overlapped; VAR lpNumberOfBytesTransferred: LONGINT; bWait: BOOL ): BOOL;
+	
 	(** The GetPrivateProfileString function retrieves a string from the specified section in an initialization file.*)
 	GetPrivateProfileString-: PROCEDURE {WINAPI} ( CONST lpAppName: ARRAY OF CHAR;
 																			CONST lpKeyName: ARRAY OF CHAR;
@@ -662,13 +678,13 @@ VAR
 															  VAR lpBuffer: ARRAY OF SYSTEM.BYTE;
 															  nNumberOfBytesToRead: LONGINT;
 															  VAR lpNumberOfBytesRead: LONGINT;
-															  lpOverlapped: ANY ): BOOL;
+															  lpOverlapped: ADDRESS ): BOOL;
 
 	PROCEDURE ReadFile- ( hFile: HANDLE;
 															  VAR lpBuffer: ARRAY OF SYSTEM.BYTE;
 															  nNumberOfBytesToRead: LONGINT;
 															  VAR lpNumberOfBytesRead: LONGINT;
-															  lpOverlapped: ANY ): BOOL;
+															  lpOverlapped: ADDRESS ): BOOL;
 	VAR b: BOOL;
 	BEGIN
 		LeaveA2;
@@ -780,14 +796,14 @@ VAR
 															   CONST lpBuffer: ARRAY   OF SYSTEM.BYTE;
 															   nNumberOfBytesToWrite: LONGINT;
 															   VAR lpNumberOfBytesWritten: LONGINT;
-															   lpOverlapped: ANY ): BOOL;
+															   lpOverlapped: ADDRESS ): BOOL;
 	(** Thread abort notifier, parameter is the threads id. Note this should only be used in modules which
 			can't use the exception handling mechanism provided by module Exceptions. *)
 	PROCEDURE WriteFile- ( hFile: HANDLE;
 															   CONST lpBuffer: ARRAY   OF SYSTEM.BYTE;
 															   nNumberOfBytesToWrite: LONGINT;
 															   VAR lpNumberOfBytesWritten: LONGINT;
-															   lpOverlapped: ANY ): BOOL;
+															   lpOverlapped: ADDRESS ): BOOL;
 	VAR b: BOOL;
 	BEGIN
 		LeaveA2;
@@ -915,6 +931,7 @@ VAR
 		GetProcAddress(mod, "GetLogicalDrives",SYSTEM.VAL(ADDRESS,GetLogicalDrives));
 		GetProcAddress(mod, "GetModuleFileNameA",SYSTEM.VAL(ADDRESS,GetModuleFileName));
 		GetProcAddress(mod, "GetModuleHandleA",SYSTEM.VAL(ADDRESS,GetModuleHandle));
+		GetProcAddress(mod, "GetOverlappedResult",SYSTEM.VAL(ADDRESS,GetOverlappedResult));
 		GetProcAddress(mod, "GetPrivateProfileStringA",SYSTEM.VAL(ADDRESS,GetPrivateProfileString));
 		(* must be done by linker: GetProcAddress(mod, "GetProcAddress",SYSTEM.VAL(ADDRESS,getProcAddress)); *)
 		GetProcAddress(mod, "GetProcessHeap",SYSTEM.VAL(ADDRESS,GetProcessHeap));

+ 58 - 33
source/Win32.V24.Mod

@@ -79,6 +79,8 @@ TYPE
 		handle: Kernel32.HANDLE;
 		portname: ARRAY 6 OF CHAR; (* Name COM1 to COM8 must terminate with a 0X *)
 		timer : Kernel.Timer;
+		
+		rOverlapped, wOverlapped: Kernel32.Overlapped;
 
 		PROCEDURE & Init*( port: LONGINT );
 		VAR fn: Heaps.FinalizerNode;
@@ -102,6 +104,10 @@ TYPE
 			IF handle = Kernel32.InvalidHandleValue THEN
 				SetPortState( bps, data, parity, stop, res );
 				IF res = Serials.Ok THEN
+					rOverlapped.hEvent := Kernel32.CreateEvent(NIL,Kernel32.True,Kernel32.False,NIL);
+					ASSERT(rOverlapped.hEvent # NIL);
+					wOverlapped.hEvent := Kernel32.CreateEvent(NIL,Kernel32.True,Kernel32.False,NIL);
+					ASSERT(wOverlapped.hEvent # NIL);
 					KernelLog.String( portname );  KernelLog.String( " opened" ); KernelLog.Ln
 				END;
 			ELSE
@@ -115,9 +121,13 @@ TYPE
 		BEGIN
 			ASSERT ( LEN( buf ) >= ofs + len );   (* array bound check not implemented in Kernel32.WriteFile *)
 			IF (handle # Kernel32.InvalidHandleValue) THEN
-				ret := Kernel32.WriteFile( handle, buf[ofs], len, written, NIL );
+				written := 0;
+				ret := Kernel32.WriteFile( handle, buf[ofs], len, written, ADDRESSOF(wOverlapped) );
+				IF ret = Kernel32.False THEN
+					ret := Kernel32.GetOverlappedResult(handle,wOverlapped,written,Kernel32.True);
+				END;
+				INC(charactersSent,written);
 				IF (ret # Kernel32.False) & (written = len) THEN
-					charactersSent  := charactersSent + written;
 					res := Serials.Ok;
 				ELSE
 					res := Serials.TransportError;
@@ -132,9 +142,13 @@ TYPE
 		VAR ret: Kernel32.BOOL;  written: LONGINT;
 		BEGIN
 			IF handle # Kernel32.InvalidHandleValue THEN
-				ret := Kernel32.WriteFile( handle, ch, 1, written, NIL );
+				written := 0;
+				ret := Kernel32.WriteFile( handle, ch, 1, written, ADDRESSOF(wOverlapped) );
+				IF ret = Kernel32.False THEN
+					ret := Kernel32.GetOverlappedResult(handle,wOverlapped,written,Kernel32.True);
+				END;
+				INC(charactersSent,written);
 				IF (ret # Kernel32.False) & (written=1) THEN
-					INC(charactersSent);
 					res := Serials.Ok;
 				ELSE
 					res := Serials.TransportError;
@@ -153,30 +167,39 @@ TYPE
 		BEGIN
 			ASSERT ( LEN( buf ) >= ofs + size );
 			ASSERT ( LEN( buf ) >= ofs + min );   (* array bound check not implemented in Kernel32.ReadFile *)
-			res := Serials.Ok; len := 0;
-			i := ofs;  l := Available();
-			WHILE (res = Serials.Ok) & ((min > 0) OR ((l > 0) & (size > 0))) DO  (* fof 060804 *)
-				IF l > size THEN l := size END;
-				IF (handle # Kernel32.InvalidHandleValue) THEN
-					IF  (l > 0) THEN
-						ret := Kernel32.ReadFile( handle, buf[i], l, read, NIL );
-						IF (ret # Kernel32.False) & (read = l) THEN
-							charactersReceived := charactersReceived + read;
-							DEC( min, l );  DEC( size, l );  INC( len, l );  INC( i, l );
-						ELSE
-							(* If we've already received <min> bytes, <res> will become Serials.Ok later *)
-							res := Serials.TransportError;
-						END;
+			IF handle # Kernel32.InvalidHandleValue THEN
+				
+				res := Serials.Ok; len := 0;
+				
+				(* blocking read of the minimally required amount of data *)
+				IF min > 0 THEN
+					read := 0;
+					ret := Kernel32.ReadFile( handle, buf[ofs], min, read, ADDRESSOF(rOverlapped) );
+					IF ret = Kernel32.False THEN
+						ret := Kernel32.GetOverlappedResult(handle,rOverlapped,read,Kernel32.True);
+					END;
+					INC(ofs,read); INC(len,read); INC(charactersReceived,read); DEC(size,read);
+					IF ret = Kernel32.False THEN
+						res := Serials.TransportError; RETURN;
 					END;
-				ELSE
-					res := Serials.Closed;
 				END;
-				l := Available();
-				IF (res = Serials.Ok) & ( (min > 0) OR ((l > 0) & (size > 0)) ) THEN
-					Wait;
+				
+				(* Non-blocking read of available data *)
+				l := MIN(size,Available());
+				IF l > 0 THEN
+					read := 0;
+					ret := Kernel32.ReadFile( handle, buf[ofs], l, read, ADDRESSOF(rOverlapped) );
+					IF ret = Kernel32.False THEN
+						ret := Kernel32.GetOverlappedResult(handle,rOverlapped,read,Kernel32.True);
+					END;
+					INC(len,read); INC(charactersReceived,read);
+					IF ret = Kernel32.False THEN (* we've already received <min> bytes, but there was an error and we are going to report it anyway! *)
+						res := Serials.TransportError;
+					END; 
 				END;
+			ELSE
+				res := Serials.Closed;
 			END;
-			IF min <= 0 THEN res := Serials.Ok END;
 		END Receive;
 
 	(** Wait for the next character is received in the input buffer.  *)
@@ -184,17 +207,17 @@ TYPE
 		VAR l, ret, read: LONGINT;
 		BEGIN
 			IF handle # Kernel32.InvalidHandleValue THEN
-				REPEAT
-					l := Available();
-					IF l = 0 THEN Wait; END;
-				UNTIL (l > 0) OR (handle = Kernel32.InvalidHandleValue);
-				ret := Kernel32.ReadFile( handle, ch, 1, read, NIL );
+				read := 0;
+				ret := Kernel32.ReadFile( handle, ch, 1, read, ADDRESSOF(rOverlapped) );
+				IF ret = Kernel32.False THEN
+					ret := Kernel32.GetOverlappedResult(handle,rOverlapped,read,Kernel32.True);
+				END;
+				INC(charactersReceived,read);
 				IF (ret # Kernel32.False) & (read = 1) THEN
-					INC(charactersReceived);
-					res := Serials.Ok
+					res := Serials.Ok;
 				ELSE
 					res := Serials.TransportError;
-				END
+				END;
 			ELSE
 				res := Serials.Closed;
 			END
@@ -371,7 +394,9 @@ TYPE
 		PROCEDURE Close*;
 		BEGIN {EXCLUSIVE}
 			IF handle # Kernel32.InvalidHandleValue THEN
-				Kernel32.CloseHandle( handle );  handle := Kernel32.InvalidHandleValue
+				Kernel32.CloseHandle( handle );  handle := Kernel32.InvalidHandleValue;
+				IF rOverlapped.hEvent # NIL THEN Kernel32.CloseHandle(rOverlapped.hEvent); rOverlapped.hEvent := NIL; END;
+				IF wOverlapped.hEvent # NIL THEN Kernel32.CloseHandle(wOverlapped.hEvent); wOverlapped.hEvent := NIL; END;
 			END
 		END Close;