Archived

This topic is now archived and is closed to further replies.

ANSI2000

Exception handling.

Recommended Posts

I have the following class...
class WinsockManager
{
public:

	static WinsockManager* getInstance()
	{
		if(instance == NULL)
			instance = new WinsockManager();
		
		return(instance);		
	}
	
	void startUp(WORD pVersion) throw(WinsockException)
	{
		int errorCode = 0;
		
		if(WSAStartup(pVersion, &WSAData) != 0)
		{  
			errorCode = WSAGetLastError();
			
			throw WinsockException("WSAStratup() failed.", errorCode);
		}

		// Make sure that Winsock is the requested version.
		if(WSAData.wVersion != pVersion)
		{
			cleanUp();

			throw WinsockException("Incompatible winsock version.", 0);
		}	
	}
	
	void cleanUp() throw(WinsockException)
	{
		int errorCode = 0;
		
		if(WSACleanup() == SOCKET_ERROR)
		{
			errorCode = WSAGetLastError();

			throw WinsockException("WSACleanup() failed.", errorCode);
		}
	}

protected:

	WinsockManager() {};

private:

	WSADATA WSAData;
    
	static WinsockManager *instance;
};
  
Because cleanUp() throws an exception do I have to catch the exception within startUp() or the exception will climb up the ladder and the try catch block of startUp() will catch the cleanUp() exception? [edited by - ANSI2000 on April 29, 2002 10:54:59 AM]

Share this post


Link to post
Share on other sites
RAII (Resource aqusition is initialization). Hunt google.

Your class is also very weird that it looks like a singleton, but yet you have startup/shutdown methods.

Share this post


Link to post
Share on other sites
I use a simple static instance to call the WSAStartUp & WSACleanUp. No need to make them throw or anything, if WSAStartp fails, so will your first call to create a socket, you can gracefully detect the error then. You could make the WSAInit singleton assert on failure, but I wouldn''t have it throw.

Throwing in ctor''s is deep magic - throwing in dtor''s is black magic. Don''t do either. xps not with MSVC - you can''t always write a throwing ctor in MSVC correctly, and you can''t ever write an air-tight throwing dtor. There''s at least one item in www.gotw.com devoted to this topic (throwing ctor''s).

Use a factory instead, and have it automagically call the intialization code for the objects. It can fail gracefully then (i.e. clean-up properly), beit an exception, error code, or null return.

The manager could call WSAStartUp in it''s ctor and WSACleanUp in it''s dtor, but don''t throw if they fail - Press On Regardless.
Let the Create<TCPSocket> method fail.

Share this post


Link to post
Share on other sites
What is wrong with throwing within Ctors? If you follow RAII closely, then throwing within ctor are fine.

In this specific example, we require a class that does this

  
class WSAResource
{
public:
WSAResource()
{
if ( ! WSAStartup(..) )
throw;
}

~WSAResource()
{
WSACleanup();
}
};


The winsock manager singleton declares a static stack instance of this


WSAResource wsaresource_;


So the ctor of WSAResource completes, you would have initialized winsock. If any exception occurs, the dtor would call WSACleanup.

Share this post


Link to post
Share on other sites
Well my class is a singleton... And I need it to be that way...

As for detecting errors I need to know if Winsock has initilaized correctly if so I do not execute the rest of the application and just quit right away. My application is a "high" performance banking "broker"... The app acts as "router" it listens on one socket receiving transactions queing them and finally sending them to the financial institution. The bank finally responds, teh application matches the transaction within the queue and sends back the response to the client that connected to the listening socket. Winsock is the most important factor of the app. In my WinsockManager singleton, I always checked to see the if Winsock initialized properly, but not if it didn''t. But I have ran into this problem...

The connection to the bank must be persistant as in always connected... Now teh case is because am in development... I shut down my app often making changes... The problem arises that sometimes when I restart my application, either the application will connect but no data will go throuogh to the bank when Isend it, or the application will not reconnect period.... The connection to the bank is through a VPN.

So I thought let me catch the WSACleanup errors to see if Winsock is freeing all resources... So I guess the simplest would be just to print out the error... Instead of catching an exception...

Which this brings me to my other post have you guys had a chance to look at that one?

Share this post


Link to post
Share on other sites
Do you mean dtors should always complete?

Ctors can throw, and it is a legitimate (only?) way to signal a constructor failure.

Without throwing an exception in ctor, you have no way to signal a ctor failure. The other solution is to have a method to test if the object is valid before use. (That is klunky)

At the most unlikely case, what happens if you want to create an object but there is not enough memory? You throw an OutOfMemory exception of course.

Share this post


Link to post
Share on other sites
quote:
Original post by ANSI2000
So I thought let me catch the WSACleanup errors to see if Winsock is freeing all resources... So I guess the simplest would be just to print out the error... Instead of catching an exception...

Which this brings me to my other post have you guys had a chance to look at that one?


If you make use WSACreate is called successfully (using the RAII class), WSACleanup will not failed. The only time when WSACleanup would fail is because you never called WSACreate(). Have faith with these C APIs.

If data failed to send through the winsock singleton, it is consider quite a common activity. Throw only an exception if you want the whole application to quit. An error code would be more appropriate to indicate the data is sent successfully or not.

I''m no winsock expert but I thought you need to rephrase your question. I suspect most people don''t understand what you are asking.

Share this post


Link to post
Share on other sites
I have the WinsockManger class which is a singleton. It allows me to initialize and cleanup Winsock in my app. I also have 2 wrapper classes: Socket and ServerSocket.

Here is how I go about connecting to a server machine using my wrapper classes... Pretend Winsock got initialized and is working...



    
// In connection thread...

// Loop...

if(!bankSockConnect)
{
try
{
printf("Connecting to bank...\n");
// The connection is done through the ctor. Instatiate a new class and assign it to the bankSock....

Socket sock("255.255.255.255", 99999);
bankSock = sock; // <----- bank sock is a private member, shared by 3 threads Connection, Read and Send.

bankSockConnect = true;
printf("Connected to bank.\n");
}
catch(WinsockException &ex)
{
bankSockConnect = false;
printf("Failed to connect to bank.\n");
// Log error...

}
}

// In Send thread...


// Loop...

if(bankSockConnect)
{
// Find trx in queue

try
{
bankSock.write(...);
}
catch(WinsockException &ex)
{
// If a Winsock error occured the connection, must be down

// reset the flag for the connection thread to reconect...

bankSockConnect = false;
// Log error...

}
}

// In Read thread...


// Loop...

if(bankSockConnect)
{
try
{
bankSock.read(...);
}
catch(WinsockException &ex)
{
// If a Winsock error occured the connection, must be down

// reset the flag for the connection thread to reconect...

bankSockConnect = false;
// Log error...

}
// Queue Response....

}


I try reconnection on any Winsock error.. This does not matter because some errors will not occur, such us blocking socket errors, or winsock not beeing initialized etc...

Now the problem is...

My application connects. I send a trx to my application. My app send it to the bank I receive a response from the bank and am happy and it goes on since the connection is persistant. So I know the code works... Now for x reason I stop my app. My app is a service running as a console for debug purposes. I trap the ctrl+c key and call 2 methods... stop(), shutdown()... stop() gets called for every ctrl+c pressed, shut down happens once when the service finally exits...


//In stop()

// Make sure stop is only called once. Since the app can be busy while pressing CTRL+L

try
{
bankSock.closeSocket()
}
catch(WinsockExceptio &ex)
{
// make sure the socket was indeed cleaned up. if not just print the error code for debug purposes related to my problem...

}

// Use WinsockManager to shut down Winsock....



The problem occurs when I shut down my app... The only reason why I put catch statement in my closeSocket() method and cleanup() method is to see if the Winsock resources where indeed freed... So... I shutdown my app... No exceptions are trhown...

I give it a few seconds either cause I make a mod etc and recompile.. or just wait a few seconds and then I restart my app... The connection thread will not throw an exception and will display connected to bank. Then I will put trx through the app, the send thread will not throw an exception, so I assum the data should have went through to the bank. but the bank claims that it did not receive the data. Also, once this has happenned, because it does not seems that any data is going through, when I shut down my app, the FIN packet does not go through, I think... So the bank on there side still claims that we are connected, when we are not. So then when i restart my app, it can no longer connect, untill I tell the bank reset your side... All this is going through a VPN....

It might be a network problem the only reason I made the post is maybe I wasn't handling my exception handling or my class implementation where wrong....


[edited by - ANSI2000 on May 1, 2002 12:40:38 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by Oluseyi
ctors always complete. That''s why you shouldn''t throw within them.



That is incorrect. It is well known that if a valid object cannot be constructed an exception should be thrown.

For example, the constructor for a positive number wrapper should throw an exception if it receieves a non-negative number. Doing so means that you can always rely on those objects being valid which reducing bugs and having to check manually.

You should never throw in a deconstructor though. They are called when expceptions are throw so what is the program to do if a destructor throws another exception during stack unwinding? There are solutions that lose information, but the default behaviour is to terminate the program.

Share this post


Link to post
Share on other sites
Stroustrup says that an object's destructor will only be called if it's constructor completes. Throwing in a constructor will NOT cause the destructor to be called. If MSCV does this it's a compiler bug or non-ansi behavior (i.e. a feature).

If you're throwing in the constructor you're most likely not calling the necessary cleanup code, which may be why it tells you you're still connected, even though reconnecting fails.

Just my $.02.

-- Succinct

[edited by - Succinct on May 1, 2002 7:49:03 PM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by Succinct
Stroustrup says that an object''s destructor will only be called if it''s constructor completes. Throwing in a constructor will NOT cause the destructor to be called. If MSCV does this it''s a compiler bug or non-ansi behavior (i.e. a feature).



Where did anyone say this?

I just said you shouldn''t throw in a destructor.

Share this post


Link to post
Share on other sites
quote:
Original post by ANSI2000
I have the WinsockManger class which is a singleton. It allows me to initialize and cleanup Winsock in my app. I also have 2 wrapper classes: Socket and ServerSocket.



I'm no winsock expert, so this may not apply.

Are you trying to recreate the connection for everytime you want to talk to the bank? Shouldn't you initialize winsock once, maintain the connection, and shutdown when you are done?


    

int main()
{
try
{
// This initializes winsock. See above post.

// WSACleanup is always called always


WSAResource wsa_resource;

// your sending code here with sockets.


// RAII socket class

Socket socket;
}
catch(...)
{
}
};


If you need rollback capability when sending fails, look around for "scopeguard" article at C++ User Journal www.cuj.com


[edited by - void on May 1, 2002 11:05:36 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by Void
Do you mean dtors should always complete?

Ctors can throw, and it is a legitimate (only?) way to signal a constructor failure.

Without throwing an exception in ctor, you have no way to signal a ctor failure. The other solution is to have a method to test if the object is valid before use. (That is klunky)

At the most unlikely case, what happens if you want to create an object but there is not enough memory? You throw an OutOfMemory exception of course.

It''s a *very* sticky issue throwing in ctor''s & dtor''s. With MSVC you cannot write throwing ctor''s correctly because you can''t properly catch thrown exceptions there to clean-up your half-initialized state. Let us suppose that we have a heavily derived class, that calls several base class ctor''s. And one in the middle throws - casting us out of the parent ctor before the remaining base ctor''s were executed but after we''ve allocated resource in the other base ctor''s. Should the dtor be invoked? No. The object doesn''t exist if the ctor doesn''t complete. So you now have a potential resource leak. Suppose the compiler did, or you explicitly invoked the dtor on the half-baked object. As mention not all of the ctor''s were invoked, so some of that RAM may be uninitialized spurious crap left over on the stack or heap. If you delete one of them you now have everyone''s favorite ''undefined'' behavior in a dtor. Or you opt leak.

quote:

If you''re throwing in the constructor you''re most likely not calling the necessary cleanup code, which may be why it tells you you''re still connected, even though reconnecting fails.


This is the crux of the problem. How exactly would one invoke this non-dtor, only-call-if-my-ctor-excepts clean-up code?
It takes a special kind of ctor exception handler, that most compilers don''t support. And even then it''s voodoo, because you have to divine initialized holes from unintialized holes. It can be done, but doing it right is a cumbersome task.

I have *never* viewed a well-defined throwing ctor in production code. The only place I have ever gazed in awe is on www.gotw.com. IIRC, the orginal solution to the throwing ctor problem presented there took experts in the field about 5 years to construct, and they had to change the language to do it. (I''m pretty sure this was the problem that was all about.)

In Ansi2000''s specific case you could throw; if the WSAStartUp function fails, then no resources have been allocated and you''re safe so long as you and no-one else ever dervies from it and another class that allocates resources. (Suppose you want to make a class automatically startup & cleanup WSA services when it''s constructed and destructed, which is one method I fiddled with). That''s not a safe assumption to make - unless you make the ctor''s private.

The lesson is, don''t throw in ctor''s or dtor''s. And then you don''t ever have to worry about it. But like, goto, there are a couple of cases where you can use if you want to because it''s safe and reasonable to do so. A well-declared throwing singleton is one of them. I still don''t recommend it.

In Java the story''s different because it pools system memory & resources - er "garbage collects" - and there''s no MI.

Black Magik I say!


"If arithmetic overflow is a fatal error, [then] some fascist pig with a read-only mind is trying to enforce machine independence." - Bill Gospher HAKMEM

Share this post


Link to post
Share on other sites
quote:
Original post by Magmai Kai Holmlor
With MSVC you cannot write throwing ctor''s correctly because you can''t properly catch thrown exceptions there to clean-up your half-initialized state. Let us suppose that we have a heavily derived class, that calls several base class ctor''s. And one in the middle throws - casting us out of the parent ctor before the remaining base ctor''s were executed but after we''ve allocated resource in the other base ctor''s. Should the dtor be invoked? No. The object doesn''t exist if the ctor doesn''t complete. So you now have a potential resource leak.




I disagree.

If the base ctor is completed and the dervied child ctor throws, the base dtor will be called. Only the dtor for the derived class will not be called.


  
#include <iostream>
using namespace std;

class Base
{
public:

Base()
{
cout << "Base ctor\n";
}

~Base() throw()
{
cout << "Base dtor\n";
}
};

class Derived : public Base
{
public:
Derived() : Base()
{
throw 100;
}

~Derived()
{
cout << "Derived dtor\n";
}
};


int main()
{
try
{
Derived d;
}
catch(...)
{
}

return 0;
}


Which VC++ are you using? VC++ 6, gcc 2.95 and comeau all agree with me.

The problem to me seems that you are not using RAII correctly.
RAII is about 1 class managing 1 resource so if the ctor completes, it means the dtor is always valid to be called.

Helpful reading (duh) at
http://www.chrisnewton.btinternet.co.uk/pp/resource.htm

Share this post


Link to post
Share on other sites
1st my WinsockManger class is a singelton if you havent noticed so there is no ctor or dtor, I mean there is the default but am not overiding them for my use. Though my Socket and ServerSocket classes ctors do throw exceptions, I do not implement anything in the dtors of those classes.

Here is the code for my Socket class...


    
#ifndef SOCKET_H
#define SOCKET_H

// Ignore this warning. MS has implemented exception handling

// but does not support it.

#pragma warning(disable : 4290)

// Required include files.

#include <stdio.h>

// Application include files.

#include "winsockexception.h"

class Socket
{
public:

Socket() {};
Socket(char *pIPAddress, int pPort, int pTimeOut = 0) throw(WinsockException);
Socket(SOCKET pSocket) : sock(pSocket) {};
~Socket() {};

void write(char * pBuffer,int pBufferLength) throw(WinsockException);
int read(char *pBuffer, int pBufferLength) throw(WinsockException);
void closeSocket() throw(WinsockException);

int getTimeOut() { return(timeOut); };

protected:

void setTimeOut(int pTimeOut) { timeOut = pTimeOut; };

private:

sockaddr_in serverAddress;
SOCKET sock;

int timeOut;
};

#endif

// Required include files.

#include <winsock2.h>

// Application include files.

#include "socket.h"

// Can throw since no dynmaic data is allocated...

Socket::Socket(char *pIPAddress, int pPort, int pTimeOut)
{
int errorCode = 0;

setTimeOut(pTimeOut);

if((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
errorCode = WSAGetLastError();

throw WinsockException("socket() failed.", errorCode);
}

int optionValue = getTimeOut();

if((optionValue != 0) || (optionValue != NULL))
{
// Set the socket send timeout.

if(setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&optionValue, sizeof(optionValue)) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("setsockopt() failed.", errorCode);
}

// Set the socket read timeout.

if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&optionValue, sizeof(optionValue)) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("setsockopt() failed.", errorCode);
}
}

memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = inet_addr(pIPAddress);
serverAddress.sin_port = htons(pPort);

if(connect(sock, (struct sockaddr *) &serverAddress, sizeof(serverAddress)) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("connect() failed.", errorCode);
}
}

void Socket::write(char * pBuffer,int pBufferLength)
{
int errorCode = 0;
int bytesSent = 0;

bytesSent = send(sock, pBuffer, pBufferLength, 0);

if(bytesSent == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("send() failed.", errorCode);
}
}

int Socket::read(char *pBuffer, int pBufferLength)
{
int errorCode = 0;

char *buffer = new char[pBufferLength];
int bytesReceived = 0;

bytesReceived = recv(sock, buffer, pBufferLength, 0);

if((bytesReceived == SOCKET_ERROR) || (bytesReceived == 0))
{
errorCode = WSAGetLastError();

throw WinsockException("recv() failed.", errorCode);
}

memcpy(pBuffer, buffer, bytesReceived);

return(bytesReceived);
}

// Make it throw to exception to see the source of the problem. For debuging can be removed...

void Socket::closeSocket()
{
int errorCode = 0;

if(closesocket(sock) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("closesocket() failed.", errorCode);
}
}



Here is the code of my ServerSocket class...


        
#ifndef SERVERSOCKET_H
#define SERVERSOCKET_H

// Ignore this warning. MS has impemented exception handling

// but does not support it.

#pragma warning(disable : 4290)

// Required include files.

#include <stdio.h>

// Application include files.

#include "winsockexception.h"
#include "socket.h"

class ServerSocket
{
public:

ServerSocket() {};
ServerSocket(int pPort, int pBackLog = SOMAXCONN, int pTimeOut = 0) throw(WinsockException);
~ServerSocket() {};

Socket acceptSocket();
void closeSocket() throw(WinsockException);

int getPort() { return(port); };
int getBackLog() { return(backLog); };
int getTimeOut() { return(timeOut); };

protected:

void setPort(int pPort) { port = pPort; };
void setBackLog(int pBackLog) { backLog = pBackLog; };
void setTimeOut(int pTimeOut) { timeOut = pTimeOut; };


private:

struct sockaddr_in serverAddress;
struct sockaddr_in clientAddress;
SOCKET serverSocket;

int port;
int backLog;
int timeOut;
};

#endif

// Required include files.

#include <winsock2.h>

// Application include files.

#include "serversocket.h"

// Should not matter if exception is thrown...

ServerSocket::ServerSocket(int pPort, int pBackLog, int pTimeOut)
{
int errorCode = 0;

setPort(pPort);
setBackLog(pBackLog);
setTimeOut(pTimeOut);

if((serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
errorCode = WSAGetLastError();

throw WinsockException("socket() failed.", errorCode);
}

int optionValue = getTimeOut();

if((optionValue != 0) || (optionValue != NULL))
{
// Set the socket send timeout.

if(setsockopt(serverSocket, SOL_SOCKET, SO_SNDTIMEO, (char *)&optionValue, sizeof(optionValue)) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("setsockopt() failed.", errorCode);
}

// Set the socket read timeout.

if(setsockopt(serverSocket, SOL_SOCKET, SO_RCVTIMEO, (char *)&optionValue, sizeof(optionValue)) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("setsockopt() failed.", errorCode);
}
}

memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(getPort());

if(bind(serverSocket, (LPSOCKADDR)&serverAddress,sizeof(serverAddress)) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("bind() failed.", errorCode);
}

if(listen(serverSocket, getBackLog()) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("listen() failed.", errorCode);
}
}

Socket ServerSocket::acceptSocket()
{
int errorCode = 0;
SOCKET clientSocket;
int clientAddressSize;

clientAddressSize = sizeof(clientAddress);

if((clientSocket = accept(serverSocket, (LPSOCKADDR)&clientAddress, &clientAddressSize)) == INVALID_SOCKET)
{
errorCode = WSAGetLastError();

throw WinsockException("accept() failed.", errorCode);
}

Socket sock(clientSocket);

return(sock);
}

// Throw for debug purposes...

void ServerSocket::closeSocket()
{
int errorCode = 0;

if(closesocket(serverSocket) == SOCKET_ERROR)
{
errorCode = WSAGetLastError();

throw WinsockException("closesocket() failed.", errorCode);
}
}



Here is the class of my WinsockManager singleton class...


        
#ifndef WINSOCKMANAGER_H
#define WINSOCKMANAGER_H

// Ignore this warning. MS has impemented exception handling

// but does not support it.

#pragma warning(disable : 4290)

// Required include files.

#include <stdio.h>

// Application include files.

#include "winsockexception.h"

class WinsockManager
{
public:

static WinsockManager* getInstance()
{
if(instance == NULL)
instance = new WinsockManager();

return(instance);
}

void startUp(WORD pVersion) throw(WinsockException)
{
int errorCode = 0;

if(WSAStartup(pVersion, &WSAData) != 0)
{
errorCode = WSAGetLastError();

throw WinsockException("WSAStratup() failed.", errorCode);
}

// Make sure that Winsock is the requested version.

if(WSAData.wVersion != pVersion)
{
cleanUp();

throw WinsockException("Incompatible winsock version.", 0);
}
}

void cleanUp() throw(WinsockException)
{
int errorCode = 0;

WSACleanup();
}

protected:

WinsockManager() {};

private:

WSADATA WSAData;

static WinsockManager *instance;
};

#endif


Here is the code I wrote for writting an NT service...


        
#ifndef FEDSSERVICE_H
#define FEDSSERVICE_H

#define _WIN32_DCOM

// Required for ADO

#import "C:\program files\common files\system\ado\msado15.dll" no_namespace rename("EOF", "adoEOF")

// Required include files.

#include <stdio.h>
#include <objbase.h>

// Application include files.

#include "service.h"
#include "utilities.h"
#include "winsockmanager.h"
#include "serversocket.h"

typedef struct _PooledSocket
{
bool active;
Socket sock;
} PooledSocket, *LPPooledSocket;

typedef struct _ReadQueue
{
bool active;
char buffer[4096];
int bufferLength;
} ReadQueue, *LPReadQueue;

typedef struct _SendQueue
{
bool active;
bool sent;
int id;
char buffer[4096];
int bufferLength;
LPPooledSocket pooledSocket;
int minutes;
int seconds;
} SendQueue, *LPSendQueue;

class BANKService: public Service
{
public:

BANKService(LPTSTR pApplicationName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependencies);

bool getConfigurationValues();
void init();
void start(DWORD dwArgc, LPTSTR *lpszArgv);
void stop();
void shutDown();

void setStatus(bool pStatus) { status = pStatus; }
bool getStatus() { return(status); }

static DWORD WINAPI FEDSConnectionThread(LPVOID p);
static DWORD WINAPI FEDSReadThread(LPVOID p);
static DWORD WINAPI FEDSSendThread(LPVOID p);
static DWORD WINAPI cleanThread(LPVOID p);
static DWORD WINAPI processThread(LPVOID p);
static DWORD WINAPI reconciliationThread(LPVOID p);
static DWORD WINAPI serverThread(LPVOID pPooledSocket);
static DWORD WINAPI supportThread(LPVOID p);

~BANKService();

private:

// Configuration values.

char logsLocation[256];
int BANKId;
int serverPort;
int supportPort;
int maxRequests;
int queueSize;
int queueTimeout;
int transactionTimeout;
int reconciliationTime;
char BANKIP[128];
int BANKPort;
char dbServer[256];
char dbName[256];
char dbUsername[256];
char dbPassword[256];

bool status;
bool stopped; // Flag preventing multiple stop() calls.

HANDLE logMutex;

// Queue structures.

LPReadQueue readQueue;
HANDLE readQueueMutex;
LPSendQueue sendQueue;
HANDLE sendQueueMutex;

// Handles for each thread.

HANDLE connectionHandle;
HANDLE readHandle;
HANDLE sendHandle;
HANDLE cleanHandle;
HANDLE processHandle;
HANDLE reconciliationHandle;
HANDLE serverHandle;
HANDLE supportHandle;

// Socket structures.

Socket BANKSocket;
bool BANKSocketConnected;
LPPooledSocket pooledSockets;
ServerSocket serverSocket;
ServerSocket supportServerSocket;

// Static pointer to Service. Used for calling non static methods within

// the call back methods.

static BANKService *thisBANKService;
};

#endif


BANKService implement a base class called Service, which has all the Win32 service code etc... There are 4 abstract methods that get overiden by BANKService... init(), start(...), stop(), shutdown().

init() gets called once at the startup of the service, then start() gets called. Start is the main application method... stop() gets called once every time CTRL+C is pressed. The Service class has callbacks to handle the CTRL+L. Once start() exits or returns, shutdown() gets called...

So in my code I do the following...


BankService::BANKService(LPTSTR pApplicationName, LPTSTR pServiceName, LPTSTR pServiceDisplayName, LPTSTR pDependencies):Service(pApplicationName, pServiceName, pServiceDisplayName, pDependencies)
{
thisBANKService = this;
}

BANKService::init()
{
// Initialize COM.

CoInitializeEx(NULL, COINIT_MULTITHREADED);

// Set all flags here.

setStatus(false);
stopped = false;
BANKSocketConnected = false;

// Report the status to the Service Control Manager.

if(ReportStatusToSCMgr(SERVICE_START_PENDING, NO_ERROR, 3000))
{
try
{
printf("Initializing Winsock...\n");
WinsockManager::getInstance()->startUp(MAKEWORD(2,2));
printf("Winsock initialized.\n");

// Init more stuff here...

setStatus(true); // <---- Used in start to run service in a loop

}
catch(WinsockException &ex)
{
printf("Failed to initialize Winsock.\n");
printf("%s", ex.getExceptionMessage(__FILE__, __LINE__));
Utilities::getInstance()->log(logsLocation, "FEDS", "Failed to initialize Winsock.\n");

stop();
}
}
else
{
if(isConsole())
printf("Error occurred trying to update service control manager.\n");
else
AddToMessageLog("Error occurred trying to update service control manager.\n", EVENTLOG_ERROR_TYPE);
}
}

void BANKService::start(DWORD dwArgc, LPTSTR *lpszArgv)
{
// Report the status to the Service Control Manager.

if (ReportStatusToSCMgr(SERVICE_RUNNING, NO_ERROR, 0))
{
// Make sure the init stage was executed correctly before

// proceeding.

if(getStatus())
{
// Create necessary threads.

printf("Creating threads...\n");
connectionHandle = CreateThread(NULL, 0, FEDSConnectionThread, NULL, 0, NULL);
readHandle = CreateThread(NULL, 0, FEDSReadThread, NULL, 0, NULL);
sendHandle = CreateThread(NULL, 0, FEDSSendThread, NULL, 0, NULL);
processHandle = CreateThread(NULL, 0, processThread, NULL, 0, NULL);
reconciliationHandle = CreateThread(NULL, 0, reconciliationThread, NULL, 0, NULL);

// Make sure all thread are created etc...

// and take appropriate action...


while(getStatus())
{
// Main loop to keep service running. Once this loop

// exits shutdown() gets called.

}

}
else
{
if(isConsole())
printf("Error occurred trying to update service control manager.\n");
else
AddToMessageLog("Error occurred trying to update service control manager.\n", EVENTLOG_ERROR_TYPE);
}
}

void BANKService::stop()
{
if(!stopped)
{
// Report the status to the service control manager.

ReportStatusToSCMgr(SERVICE_STOP, NO_ERROR, 0);

setStatus(false);
BANKSocketConnected = false;

printf("Stopping service...\n");

try
{
FEDSSocket.closeSocket();
serverSocket.closeSocket();
supportServerSocket.closeSocket();
WinsockManager::getInstance()->cleanUp();
}
catch(WinsockException &ex)
{
printf("WARNING: Error occured while cleaning Winsock resources.%s", ex.getExceptionMessage(__FILE__, __LINE__));
WaitForSingleObject(thisFEDSService->logMutex, INFINITE);
Utilities::getInstance()->log(thisFEDSService->logsLocation, "FEDS", ex.getExceptionMessage(__FILE__, __LINE__));
ReleaseMutex(thisFEDSService->logMutex);
}

printf("Service stopped.\n");
WaitForSingleObject(logMutex, INFINITE);
Utilities::getInstance()->log(logsLocation, "FEDS", "Service stopped.\n");
ReleaseMutex(logMutex);
}

stopped = true;
}

void BANKService::shutDown()
{
printf("Shuting down service...\n");

// Final cleanup.

if(readQueue != NULL)
delete readQueue;

if(readQueueMutex != NULL)
{
ReleaseMutex(readQueueMutex);
CloseHandle(readQueueMutex);
}

if(sendQueue != NULL)
delete sendQueue;

if(sendQueueMutex != NULL)
{
ReleaseMutex(sendQueueMutex);
CloseHandle(sendQueueMutex);
}

if(connectionHandle != NULL)
CloseHandle(connectionHandle);

if(readHandle != NULL)
CloseHandle(readHandle);

if(sendHandle != NULL)
CloseHandle(sendHandle);

if(cleanHandle != NULL)
CloseHandle(cleanHandle);

if(processHandle != NULL)
CloseHandle(processHandle);

if(supportHandle != NULL)
CloseHandle(supportHandle);

if(serverHandle != NULL)
CloseHandle(serverHandle);

if(pooledSockets != NULL)
delete pooledSockets;

if(thisFEDSService != NULL)
thisFEDSService = NULL;

// UnInitialize COM.

CoUninitialize();

printf("Service shut down.\n");
Utilities::getInstance()->log(logsLocation, "FEDS", "Service shut down.\n");
}

BANKService::~BANKService()
{
thisBANKService = NULL;
}

DWORD WINAPI BANKService::FEDSConnectionThread(LPVOID p)
{
while(thisFEDSService->getStatus())
{
if(!thisFEDSService->FEDSSocketConnected)
{
try
{
printf("Connect - Connecting to FEDS...\n");
WaitForSingleObject(thisFEDSService->logMutex, INFINITE);
Utilities::getInstance()->log(thisFEDSService->logsLocation, "Connect", "Connecting to FEDS...\n");
ReleaseMutex(thisFEDSService->logMutex);

Socket sock(thisBANKService->BANKSIP, thisBANKService->BANKSPort);

thisBANKService->BANKSocket = sock;

printf("Connect - Connected to FEDS.\n");
WaitForSingleObject(thisFEDSService->logMutex, INFINITE);
Utilities::getInstance()->log(thisFEDSService->logsLocation, "Connect", "Connected to FEDS.\n");
ReleaseMutex(thisFEDSService->logMutex);

thisFEDSService->BANKSocketConnected = true;
}
catch(WinsockException &ex)
{
printf("Connect - Error occured while connecting to FEDS.\n%s", ex.getExceptionMessage(__FILE__, __LINE__));
WaitForSingleObject(thisFEDSService->logMutex, INFINITE);
Utilities::getInstance()->log(thisFEDSService->logsLocation, "Connect", ex.getExceptionMessage(__FILE__, __LINE__));
ReleaseMutex(thisFEDSService->logMutex);

thisBANKService->BANKSocketConnected = false;
}
}

Sleep(30000);
}
return(1);
}

// Similar is done in read and write threads...



[edited by - ANSI2000 on May 2, 2002 10:20:54 AM]

Share this post


Link to post
Share on other sites
The only reason why I trow an exception when calling WSACleanup is to use my standard error handlin in my app I just wanted to see if any error message do occur, while shutting down, since the 3rd party "bank" was claiming that there application is fine and am not freeing socket resources. Which I think I am. They do receive the FIN packet, but some how after that the sockets are all screwy, look further up in my posts to see what happens...

Share this post


Link to post
Share on other sites
I don't know much about socket codes but I see a glaring problem with your singleton now.

You are using a pointer as the singleton instance in Get_Instance. Who will delete that when program quits or when an exception is thrown?

Remember, RAII. Your singleton must be able to delete itself. WSAStartup/WSAShutdown needs to be in ctor/dtor respectively on a stack variable, not pointer.

Go read the article "An exceptional quest" here in gamedev also or wait for another upcoming article on singletons.

Note: There is another thread about singleton going on currently. It seems to be related.

[edited by - void on May 2, 2002 11:07:36 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by Void

I disagree.

If the base ctor is completed and the dervied child ctor throws, the base dtor will be called. Only the dtor for the derived class will not be called.


Argh, I mixed up the problem with the solution.
This is the gotw.
The problem is when you don''t use RAII, and ''new'' multiple things in the derived ctor body - then there''s a potential resource leak. The missing syntax in MSVC is the ctor exception translator.

My apologizes, I''ll make a point of double checking my facts before I spout off non-sense again.

Share this post


Link to post
Share on other sites
Liked I said the only reason why I wanted to catch a cleanUp exception is to use my standard way of error handling and see what the WSA error was... I call cleanUp() once inside startUp() only if Winsock dont initialize, which will only throw and exception after only clean up was called... My question was that if clean up throws an exception before start up throws one will it be caught by the try catch block of the startUp() call...

Any how I call cleanUp once at the end in my services shutdown() method... You must call WSACleanup for every WSAStartup call, you can also call WSACleanup as many times as you wish, but eventually it will fail since all of winsock will have been cleaned up...

Share this post


Link to post
Share on other sites
Either you didn''t paste the code correctly or something amiss but I don''t see a try/catch block inside startUp() that tries to suppress exceptions thrown by cleanUp(). And of course if cleanup() throws, this is never called.


// Make sure that Winsock is the requested version.
if(WSAData.wVersion != pVersion)
{
cleanUp();

// THIS MAY NEVER GET CALLED
throw WinsockException("Incompatible winsock version.", 0);
}


You are doing something similar to throwing in a dtor, which is non RAII.

Keep an instance of the WSAResource (see above post) as the first member variable in your WinsockManager. Then WSAStartup will be called first when you initialize the manager, and WSACleanup will be called last when you delete the manager.

The alternative is to not call a throwable Cleanup() but called WSACleanup() directly inside startup(). As I said, have faith that the cleanups calls do not fail unless you misused the setup.

Share this post


Link to post
Share on other sites