From b85079a7cb4d0f82e98616b7338fe3a4e95a19b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Mussche?= Date: Wed, 25 Jun 2014 15:30:18 +0200 Subject: [PATCH] shutdown fixes, error event handling --- IdHTTPWebsocketClient.pas | 61 ++++++++++++++++++++++++++------- IdSocketIOHandling.pas | 22 +++++++----- uROIndyHTTPWebsocketChannel.pas | 3 ++ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/IdHTTPWebsocketClient.pas b/IdHTTPWebsocketClient.pas index cb0406d..d18b477 100644 --- a/IdHTTPWebsocketClient.pas +++ b/IdHTTPWebsocketClient.pas @@ -184,6 +184,9 @@ uses IdCoderMIME, SysUtils, Math, IdException, IdStackConsts, IdStack, IdStackBSDBase, IdGlobal, Windows, StrUtils, DateUtils; +var + GUnitFinalized: Boolean = false; + //type // TAnonymousThread = class(TThread) // protected @@ -1148,6 +1151,8 @@ procedure TIdWebsocketMultiReadThread.AddClient( var l: TList; begin //Assert( (aChannel.IOHandler as TIdIOHandlerWebsocket).IsWebsocket, 'Channel is not a websocket'); + if Self = nil then Exit; + if Self.Terminated then Exit; l := FChannels.LockList; try @@ -1212,6 +1217,13 @@ end; destructor TIdWebsocketMultiReadThread.Destroy; begin + if FReconnectThread <> nil then + begin + FReconnectThread.Terminate; + FReconnectThread.WaitFor; + FReconnectThread.Free; + end; + IdWinsock2.closesocket(FTempHandle); FTempHandle := 0; FChannels.Free; @@ -1256,6 +1268,8 @@ class function TIdWebsocketMultiReadThread.Instance: TIdWebsocketMultiReadThread begin if (FInstance = nil) then begin + if GUnitFinalized then Exit(nil); + FInstance := TIdWebsocketMultiReadThread.Create(True); FInstance.Start; end; @@ -1269,6 +1283,8 @@ var ws: TIdIOHandlerWebsocket; i: Integer; begin + if Terminated then Exit; + l := FChannels.LockList; try for i := 0 to l.Count - 1 do @@ -1311,8 +1327,11 @@ begin FChannels.UnlockList; end; + if Terminated then Exit; + //reconnect needed? (in background) - if (FReconnectlist <> nil) and (FReconnectlist.Count > 0) then + if FReconnectlist <> nil then + if FReconnectlist.Count > 0 then begin if FReconnectThread = nil then FReconnectThread := TIdWebsocketQueueThread.Create(False{direct start}); @@ -1456,7 +1475,8 @@ begin //some data? if (iResult > 0) then begin - //strmEvent := nil; + //make sure the thread is created outside a lock + TIdWebsocketDispatchThread.Instance; l := FChannels.LockList; if l = nil then Exit; @@ -1503,6 +1523,7 @@ procedure TIdWebsocketMultiReadThread.RemoveClient( aChannel: TIdHTTPWebsocketClient); begin if Self = nil then Exit; + if Self.Terminated then Exit; aChannel.Lock; try @@ -1516,18 +1537,23 @@ begin end; class procedure TIdWebsocketMultiReadThread.RemoveInstance(aForced: boolean); +var + o: TIdWebsocketMultiReadThread; begin if FInstance <> nil then begin FInstance.Terminate; + o := FInstance; + FInstance := nil; + if aForced then begin - WaitForSingleObject(FInstance.Handle, 2 * 1000); - TerminateThread(FInstance.Handle, MaxInt); + WaitForSingleObject(o.Handle, 2 * 1000); + TerminateThread(o.Handle, MaxInt); end else - FInstance.WaitFor; - FreeAndNil(FInstance); + o.WaitFor; + FreeAndNil(o); end; end; @@ -1544,6 +1570,8 @@ end; procedure TIdWebsocketMultiReadThread.Terminate; begin inherited Terminate; + if FReconnectThread <> nil then + FReconnectThread.Terminate; FChannels.LockList; try @@ -1560,6 +1588,8 @@ class function TIdWebsocketDispatchThread.Instance: TIdWebsocketDispatchThread; begin if FInstance = nil then begin + if GUnitFinalized then Exit(nil); + GlobalNameSpace.BeginWrite; try if FInstance = nil then @@ -1575,17 +1605,22 @@ begin end; class procedure TIdWebsocketDispatchThread.RemoveInstance; +var + o: TIdWebsocketDispatchThread; begin if FInstance <> nil then begin FInstance.Terminate; + o := FInstance; + FInstance := nil; + if aForced then begin - WaitForSingleObject(FInstance.Handle, 2 * 1000); - TerminateThread(FInstance.Handle, MaxInt); + WaitForSingleObject(o.Handle, 2 * 1000); + TerminateThread(o.Handle, MaxInt); end; - FInstance.WaitFor; - FreeAndNil(FInstance); + o.WaitFor; + FreeAndNil(o); end; end; @@ -1604,7 +1639,9 @@ end; initialization finalization + GUnitFinalized := True; + if TIdWebsocketMultiReadThread.Instance <> nil then + TIdWebsocketMultiReadThread.Instance.Terminate; + TIdWebsocketDispatchThread.RemoveInstance(); TIdWebsocketMultiReadThread.RemoveInstance(); - TIdWebsocketDispatchThread.RemoveInstance() - end. diff --git a/IdSocketIOHandling.pas b/IdSocketIOHandling.pas index 92d7c3d..03d810d 100644 --- a/IdSocketIOHandling.pas +++ b/IdSocketIOHandling.pas @@ -6,7 +6,7 @@ uses Classes, Generics.Collections, superobject, IdServerBaseHandling, IdContext, IdException, IdIOHandlerWebsocket, IdHTTP, - SyncObjs; + SyncObjs, SysUtils; type TSocketIOContext = class; @@ -22,6 +22,7 @@ type TSocketIONotify = reference to procedure(const ASocket: ISocketIOContext); TSocketIOEvent = reference to procedure(const ASocket: ISocketIOContext; const aArgument: TSuperArray; const aCallback: ISocketIOCallback); TSocketIOError = reference to procedure(const ASocket: ISocketIOContext; const aErrorClass, aErrorMessage: string); + TSocketIOEventError = reference to procedure(const ASocket: ISocketIOContext; const aCallback: ISocketIOCallback; E: Exception); TSocketIONotifyList = class(TList); TSocketIOEventList = class(TList); @@ -139,6 +140,8 @@ type FOnSocketIOJson: TSocketIOMsgJSON; procedure ProcessEvent(const AContext: TSocketIOContext; const aText: string; aMsgNr: Integer; aHasCallback: Boolean); + private + FOnEventError: TSocketIOEventError; protected type TSocketIOCallback = procedure(const aData: string) of object; @@ -194,6 +197,7 @@ type procedure OnEvent (const aEventName: string; const aCallback: TSocketIOEvent); procedure OnConnection(const aCallback: TSocketIONotify); procedure OnDisconnect(const aCallback: TSocketIONotify); + property OnEventError: TSocketIOEventError read FOnEventError write FOnEventError; procedure EnumerateSockets(const aEachSocketCallback: TSocketIONotify); end; @@ -208,7 +212,7 @@ type implementation uses - SysUtils, StrUtils, IdServerWebsocketContext, IdHTTPWebsocketClient, Windows; + StrUtils, IdServerWebsocketContext, IdHTTPWebsocketClient, Windows; procedure TIdBaseSocketIOHandling.AfterConstruction; begin @@ -501,15 +505,15 @@ begin else callback := nil; try + for event in list do try - for event in list do - event(AContext, args, callback); - except - on E:Exception do - begin + event(AContext, args, callback); + except on E:Exception do + if Assigned(OnEventError) then + OnEventError(AContext, callback, e) + else if callback <> nil then - callback.SendResponse( SO(['Error', e.Message]).AsJSon ); - end; + callback.SendResponse( SO(['Error', SO(['msg', e.message])]).AsJSon ); end; finally callback := nil; diff --git a/uROIndyHTTPWebsocketChannel.pas b/uROIndyHTTPWebsocketChannel.pas index 32649a3..7475c55 100644 --- a/uROIndyHTTPWebsocketChannel.pas +++ b/uROIndyHTTPWebsocketChannel.pas @@ -116,18 +116,21 @@ procedure TROIndyHTTPWebsocketChannel.SetHost(const Value: string); begin IndyClient.Host := Value; TargetURL := Format('ws://%s:%d/%s', [Host, Port, WSResourceName]); + FTriedUpgrade := False; //reset end; procedure TROIndyHTTPWebsocketChannel.SetPort(const Value: integer); begin IndyClient.Port := Value; TargetURL := Format('ws://%s:%d/%s', [Host, Port, WSResourceName]); + FTriedUpgrade := False; //reset end; procedure TROIndyHTTPWebsocketChannel.SetWSResourceName(const Value: string); begin IndyClient.WSResourceName := Value; TargetURL := Format('ws://%s:%d/%s', [Host, Port, WSResourceName]); + FTriedUpgrade := False; //reset end; function TROIndyHTTPWebsocketChannel.GetHost: string;