// -*- mode:C++; tab-width:2; c-basic-offset:2; indent-tabs-mode:nil -*-
//
// Copyright (C) 2000-2005 by Roger Rene Kommer / artefaktur, Kassel, Germany.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Library General Public License (LGPL).
//
//
// This library 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
// License ACDK-FreeLicense document enclosed in the distribution
// for more for more details.
// This file is part of the Artefaktur Component Development Kit:
// ACDK
//
// Please refer to
// - http://www.acdk.de
// - http://www.artefaktur.com
// - http://acdk.sourceforge.net
// for more information.
//
// $Header: /cvsroot/acdk/acdk/acdk_core/src/acdk/lang/sys/core_specific.cpp,v 1.32 2005/03/31 12:26:40 kommer Exp $
#include <acdk.h>
#include "core_specific.h"
#include "core_vector.h"
#include "core_fastmutex.h"
#include "core_guard.h"
#include "core_thread_id.h"
#include "core_syslocal.h"
#include <acdk/lang/ThreadLocalImpl.h>
#include <acdk/lang/System.h>
namespace acdk {
namespace lang {
namespace sys {
/**
Holds the values of one thread.
*/
class core_tls_values
{
public:
core_vector<void*> _values;
core_tls_values()
: _values(0, 20, 0)
{
}
~core_tls_values();
void* get(int idx)
{
if (_values.size() <= idx)
return 0;
return _values[idx];
}
void set(int idx, void* value)
{
_values.ensureSize(idx + 1);
_values[idx] = value;
if (idx >= _values.size())
_values.erase(_values.begin() + idx + 1);
}
};
/*
core_tls_values* get_tls_table()
{
return (core_tls_values*)core_local_core_tls_values;
}
void set_tls_table(core_tls_values* table)
{
core_local_core_tls_values = (int)table;
}
*/
/*
#else // ACDK_WIN32_NO_LATE_BIND
static DWORD core_local_core_tls_handle = 0;
inline core_tls_values* get_tls_table()
{
if (core_local_core_tls_handle == 0)
{
core_local_core_tls_handle = TlsAlloc();
}
return (core_tls_values*)TlsGetValue(core_local_core_tls_handle);
}
inline void set_tls_table(core_tls_values* table)
{
TlsSetValue(core_local_core_tls_handle, table);
}
#endif // ACDK_WIN32_NO_LATE_BIND
#endif //WIN32_THREADS
*/
/**
Represents value entries in a a given thread
*/
struct core_tls_value_table
{
core_thread_id _threadId;
core_tls_values* _table;
core_tls_value_table()
: _threadId()
, _table(0)
{
}
core_tls_value_table(core_thread_id threadId, core_tls_values* table)
: _threadId(threadId)
, _table(table)
{
}
void destroyValues(core_specific* spec);
void destroyThread()
{
delete _table;
_table = 0;
}
};
/**
A global table, which holds all registered core_tls_table
*/
class core_tls_tables
{
core_vector<core_tls_value_table> _entries;
core_vector<core_specific*> _specifics;
core_vector<core_specific::CleanUpFunc> _cleanFunctions;
core_fastmutex _mutex;
bool _inited;
public:
core_tls_tables()
: _entries(20, 20, core_tls_value_table())
, _specifics(20, 20, 0)
, _cleanFunctions(20, 20, 0)
, _inited(true)
{
}
~core_tls_tables()
{
/** because is allocated globally, this prevents to
call methods after destruction
*/
_inited = false;
}
void insert(core_tls_values* table)
{
core_static_lock_guard<core_fastmutex> lock(_mutex);
for (int i = 0; i < _entries.size(); ++i)
{
if (_entries[i]._table == 0)
{
_entries[i]._table = table;
_entries[i]._threadId = core_thread_id::get_current();
return;
}
}
_entries.push_back(core_tls_value_table( core_thread_id::get_current(), table));
}
void remove_current()
{
core_static_lock_guard<core_fastmutex> lock(_mutex);
core_thread_id ctid = core_thread_id::get_current();
for (int i = 0; i < _entries.size(); ++i)
{
if (_entries[i]._threadId == ctid) {
_entries[i].destroyThread();
return;
}
}
// should never reach
}
void insert_specific(core_specific* spec)
{
core_static_lock_guard<core_fastmutex> lock(_mutex);
int i;
for (i = 0; i < _specifics.size(); ++i)
{
if (_specifics[i] == 0) {
spec->_value_index = i;
_specifics[i] = spec;
_cleanFunctions[i] = core_specific::cleanup;
return;
}
}
spec->_value_index = _specifics.size();
_specifics.push_back(spec);
_cleanFunctions.push_back((core_specific::CleanUpFunc)core_specific::cleanup);
}
void remove_specific(core_specific* spec)
{
if (_inited == false)
return;
core_static_lock_guard<core_fastmutex> lock(_mutex);
for (int i = 0; i < _entries.size(); ++i)
{
_entries[i].destroyValues(spec);
}
_specifics[spec->_value_index] = 0;
_cleanFunctions[spec->_value_index] = 0;
}
void register_cleanup(int idx, core_specific::CleanUpFunc cleanup)
{
if (_inited == false)
return;
core_static_lock_guard<core_fastmutex> lock(_mutex);
_cleanFunctions[idx] = cleanup;
}
core_tls_values* get_core_specific_table();
void cleanup_core_specific_table();
void cleanup(int idx, void* ptr)
{
core_static_lock_guard<core_fastmutex> lock(_mutex);
if (_cleanFunctions[idx] != 0)
{
_cleanFunctions[idx](ptr);
_cleanFunctions[idx] = 0;
}
}
void get_thread_locals(core_vector<void*>& erg, core_thread_id tid)
{
core_static_lock_guard<core_fastmutex> lock(_mutex);
for (int i = 0; _entries.size(); ++i)
{
if (_entries[i]._threadId == tid) {
if (_entries[i]._table == 0)
return;
core_vector<void*>& values = _entries[i]._table->_values;
for (int j = 0; j < values.size(); j++)
{
if (values[j] != 0)
erg.push_back(values[j]);
}
return;
}
}
}
void get_locals(core_vector<void*>& erg, int idx)
{
// ### implement?
}
};
core_tls_tables& get_tls_tables()
{
/* to make sure, that it will initialized
the right time. especially on static
specific<type> declarations
*/
static core_tls_tables g_core_tls_tables;
return g_core_tls_tables;
}
void core_tls_value_table::destroyValues(core_specific* spec)
{
if (_table == 0)
return;
get_tls_tables().cleanup(spec->get_key(), _table->get(spec->get_key()));
_table->set(spec->get_key(), 0);
}
core_tls_values::~core_tls_values()
{
for (int i = 0; i < _values.size(); i++)
{
if (_values[i] != 0)
{
void* p = _values[i];
_values[i] = 0;
get_tls_tables().cleanup(i, p);
}
}
}
DEFINE_STATIC_SPECIFIC(core_tls_values*, gcore_local_core_tls_values);
core_tls_values*
core_tls_tables::get_core_specific_table()
{
static core_tls_values* globalStaticValues = 0;
static ThreadLocalImpl dynLocal;
if (System::isInMain() == false)
{
if (globalStaticValues == 0)
globalStaticValues = new core_tls_values();
return globalStaticValues;
}
#if defined(ACDK_SUPPORT_STATIC_THREADLOCAL)
if (core_system::acdk_core_static_bound == true)
{
if (gcore_local_core_tls_values == 0)
gcore_local_core_tls_values = new core_tls_values();
return gcore_local_core_tls_values;
}
#endif
/FONT>
core_tls_values* ctv = (core_tls_values*)dynLocal.get();
if (ctv == 0)
{
ctv = new core_tls_values();
dynLocal.set((void*)ctv);
}
return ctv;
}
void
core_tls_tables::cleanup_core_specific_table()
{
}
//#if defined(_MSC_VER) //==============================================
/*
core_tls_values*
core_tls_tables::get_core_specific_table()
{
core_tls_values* _table = get_tls_table();
if (_table != 0)
return _table;
_table = new core_tls_values();
set_tls_table(_table);
get_tls_tables().insert(_table);
return _table;
}
void
core_tls_tables::cleanup_core_specific_table()
{
//core_tls_values*& _table = (core_tls_values*&)core_local_core_tls_values();
//cleanup_current();
}
*/
/*
#elif defined(ACDK_OS_LINUX) //==============================================
char* _lastStackAddressTop = 0;
char* _lastStackAddressBottom = 0;
const size_t StandardStackSize = 2 * 1024 * 1024; // 2MB
//core_specific_table _mainThreadTable;
core_tls_values*
core_tls_tables::get_core_specific_table()
{
char csf;
char *sp = &csf;
if (sp >= _lastStackAddress)
return &_mainThreadTable;
else if (sp >= _lastStackAddressBottom && sp < _lastStackAddressTop)
return &__pthread_manager_thread;
return (core_tls_values*)(((unsigned long)sp | (STACK_SIZE-1))+1) - 1;
}
void
core_tls_tables::cleanup_core_specific_table()
{
}
*/
//#else // defined(_MSC_VER) ==============================================
/*
//typdef ::acdk::lang::ThreadLocalImpl ThreadLocal;
::acdk::lang::ThreadLocalImpl gSpecificTable;
core_tls_values*
core_tls_tables::get_core_specific_table()
{
core_tls_values* _table = (core_tls_values*)gSpecificTable.get();
if (_table != 0)
return _table;
_table = new core_tls_values();
gSpecificTable.set(_table);
return _table;
}
void
core_tls_tables::cleanup_core_specific_table()
{
//core_tls_values*& _table = (core_tls_values*&)core_local_core_tls_values();
//cleanup_current();
}
*/
//#endif //// defined(_MSC_VER)==============================================
core_specific::core_specific()
: _value_index(0)
//, _cleanup(core_specific::cleanup)
{
get_tls_tables().insert_specific(this);
}
void core_specific::register_cleanup(core_specific::CleanUpFunc fnc)
{
get_tls_tables().register_cleanup(get_key(), fnc);
}
//virtaul
core_specific::~core_specific()
{
get_tls_tables().remove_specific(this);
}
void
core_specific::set(void *t)
{
core_tls_values* table = get_tls_tables().get_core_specific_table();
table->set(_value_index, t);
}
void*
core_specific::get()
{
core_tls_values* table = get_tls_tables().get_core_specific_table();
return table->get(_value_index);
}
//static
void
core_specific::thread_cleanup()
{
get_tls_tables().remove_current();
}
void core_specific::get_locals(core_vector<void*>& erg)
{
}
//static
void
core_specific::get_thread_locals(core_vector<void*>& erg, core_thread_id tid)
{
get_tls_tables().get_thread_locals(erg, tid);
}
} // namespace sys
} //namespace lang
} // namespace acdk
|