/*==============================================================================================*\
| Builds/Runs on SOLARIS only!
|
| Compile and link as:
|
| 	CC -I$OPENSSLDIR/include -L$OPENSSLDIR/lib -mt -norunpath -I- -DDEBUG -g -D_REENTRANT -DNO_RSA -DNO_RC4 -DNO_RC5 -DNO_BF -DNO_IDEA -I. -lcrypto -lssl -c tst.cpp
| 	CC -o tst tst.o -L$OPENSSLDIR/lib -lssl -lcrypto -lsocket -mt 
|
| Run 'tst' to casue threads to crash.
|-----------------------------------------------------------------------------------------------
|
| !!!! NOTE !!!!
|
|	Use Editor set with HARD TABS at 4 spaces apart or this is unreadable!! 
|
\*==============================================================================================*/
#include <pthread.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <string.h>
#include <unistd.h>

#include "openssl/crypto.h"
#include "openssl/ssl.h"
#include "openssl/err.h"

extern "C" void * ThreadRootStartingPoint	(		void 					* pThreadInstance);

/*----------------------------------------------------------------------------------------------*\
| Sleep_us - high-granularity thread-sleep function (accuracy & precision are platform dependent)
\*----------------------------------------------------------------------------------------------*/
Sleep_us (int iMicroseconds)
{
	// Resist the temptation to make any of these static, as doing so makes us Non-MT-Safe!  
	int iDummySocket;
    fd_set ErrorSockets;
   	iDummySocket = socket (PF_INET, SOCK_STREAM, 0);
    FD_ZERO (&ErrorSockets);
    FD_SET (iDummySocket, &ErrorSockets);

   	struct timeval  tval;
   	tval.tv_sec = iMicroseconds / 1000000;
   	tval.tv_usec = iMicroseconds % 1000000;
   	select(0, NULL, NULL, &ErrorSockets, &tval);

	shutdown (iDummySocket,2);
	close (iDummySocket);

   	return (0);
}

/*----------------------------------------------------------------------------------------------*\
| __cCritSec class [mutexes]
\*----------------------------------------------------------------------------------------------*/
class __cCritSec
{
	public:
		__cCritSec () {pthread_mutex_init (&_mutex, NULL);};

		void Enter () {pthread_mutex_lock (&_mutex);};
		void Leave () {pthread_mutex_unlock (&_mutex);};
										     
		pthread_mutex_t 	_mutex;
};

static __cCritSec ** pcCSCrypto;

/*----------------------------------------------------------------------------------------------*\
| Locking callback routines as prescribed by OpenSSL Docs/examples
\*----------------------------------------------------------------------------------------------*/
extern "C" void crypto_locking_callback(int mode, int type, const char *file, int line)
{
	if (mode & CRYPTO_LOCK)
	{
		pcCSCrypto[type]->Enter ();
	}
	else
	{
		pcCSCrypto[type]->Leave ();
	}
}

void thread_setup (void)
{
	pcCSCrypto = (__cCritSec **)malloc (CRYPTO_num_locks() * sizeof(__cCritSec *));
	for (int i = 0; i < CRYPTO_num_locks (); i++)
	{
		pcCSCrypto[i] = new __cCritSec ();
	}
	CRYPTO_set_locking_callback (crypto_locking_callback);
}

void thread_cleanup (void)
{
	CRYPTO_set_locking_callback (NULL);
	for (int i = 0; i < CRYPTO_num_locks (); i++)
	{
		delete pcCSCrypto[i];
	}
	free (pcCSCrypto);
}

/*----------------------------------------------------------------------------------------------*\
| __cThread class
\*----------------------------------------------------------------------------------------------*/
class __cThread
{
	public:
		__cThread () {	_cCritSec.Enter (); 
							++_iThreadCnt; 
							++_iTotThreads;
						_cCritSec.Leave ();
						pthread_attr_init (&_threadAttr);
				 		pthread_attr_setdetachstate (&_threadAttr, PTHREAD_CREATE_JOINABLE);
						int iError = pthread_create ((pthread_t *)&_hThreadHandle, &_threadAttr, ThreadRootStartingPoint, this);
						if (iError)
						{
							printf ("\nERROR CREATING THREAD!\n");
						}
					};
		~__cThread (){
						_cCritSec.Enter ();
							--_iThreadCnt;
						_cCritSec.Leave ();
					}
		// ThreadMain is the "body" of the thread.  This one just sleeps 1 millisecond and exits
		int ThreadMain () 
					{
			  			Sleep_us (1000);
						return (0);
					};	

		static int 			_iThreadCnt;
		static int 			_iTotThreads;
		static __cCritSec 	_cCritSec;
		pthread_attr_t		_threadAttr;
		int					_hThreadHandle;
};

// Statics for the thread class
int 		__cThread::_iThreadCnt = 0;
int 		__cThread::_iTotThreads = 0;
__cCritSec 	__cThread::_cCritSec;

/*----------------------------------------------------------------------------------------------*\
| ThreadRootStartingPoint
| 
| This is the bootstrap for the __cThread class;  it's called by the C library's "create a thread"  
\*----------------------------------------------------------------------------------------------*/
extern "C" void *
ThreadRootStartingPoint	(		void 					* pThreadInstance)
{
	__cThread * pcThisThread	= (__cThread *)pThreadInstance;

	// Here's where the thread actually runs the user code...

	pcThisThread->ThreadMain ();	

	// When ThreadMain returns, we're done.  Clean up.

	char buf[256];

	int iErr = ERR_get_error ();
	ERR_error_string (iErr, buf);
	ERR_reason_error_string (iErr);
	ERR_remove_state (0);

	pthread_detach (pthread_self());
	pthread_attr_destroy (&pcThisThread->_threadAttr);

	delete pcThisThread;

	return (0);
}

/*----------------------------------------------------------------------------------------------*\
| main
\*----------------------------------------------------------------------------------------------*/
main ()
{	
	int iTargetNum = 80;

	SSL_library_init ();
	SSL_load_error_strings();
	// This is apparently yet another string set to load.  Not sure.  Taken from examples.
	ERR_load_ERR_strings();

	// Set up the locking callbacks
	thread_setup ();

	//-------------------------------------------------------------------------------------------
	// just keep starting new threads as old ones die off, attempting to keep 'iTargetNum' of 
	// them going at once. 
 	int iCnt;
	while (1)
	{
		__cThread::_cCritSec.Enter (); 
		iCnt = __cThread::_iThreadCnt;
		__cThread::_cCritSec.Leave ();
		if (iCnt < iTargetNum)
		{
			// Don't CritSec these accesses - we don't really care if they're exactly correct.
			printf ("#%d/%d ", __cThread::_iThreadCnt, __cThread::_iTotThreads);
			new __cThread ();
		}
		else
		{
			// Just Poll the thread counter by waiting a bit for some threads to die...
			printf (".");
  			Sleep_us (100);
  		}
	}	
}





