/*
* Copyright (C) 2013 Martin Willi
* Copyright (C) 2013 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include "thread.h"
#include <threading/thread_value.h>
typedef struct private_thread_value_t private_thread_value_t;
/**
* Unified thread_value_t implementation
*/
struct private_thread_value_t {
/**
* Public interface.
*/
thread_value_t public;
union {
/**
* Cleanup function
*/
thread_cleanup_t cleanup;
/**
* Windows TLS index, if used
*/
DWORD index;
};
};
/**
* TLS entry
*/
typedef struct {
/** TLS value */
void *value;
/** cleanup handler function */
thread_cleanup_t cleanup;
} entry_t;
/**
* See windows/thread.h
*/
void thread_tls_cleanup(void *value)
{
entry_t *entry = (entry_t*)value;
if (entry->cleanup)
{
entry->cleanup(entry->value);
}
free(entry);
}
METHOD(thread_value_t, tls_set, void,
private_thread_value_t *this, void *val)
{
entry_t *entry;
if (val)
{
INIT(entry,
.cleanup = this->cleanup,
.value = val,
);
free(thread_tls_put(this, entry));
}
else
{
free(thread_tls_remove(this));
}
}
METHOD(thread_value_t, tls_get, void*,
private_thread_value_t *this)
{
entry_t *entry;
entry = thread_tls_get(this);
if (entry)
{
return entry->value;
}
return NULL;
}
METHOD(thread_value_t, tls_destroy, void,
private_thread_value_t *this)
{
entry_t *entry;
entry = thread_tls_remove(this);
if (entry)
{
thread_tls_cleanup(entry);
}
free(this);
}
METHOD(thread_value_t, tls_set_index, void,
private_thread_value_t *this, void *val)
{
TlsSetValue(this->index, val);
}
METHOD(thread_value_t, tls_get_index, void*,
private_thread_value_t *this)
{
return TlsGetValue(this->index);
}
METHOD(thread_value_t, tls_destroy_index, void,
private_thread_value_t *this)
{
TlsFree(this->index);
free(this);
}
/**
* Described in header.
*/
thread_value_t *thread_value_create(thread_cleanup_t cleanup)
{
private_thread_value_t *this;
DWORD index = TLS_OUT_OF_INDEXES;
/* we have two implementations: Windows Tls* functions do not support
* callbacks and has limited instances. We use it nonetheless if possible,
* especially as leak detective relies on TLS, but we have to mangle
* leak detective state for TLS storage. */
if (!cleanup)
{
index = TlsAlloc();
}
if (index == TLS_OUT_OF_INDEXES)
{
INIT(this,
.public = {
.set = _tls_set,
.get = _tls_get,
.destroy = _tls_destroy,
},
.cleanup = cleanup,
);
}
else
{
INIT(this,
.public = {
.set = _tls_set_index,
.get = _tls_get_index,
.destroy = _tls_destroy_index,
},
.index = index,
);
}
return &this->public;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>