/*
  File: dldet.c

  First prototype of a deadlock detector for POSIX threads.

  pfh 11/19/00
*/

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include "dldet.h"

// Pthread_once magic for startup
pthread_once_t key_once = PTHRED_ONCE_INIT;

// Key to per-thread structures
pthread_key_t  tsd_key;

/* ---------------------------------------------------------------------
   Routine to init a dld_mutex_t struct to default values

   Assumes
     Valid structure pointer

   Returns
     PTHREAD_SUCCESS, or PTHREAD_ERROR
   pfh 11/19/2000
*/
int dld_dmt_init(dld_mutex_t *dmt)
{
    // Larry, Moe or Curly?
    if(dmt == NULL)
    {
	fprintf(stderr, "Null structure!");
	return(PTHREAD_ERROR);
    }

    // Assume valid, then
    dmt->mutex = NULL;
    dmt->lock_count = 0;
    dmt->thr_owner = NULL;

    return(PTHREAD_SUCCESS);
}

/* ---------------------------------------------------------------------
   Pthread_once startup function

   Assumes
     Called once
     Can create TSD key
*/
void pthread_once_routine(void)
{
    int    status;

    printf("\nDoing one-time init");

    status = pthread_key_create(&tsd_key, NULL);
    if(status != 0)
	fprintf(stderr, "Error creating TSD key!");
}


/* ---------------------------------------------------------------------
   Code, replacements for POSIX routines
*/
int dld_pthread_create(pthread_t *thread, const pthread_attr_t *attr,
		       void *(*start)(void *), void *arg)
{
    int        pret = 0;
    
    /*
      For now, all we do is call pthreads. 
    */
    pret = pthread_create(thread, attr, start, arg);

    // If thread create failed, don't bother 
    if(pret != PTHREAD_SUCCESS)
	return(pret);

    return(PTHREAD_SUCCESS);
}

/* ---------------------------------------------------------------------
   Replacement mutex lock routine.

   Assumes
     Pthread_once already called, and that the TSD exists.

   Returns
     Success if locked OK

   pfh 11/19/2000
*/
int dld_pthread_mutex_lock(const pthread_mutex_t *mutex)
{
    tsd_t   *p_tsd = NULL;
    int     pret, itmp;

    // Try and retrieve the TSD structure
    p_tsd = (tsd_t *) pthread_getspecific(tsd_key);
    if(p_tsd == NULL)
    {
	fprintf(stderr, "Unable to retrieve TSD, no checking performed!");
	return(pthread_mutex_lock(mutex));
    }

    // Search mlist for matching mutex, etc, etc

    return(pthread_mutex_lock(mutex));
}

/* ---------------------------------------------------------------------
   Function wrapper for starting up a thread. We need to execute
   code in the threads' context, so we run our needs first then
   run their code.

   Could be implemented as a hairy C macro.
*/
void *dld_pthread_startup(void *(*start)(void *), void *arg)
{
    int        pret = 0;
    tsd_t      *p_tsd = NULL;
    int        itmp;
    
    
    // Ensure TSD key has been created
    pret = pthread_once(&key_once, pthread_once_routine);

    // If it failed, keep going since its our fault most likely
    if(pret != PTHREAD_SUCCESS)
    {
	fprintf(stderr, "Error calling pthread_once!");
	return(start(arg));
    }

    // Create thread-specific storage
    p_tsd = (tsd_t *) malloc(sizeof(tsd_t));
    if(p_tsd == NULL)
    {
	fprintf(stderr, "Malloc error creating TSD!");
	return(start(arg));	
    }

    // Set this threads' TSD to malloc'd space
    pret = pthread_setspecific(tsd_key, p_tsd);
    if(pret != PTHREAD_SUCCESS)
    {
	fprintf(stderr, "Error setting TSD data!");
	return(start(arg));	
    }

    // Save data / fill structure
    p_tsd->thread_id = pthread_self();
    for(itmp = 0; itmp < DLD_TRACK_COUNT; itmp++)
    {
	pret = dld_dmt_init(&p_tsd->mlist[itmp]);
	if(pret != PTHREAD_SUCCESS)
	{
	    fprintf(stderr, "Error initializing structure!");
	    return(start(arg));	
	}
    }

    // Call the threads' main function
    return(start(arg));
}

