/* mt/cb/mt_win32.c */

/* Copyright (C) 1999-2000, RSA Security Development Pty Ltd.
 *
 * This file shall only be used to demonstrate how to interface to an
 * RSA Security Development Pty Ltd licensed development product.
 *
 * You have a royalty-free right to use, reproduce and distribute this 
 * demonstration file, provided that you agree that RSA Security 
 * Development Pty Ltd has no warranty, implied or otherwise, or
 * liability for this demonstration file.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <windows.h>
#ifdef OPENSSL
#include <openssl/ssl.h>
#else
#include "sslc.h"
#endif
#include "dmalloc.h"  // this may break it!!! 
#ifndef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE       (16*1024)
#endif

static HANDLE *lock_cs=NULL;
static long *lock_count=NULL;
static APP_THREAD_ID *thread_ctx=NULL;
static int N_thread_ctx=0;
static int num_locks=0;
static int num_threads=0;

static int show_trace=0;

int APP_locking_cb_add()
	{
	int i;

	num_locks=CRYPTO_get_num_locks()+1;

#ifdef APP_LOCK_TRACE
        if (show_trace)
	    fprintf(stderr,"APP_locking_cb_add: num_locks %d\n",                                                            num_locks);
#endif /* APP_LOCK_TRACE */

        /* create a table to hold a lock counter (stats) and the
         * locks themselves (semaphores)
         */
        lock_count=(long *)malloc(sizeof(long)*num_locks);
	lock_cs=(HANDLE *)malloc(sizeof(HANDLE)*num_locks);

        /* initialise each lock */
        for (i=0; i<num_locks; i++)
                {
                lock_count[i]=0;
		lock_cs[i]=CreateMutex(NULL,FALSE,NULL);
                }

        /* register the callbacks now */
        CRYPTO_set_id_cb((unsigned long (*)(void))APPCB_thread_id);
#ifdef OPENSSL
		CRYPTO_set_locking_cb((int (*)(int,int,const char *,int))APPCB_locking_cb);
#else
		CRYPTO_set_locking_cb((int (*)(int,int,char *,int))APPCB_locking_cb);
#endif
        return(1);
	}

int APP_locking_cb_delete()
	{
	int i;

        /* clear the callback */
        /* note: we leave in the id_callback for the error stack */
	CRYPTO_set_locking_cb(NULL);

	for (i=0; i<num_locks; i++)
		CloseHandle(lock_cs[i]);
	free(lock_cs);
        free(lock_count);
        lock_cs=NULL;
        lock_count=NULL;
        return(1);
	}

unsigned long APPCB_thread_id(void)
        {
        unsigned long ret;

        ret=(unsigned long)GetCurrentThreadId();
        return(ret);
        }

int APPCB_locking_cb(mode,lockid,file,line)
int mode;
int lockid;
char *file;
int line;
	{

#ifdef APP_LOCK_TRACEx
        if (show_trace)
	    fprintf(stderr,"thread=%4d mode=%s lock=%s %d(%s) %s:%d\n",
		CRYPTO_thread_id(),
		(mode&CRYPTO_LOCK)?"l":"u",
		(lockid&CRYPTO_READ)?"r":"w",
		lockid,CRYPTO_get_lock_name(lockid),file,line);
#endif /* APP_LOCK_TRACE */

	if (mode & CRYPTO_LOCK)
		{
		WaitForSingleObject(lock_cs[lockid],INFINITE);
                lock_count[lockid]++;
		}
	else
		{
		ReleaseMutex(lock_cs[lockid]);
		}

#ifdef APP_LOCK_TRACEx
        if (show_trace)
	    fprintf(stderr,"thread=%4d mode=%s lock=%s %d(%s) %s:%d DONE\n",
		CRYPTO_thread_id(),
		(mode&CRYPTO_LOCK)?"l":"u",
		(lockid&CRYPTO_READ)?"r":"w",
		lockid,CRYPTO_get_lock_name(lockid),file,line);
#endif /* APP_LOCK_TRACE */
	return(1);
	}

int APP_thread_initialize(thread_count)
int thread_count;
{
        int ret=0;

        thread_ctx=(APP_THREAD_ID *)malloc(sizeof(APP_THREAD_ID)*thread_count);
        if (thread_ctx!=NULL)
                {
                N_thread_ctx=thread_count;
                memset(thread_ctx,0,sizeof(APP_THREAD_ID)*thread_count);
                ret=1;
                }
        num_threads=0;
        return(ret);
}


int APP_thread_create(void (*func)(void *arg),void *func_arg,
                                        APP_THREAD_ID *thread_id)
        {
        APP_THREAD_ID tid;    /* actually a handle */
	DWORD the_id;

	tid=CreateThread(NULL,THREAD_STACK_SIZE,
			(LPTHREAD_START_ROUTINE)func,
			(void *)func_arg,
			0L,
			&the_id);

        /* track the thread id for cleanup later */
        if (num_threads<N_thread_ctx)
            thread_ctx[num_threads]=tid;

        num_threads++;

        /* copy the thread id back if the caller wanted it */
        if (thread_id!=NULL)
                (*thread_id)=tid;

        return(1);
        }

int APP_thread_finalize(thread_number)
int thread_number;
{
        int i;
        int ret=1;

#ifdef APP_LOCK_TRACE
        if (show_trace)
            fprintf(stderr,"APP_thread_finalize: reaping\n");
#endif /* APP_LOCK_TRACE */

        if (thread_number>num_threads)
                thread_number=num_threads;

        for (i=0; i<thread_number; i+=MAXIMUM_WAIT_OBJECTS) 
		{
		int j;

                j=(thread_number-i);
                if (j>MAXIMUM_WAIT_OBJECTS)
                    j=MAXIMUM_WAIT_OBJECTS;

		if (WaitForMultipleObjects(j,
		            (CONST HANDLE *)&(thread_ctx[i]),
                            TRUE,INFINITE) == WAIT_FAILED)
			{
#ifdef APP_LOCK_TRACE

                        if (show_trace)
			    fprintf(stderr,"APP_thread_finalize: WaitForMultipleObjects failed:%d\n",GetLastError());

#endif /* APP_LOCK_TRACE */
                        ret=0;
                        break;
			}
		}

        /* finished with thread table */
        if (thread_ctx!=NULL)
                {
                free(thread_ctx);
                thread_ctx=NULL;
                }
        num_threads=0;

#ifdef APP_LOCK_TRACE
        if (show_trace)
            fprintf(stderr,"APP_thread_finalize: done\n");
#endif /* APP_LOCK_TRACE */

        return(ret);
	}

int APP_thread_show_stats()
        {

#ifdef APP_LOCK_TRACEx
        int i;
		for (i=1; i<num_locks; i++)
                fprintf(stderr,"%8ld:%s\n",lock_count[i],
                        CRYPTO_get_lock_name(i));
#endif
        return(1);
        }

int APP_thread_trace(val)
int val;
        {
        show_trace=val;
#ifdef APP_LOCK_TRACE
        return(1);
#else /* !APP_LOCK_TRACE */
        return(0);
#endif /* APP_LOCK_TRACE */
        }


