/* mt/cb/mt_pth.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 <pthread.h>
#ifdef OPENSSL

#include <openssl/rsa.h>       /* SSLeay stuff */
#include <openssl/crypto.h>
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

#include "openssl/crypto.h"
#include "openssl/e_os2.h"

#define CRYPTO_set_id_cb CRYPTO_set_id_callback
#define CRYPTO_set_locking_cb CRYPTO_set_locking_callback
#define CRYPTO_get_num_locks CRYPTO_num_locks
#else
#include "crypto.h"
#include "e_os2.h"
#endif

#ifndef THREAD_STACK_SIZE
#define THREAD_STACK_SIZE	(16*1024)
#endif

static pthread_mutex_t *lock_cs=NULL;

static long *lock_count=NULL;
static int num_locks;
static int num_threads=0;

static int show_trace=0;

static APP_THREAD_ID *thread_ctx=NULL;
static int N_thread_ctx=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 
	 * lock itself (either a mutex or a rwlock)
	 */
	lock_count=(long *)malloc(sizeof(long)*num_locks);
	lock_cs=(pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)*num_locks);

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

	/* register the callbacks now */
	CRYPTO_set_id_cb((unsigned long (*)())APPCB_thread_id);
	CRYPTO_set_locking_cb((void (*)())APPCB_locking_cb); // chrisp changed 'int' to 'void' 
	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);

	/* destroy all the locks - mutex or rwlocks */
	for (i=0; i<num_locks; i++)
		{
		pthread_mutex_destroy(&(lock_cs[i]));
                }
	free(lock_cs);
	free(lock_count);
	lock_cs=NULL;
	lock_count=NULL;
	return(1);
	}

unsigned long APPCB_thread_id()
	{
	unsigned long ret;

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

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

#ifdef APP_LOCK_TRACE
        if (show_trace)
#endif /* APP_LOCK_TRACE */

	if (mode & CRYPTO_LOCK)
		{
		pthread_mutex_lock(&(lock_cs[lockid]));
		lock_count[lockid]++;
		}
	else
		{
		pthread_mutex_unlock(&(lock_cs[lockid]));
		}

#ifdef APP_LOCK_TRACE
        if (show_trace)
#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);
#ifdef sun
		thr_setconcurrency(thread_count);
#endif
		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;

	pthread_create(&tid, NULL,(void *(*)())func, (void *)func_arg);

	/* 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;

#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;

	/* wait for each thread to terminate - in order 
	 *
	 * note: we could simply pass in 0 for the thread id and
	 *       simply wait for the right number of threads to 
	 *       complete
	 */
	for (i=0; i<thread_number; i++)
		{
		pthread_join(thread_ctx[i],NULL);
		}

	/* 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(1);
}

int APP_thread_show_stats()
        {
	int i;

	for (i=1; i<num_locks; i++)
	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 */
	}


