THRIFT-1880 Make named pipes server work asynchronously (overlapped) to allow for clean server stops

Patch: Jens Geyer
diff --git a/lib/delphi/test/TestClient.pas b/lib/delphi/test/TestClient.pas
index 2f77de8..37fe7d7 100644
--- a/lib/delphi/test/TestClient.pas
+++ b/lib/delphi/test/TestClient.pas
@@ -19,6 +19,9 @@
 
 unit TestClient;
 
+{.$DEFINE StressTest}   // activate to stress-test the server with frequent connects/disconnects
+{.$DEFINE PerfTest}     // activate to activate the performance test
+
 interface
 
 uses
@@ -63,6 +66,7 @@
 
     procedure ClientTest;
     procedure JSONProtocolReadWriteTest;
+    procedure StressTest(const client : TThriftTest.Iface);
   protected
     procedure Execute; override;
   public
@@ -238,11 +242,11 @@
       begin
         if sPipeName <> '' then begin
           Console.WriteLine('Using named pipe ('+sPipeName+')');
-          streamtrans := TNamedPipeImpl.Create( sPipeName, 0, nil, TIMEOUT);
+          streamtrans := TNamedPipeTransportClientEndImpl.Create( sPipeName, 0, nil, TIMEOUT);
         end
         else if bAnonPipe then begin
           Console.WriteLine('Using anonymous pipes ('+IntToStr(Integer(hAnonRead))+' and '+IntToStr(Integer(hAnonWrite))+')');
-          streamtrans := TAnonymousPipeImpl.Create( hAnonRead, hAnonWrite, FALSE);
+          streamtrans := TAnonymousPipeTransportImpl.Create( hAnonRead, hAnonWrite, FALSE);
         end
         else begin
           Console.WriteLine('Using sockets ('+host+' port '+IntToStr(port)+')');
@@ -370,6 +374,10 @@
   client := TThriftTest.TClient.Create( FProtocol);
   FTransport.Open;
 
+  {$IFDEF StressTest}
+  StressTest( client);
+  {$ENDIF StressTest}
+
   // in-depth exception test
   // (1) do we get an exception at all?
   // (2) do we get the right exception?
@@ -422,6 +430,11 @@
   s := client.testString('Test');
   Expect( s = 'Test', 'testString(''Test'') = "'+s+'"');
 
+  s := client.testString(HUGE_TEST_STRING);
+  Expect( length(s) = length(HUGE_TEST_STRING),
+          'testString( lenght(HUGE_TEST_STRING) = '+IntToStr(Length(HUGE_TEST_STRING))+') '
+         +'=> length(result) = '+IntToStr(Length(s)));
+
   i8 := client.testByte(1);
   Expect( i8 = 1, 'testByte(1) = ' + IntToStr( i8 ));
 
@@ -831,6 +844,7 @@
   Expect( TRUE, 'Test Oneway(1)');  // success := no exception
 
   // call time
+  {$IFDEF PerfTest}
   StartTestGroup( 'Test Calltime()');
   StartTick := GetTIckCount;
   for k := 0 to 1000 - 1 do
@@ -838,12 +852,31 @@
     client.testVoid();
   end;
   Console.WriteLine(' = ' + FloatToStr( (GetTickCount - StartTick) / 1000 ) + ' ms a testVoid() call' );
+  {$ENDIF PerfTest}
 
   // no more tests here
   StartTestGroup( '');
 end;
 
 
+procedure TClientThread.StressTest(const client : TThriftTest.Iface);
+begin
+  while TRUE do begin
+    try
+      if not FTransport.IsOpen then FTransport.Open;   // re-open connection, server has already closed
+      try
+        client.testString('Test');
+        Write('.');
+      finally
+        if FTransport.IsOpen then FTransport.Close;
+      end;
+    except
+      on e:Exception do Writeln(#10+e.message);
+    end;
+  end;
+end;
+
+
 procedure TClientThread.JSONProtocolReadWriteTest;
 // Tests only then read/write procedures of the JSON protocol
 // All tests succeed, if we can read what we wrote before
diff --git a/lib/delphi/test/TestConstants.pas b/lib/delphi/test/TestConstants.pas
index b6664ef..f21a4bb 100644
--- a/lib/delphi/test/TestConstants.pas
+++ b/lib/delphi/test/TestConstants.pas
@@ -33,6 +33,79 @@
   BINARY_STRICT_READ  = FALSE;
   BINARY_STRICT_WRITE = FALSE;
 
+  HUGE_TEST_STRING = 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. '
+                   + 'Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy '
+                   + 'eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam '
+                   + 'voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit '
+                   + 'amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam '
+                   + 'nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed '
+                   + 'diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet '
+                   + 'clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. ';
+
 implementation
 
 // nothing
diff --git a/lib/delphi/test/TestServer.pas b/lib/delphi/test/TestServer.pas
index 791468b..7b74e58 100644
--- a/lib/delphi/test/TestServer.pas
+++ b/lib/delphi/test/TestServer.pas
@@ -21,6 +21,8 @@
 
 {$WARN SYMBOL_PLATFORM OFF}
 
+{.$DEFINE RunEndless}   // activate to interactively stress-test the server stop routines via Ctrl+C
+
 interface
 
 uses
@@ -46,6 +48,7 @@
 
       ITestHandler = interface( TThriftTest.Iface )
         procedure SetServer( const AServer : IServer );
+        procedure TestStop;
       end;
 
       TTestHandlerImpl = class( TInterfacedObject, ITestHandler )
@@ -73,17 +76,45 @@
         function testMultiException(const arg0: string; const arg1: string): IXtruct;
         procedure testOneway(secondsToSleep: Integer);
 
-         procedure testStop;
-
+        procedure TestStop;
         procedure SetServer( const AServer : IServer );
       end;
 
-      class procedure LaunchAnonPipeChild( const app : string; const transport : IAnonymousServerPipe);
+      class procedure LaunchAnonPipeChild( const app : string; const transport : IAnonymousPipeServerTransport);
       class procedure Execute( const args: array of string);
   end;
 
 implementation
 
+
+var g_Handler : TTestServer.ITestHandler = nil;
+
+
+function MyConsoleEventHandler( dwCtrlType : DWORD) : BOOL;  stdcall;
+// Note that this Handler procedure is called from another thread
+var handler : TTestServer.ITestHandler;
+begin
+  result := TRUE;
+  try
+    case dwCtrlType of
+      CTRL_C_EVENT        :  Console.WriteLine( 'Ctrl+C pressed');
+      CTRL_BREAK_EVENT    :  Console.WriteLine( 'Ctrl+Break pressed');
+      CTRL_CLOSE_EVENT    :  Console.WriteLine( 'Received CloseTask signal');
+      CTRL_LOGOFF_EVENT   :  Console.WriteLine( 'Received LogOff signal');
+      CTRL_SHUTDOWN_EVENT :  Console.WriteLine( 'Received Shutdown signal');
+    else
+      Console.WriteLine( 'Received console event #'+IntToStr(Integer(dwCtrlType)));
+    end;
+
+    handler := g_Handler;
+    if handler <> nil then handler.TestStop;
+
+  except
+    // catch all
+  end;
+end;
+
+
 { TTestServer.TTestHandlerImpl }
 
 procedure TTestServer.TTestHandlerImpl.SetServer( const AServer: IServer);
@@ -405,7 +436,7 @@
 { TTestServer }
 
 
-class procedure TTestServer.LaunchAnonPipeChild( const app : string; const transport : IAnonymousServerPipe);
+class procedure TTestServer.LaunchAnonPipeChild( const app : string; const transport : IAnonymousPipeServerTransport);
 //Launch child process and pass R/W anonymous pipe handles on cmd line.
 //This is a simple example and does not include elevation or other
 //advanced features.
@@ -457,8 +488,8 @@
   testProcessor : IProcessor;
   ServerTrans : IServerTransport;
   ServerEngine : IServer;
-  anonymouspipe : IAnonymousServerPipe;
-  namedpipe : INamedServerPipe;
+  anonymouspipe : IAnonymousPipeServerTransport;
+  namedpipe : INamedPipeServerTransport;
   TransportFactory : ITransportFactory;
   ProtocolFactory : IProtocolFactory;
   i : Integer;
@@ -542,12 +573,12 @@
 
     if sPipeName <> '' then begin
       Console.WriteLine('- named pipe ('+sPipeName+')');
-      namedpipe   := TNamedServerPipeImpl.Create( sPipeName, 4096, PIPE_UNLIMITED_INSTANCES, TIMEOUT);
+      namedpipe   := TNamedPipeServerTransportImpl.Create( sPipeName, 4096, PIPE_UNLIMITED_INSTANCES, TIMEOUT);
       servertrans := namedpipe;
     end
     else if AnonPipe then begin
       Console.WriteLine('- anonymous pipes');
-      anonymouspipe := TAnonymousServerPipeImpl.Create;
+      anonymouspipe := TAnonymousPipeServerTransportImpl.Create;
       servertrans   := anonymouspipe;
     end
     else begin
@@ -580,11 +611,18 @@
     if AnonPipe
     then LaunchAnonPipeChild( ExtractFilePath(ParamStr(0))+'client.exe', anonymouspipe);
 
+    // install Ctrl+C handler before the server starts
+    g_Handler := testHandler;
+    SetConsoleCtrlHandler( @MyConsoleEventHandler, TRUE);
 
     Console.WriteLine('');
-    Console.WriteLine('Starting the server ...');
-    serverEngine.Serve;
+    repeat
+      Console.WriteLine('Starting the server ...');
+      serverEngine.Serve;
+    until {$IFDEF RunEndless} FALSE {$ELSE} TRUE {$ENDIF};
+
     testHandler.SetServer( nil);
+    g_Handler := nil;
 
   except
     on E: Exception do
@@ -595,4 +633,5 @@
   Console.WriteLine( 'done.');
 end;
 
+
 end.
diff --git a/lib/delphi/test/server.dpr b/lib/delphi/test/server.dpr
index 5fad6eb..ca485af 100644
--- a/lib/delphi/test/server.dpr
+++ b/lib/delphi/test/server.dpr
@@ -54,7 +54,7 @@
       args[i-1] := arg;
     end;
     TTestServer.Execute( args );
-    Readln;
+    Writeln('Press ENTER to close ... '); Readln;
   except
     on E: Exception do
       Writeln(E.ClassName, ': ', E.Message);