/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/tls.c,v 1.9 2004/04/30 13:41:56 aleigh Exp $ 
 */

/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Disclosure, copying, distribution, or transmission of this code without
 * express permission is prohibited.
 */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>

#include "mecha.h"

static pthread_key_t tls_key;
static void **clean_funcs;
static int clean_count = 0;

struct tlsSent {
    void *data;
    int class;
    char *name;
    struct tlsSent *next;
};
typedef struct tlsSent tlsTent;

static void tlsFfree(tlsTent * p);

static void tlsFadd_internal(void *data, char *name, int class);

#undef TLS_DEBUG

/*
 * Allow another piece of code to register a cleanup handler. This function
 * will return an integer which is the TLS class. This class needs to be
 * provided for subsequent calls to the TLS system.
 */
int tlsFregister(void *(*clean_fn) (void *))
{
    if (clean_count == 0) {
        clean_funcs = (void *) malloc(sizeof(void *));
        clean_funcs[0] = (void *) clean_fn;
        clean_count++;
    } else {
        clean_count++;
        clean_funcs = realloc(clean_funcs, sizeof(void *) * clean_count);
        clean_funcs[clean_count - 1] = (void *) clean_fn;
    }
    return clean_count - 1;
}

/*
 * Free a TLS entry and call it's associated cleanup function if it has
 * client data
 */
static void tlsFfree(tlsTent * p)
{
    void (*f) (void *data);

#ifdef TLS_DEBUG
    logFmsg(3, "tlsFfree: freeing a TLS entity %s class %d data 0x%x",
            p->name, p->class, p->data);
#endif

    if (p->data != NULL) {

        f = (void (*)(void *)) clean_funcs[p->class];
#ifdef TLS_DEBUG
        logFmsg(3, "tlsFfree: calling 0x%x for cleanup", f);
#endif
        (*f) (p->data);
    }
    free(p->name);
    free(p);
}

/*
 * Destroy an entire TLS list, calling any associated cleanup functions for
 * it's client data.
 */
void tlsFdestroy(void *p)
{
    tlsTent *ent, *next = NULL;

    if (p == NULL)
        return;

    ent = (tlsTent *) p;

    while (1) {
        next = ent->next;
        tlsFfree(ent);
        if (next == NULL)
            return;
        ent = next;
    }
}

/*
 * Remove all of a thread's TLS storage.
 */
void tlsFclean(void)
{
    tlsTent *p;

    p = pthread_getspecific(tls_key);
    if (p == NULL)
        return;

    tlsFdestroy(p);
    pthread_setspecific(tls_key, NULL);
}

void tlsFinit(void)
{
    if (pthread_key_create(&tls_key, tlsFdestroy) != 0) {
#ifdef TLS_DEBUG
        logFmsg(2, "tls: Unable to register tls_key. [e: %d]", errno);
#endif
        abort();
    }
    tlsFregister((void *(*)(void *)) lstFset_free);
}

void tlsFadd(void *data, char *name, int class)
{
    tlsFadd_internal(data, strFcopy(name), class);
}

static void tlsFadd_internal(void *data, char *name, int class)
{
    tlsTent *tls;
    tlsTent *ent;

#ifdef TLS_DEBUG
    logFmsg(3, "tlsFadd: called to add: 0x%x %s %d", data, name, class);
#endif
    ent = malloc(sizeof(tlsTent));
    ent->name = name;
    ent->data = data;
    ent->class = class;

    tls = pthread_getspecific(tls_key);

    if (tls == NULL) {
        ent->next = NULL;
    } else {
        /* TODO: Lame */
        ent->next = tls;
    }

    if (pthread_setspecific(tls_key, ent) != 0) {
        logFmsg(2, "tlsFadd: setspecific failed. [e: %d]", errno);
    }
#ifdef TLS_DEBUG
    else {

        logFmsg(3, "tlsFadd: setspecific 0.");
    }
#endif
}

void tlsFdel(char *name, int class)
{
    tlsTent *e;
    void *(*f) (void *data);

#ifdef TLS_DEBUG
    logFmsg(3, "tlsFdel: called for %s %d", name, class);
#endif

    e = pthread_getspecific(tls_key);

    for (; e != NULL; e = e->next) {
        /* We do this as two to avoid the strcmp for other classes */
        if (e->class == class) {
            if (strcmp(name, e->name) == 0) {
#ifdef TLS_DEBUG
                logFmsg(3, "tlsFdel: found match");
#endif
                if (e->data != NULL) {
                    f = (void *(*)(void *)) clean_funcs[e->class];
                    (*f) (e->data);
                    e->data = NULL;
                }
            }
        }
    }
}

void *tlsFget(const char *name, int class)
{
    tlsTent *e;

    assert(name != NULL);

    e = pthread_getspecific(tls_key);

#ifdef TLS_DEBUG
    logFmsg(3, "tlsFget: e is 0x%x", e);
#endif

    if (e == NULL)
        return NULL;

    while (1) {
        if (strcmp(e->name, name) == 0 && e->class == class) {
#ifdef TLS_DEBUG
            logFmsg(3, "tlsFget: returning 0x%x", e->data);
#endif
            return e->data;
        }
        if (e->next == NULL)
            return NULL;
        e = e->next;
    }
}

char *tlsFadd_makeid(void *data, int class)
{
    char *id = malloc(sizeof(char) * 37);

    uidFgenerate_str(id, 37);

    tlsFadd_internal(data, id, class);

    return id;
}
