From 0e2ae18a97fd507bac872031888d34c3a7d8b17b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 3 Sep 2010 16:20:38 +0200 Subject: Implement Win32 TLS support --- odb/details/win32/dll.cxx | 52 +++++++++ odb/details/win32/init.cxx | 43 ++++++++ odb/details/win32/init.hxx | 38 +++++++ odb/details/win32/lock.hxx | 51 +++++++++ odb/details/win32/once-init.hxx | 25 +++++ odb/details/win32/once.cxx | 29 +++++ odb/details/win32/once.hxx | 49 +++++++++ odb/details/win32/once.ixx | 30 +++++ odb/details/win32/tls-init.hxx | 28 +++++ odb/details/win32/tls.cxx | 236 ++++++++++++++++++++++++++++++++++++++++ odb/details/win32/tls.hxx | 111 +++++++++++++++++++ odb/details/win32/tls.ixx | 22 ++++ odb/details/win32/tls.txx | 87 +++++++++++++++ 13 files changed, 801 insertions(+) create mode 100644 odb/details/win32/dll.cxx create mode 100644 odb/details/win32/init.cxx create mode 100644 odb/details/win32/init.hxx create mode 100644 odb/details/win32/lock.hxx create mode 100644 odb/details/win32/once-init.hxx create mode 100644 odb/details/win32/once.cxx create mode 100644 odb/details/win32/once.hxx create mode 100644 odb/details/win32/once.ixx create mode 100644 odb/details/win32/tls-init.hxx create mode 100644 odb/details/win32/tls.cxx create mode 100644 odb/details/win32/tls.hxx create mode 100644 odb/details/win32/tls.ixx create mode 100644 odb/details/win32/tls.txx (limited to 'odb/details/win32') diff --git a/odb/details/win32/dll.cxx b/odb/details/win32/dll.cxx new file mode 100644 index 0000000..843be8d --- /dev/null +++ b/odb/details/win32/dll.cxx @@ -0,0 +1,52 @@ +// file : odb/details/win32/dll.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +// If we are building a static library from VC++ (LIBODB_STATIC_LIB) or +// a static library from automake (!DLL_EXPORT), then omit DllMain. +// +#if defined(LIBODB_STATIC_LIB) || !defined (_MSC_VER) && !defined(DLL_EXPORT) + +#include + +#include + +using namespace odb::details; + +extern "C" BOOL WINAPI +DllMain (HINSTANCE, DWORD reason, LPVOID reserved) +{ + switch (reason) + { + case DLL_PROCESS_ATTACH: + { + process_start (); + thread_start (); + break; + } + + case DLL_THREAD_ATTACH: + { + thread_start (); + break; + } + + case DLL_THREAD_DETACH: + { + thread_end (); + break; + } + + case DLL_PROCESS_DETACH: + { + thread_end (); + process_end (reserved == NULL); + break; + } + } + + return 1; +} + +#endif diff --git a/odb/details/win32/init.cxx b/odb/details/win32/init.cxx new file mode 100644 index 0000000..c07b9d8 --- /dev/null +++ b/odb/details/win32/init.cxx @@ -0,0 +1,43 @@ +// file : odb/details/win32/init.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include +#include + +namespace odb +{ + namespace details + { + void + process_start () + { + // The order is important. + // + once_process_start (); + tls_process_start (); + } + + void + process_end (bool safe) + { + // The order is important. + // + tls_process_end (safe); + once_process_end (safe); + } + + void + thread_start () + { + } + + void + thread_end () + { + tls_thread_end (); + } + } +} diff --git a/odb/details/win32/init.hxx b/odb/details/win32/init.hxx new file mode 100644 index 0000000..4a49129 --- /dev/null +++ b/odb/details/win32/init.hxx @@ -0,0 +1,38 @@ +// file : odb/details/win32/init.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_WIN32_INIT_HXX +#define ODB_DETAILS_WIN32_INIT_HXX + +#include + +namespace odb +{ + namespace details + { + void + process_start (); + + // The safe parameter indicates whether it is safe to free heap objects. + // If the process is terminated by a call to ExitProcess(), some threads + // might have been killed leaving things in inconsistent state. + // + void + process_end (bool safe = true); + + void + thread_start (); + + // This function may be called even for thread for which thread_start() + // hasn't been called. + // + void + thread_end (); + } +} + +#include + +#endif // ODB_DETAILS_WIN32_INIT_HXX diff --git a/odb/details/win32/lock.hxx b/odb/details/win32/lock.hxx new file mode 100644 index 0000000..50611e0 --- /dev/null +++ b/odb/details/win32/lock.hxx @@ -0,0 +1,51 @@ +// file : odb/details/win32/lock.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_WIN32_LOCK_HXX +#define ODB_DETAILS_WIN32_LOCK_HXX + +#include + +#include + +namespace odb +{ + namespace details + { + // Critical section lock. Not exported; for internal use only. + // + struct win32_lock + { + win32_lock (CRITICAL_SECTION& cs) + : cs_ (&cs) + { + EnterCriticalSection (cs_); + } + + ~win32_lock () + { + if (cs_ != 0) + LeaveCriticalSection (cs_); + } + + void + unlock () + { + if (cs_ != 0) + { + LeaveCriticalSection (cs_); + cs_ = 0; + } + } + + private: + CRITICAL_SECTION* cs_; + }; + } +} + +#include + +#endif // ODB_DETAILS_WIN32_LOCK_HXX diff --git a/odb/details/win32/once-init.hxx b/odb/details/win32/once-init.hxx new file mode 100644 index 0000000..2826a8b --- /dev/null +++ b/odb/details/win32/once-init.hxx @@ -0,0 +1,25 @@ +// file : odb/details/win32/once-init.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_WIN32_ONCE_INIT_HXX +#define ODB_DETAILS_WIN32_ONCE_INIT_HXX + +#include + +namespace odb +{ + namespace details + { + void + once_process_start (); + + void + once_process_end (bool safe); + } +} + +#include + +#endif // ODB_DETAILS_WIN32_ONCE_INIT_HXX diff --git a/odb/details/win32/once.cxx b/odb/details/win32/once.cxx new file mode 100644 index 0000000..9201ea6 --- /dev/null +++ b/odb/details/win32/once.cxx @@ -0,0 +1,29 @@ +// file : odb/details/win32/once.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +#include +#include + +namespace odb +{ + namespace details + { + CRITICAL_SECTION once::cs_; + + void + once_process_start () + { + InitializeCriticalSection (&once::cs_); + } + + void + once_process_end (bool) + { + DeleteCriticalSection (&once::cs_); + } + } +} diff --git a/odb/details/win32/once.hxx b/odb/details/win32/once.hxx new file mode 100644 index 0000000..258d5e0 --- /dev/null +++ b/odb/details/win32/once.hxx @@ -0,0 +1,49 @@ +// file : odb/details/win32/once.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_WIN32_ONCE_HXX +#define ODB_DETAILS_WIN32_ONCE_HXX + +#include + +#include + +#include + +namespace odb +{ + namespace details + { + class LIBODB_EXPORT once + { + public: + once (); + + void + call (void (*func) ()); + + private: + once (const once&); + once& operator= (const once&); + + private: + friend void + once_process_start (); + + friend void + once_process_end (bool); + + private: + bool called_; + static CRITICAL_SECTION cs_; + }; + } +} + +#include + +#include + +#endif // ODB_DETAILS_WIN32_ONCE_HXX diff --git a/odb/details/win32/once.ixx b/odb/details/win32/once.ixx new file mode 100644 index 0000000..9f1706d --- /dev/null +++ b/odb/details/win32/once.ixx @@ -0,0 +1,30 @@ +// file : odb/details/win32/once.ixx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include + +namespace odb +{ + namespace details + { + inline once:: + once () + : called_ (false) + { + } + + inline void once:: + call (void (*func) ()) + { + win32_lock l (cs_); + + if (!called_) + { + func (); + called_ = true; + } + } + } +} diff --git a/odb/details/win32/tls-init.hxx b/odb/details/win32/tls-init.hxx new file mode 100644 index 0000000..80e00f9 --- /dev/null +++ b/odb/details/win32/tls-init.hxx @@ -0,0 +1,28 @@ +// file : odb/details/win32/tls-init.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_WIN32_TLS_INIT_HXX +#define ODB_DETAILS_WIN32_TLS_INIT_HXX + +#include + +namespace odb +{ + namespace details + { + void + tls_process_start (); + + void + tls_process_end (bool safe); + + void + tls_thread_end (); + } +} + +#include + +#endif // ODB_DETAILS_WIN32_TLS_INIT_HXX diff --git a/odb/details/win32/tls.cxx b/odb/details/win32/tls.cxx new file mode 100644 index 0000000..48c2f00 --- /dev/null +++ b/odb/details/win32/tls.cxx @@ -0,0 +1,236 @@ +// file : odb/details/win32/tls.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include +#include // ERROR_INVALID_INDEX + +#include +#include // std::size_t + +#include +#include +#include +#include + +using namespace std; + +namespace odb +{ + namespace details + { + typedef void (*dtor_func) (void*); + + struct entry + { + void* value; + dtor_func dtor; + }; + + struct thread_data + { + size_t size; + size_t capacity; + entry entries[0]; + }; + + struct process_data + { + size_t size; + size_t capacity; + dtor_func dtors[0]; + }; + + static DWORD index_ = TLS_OUT_OF_INDEXES; + static CRITICAL_SECTION cs_; + static process_data* proc_data_; + + const size_t init_capacity = 4; + + void + tls_process_start () + { + index_ = TlsAlloc (); + + if (index_ == TLS_OUT_OF_INDEXES) + throw win32_exception (); + + InitializeCriticalSection (&cs_); + + proc_data_ = static_cast ( + operator new ( + sizeof (process_data) + sizeof (dtor_func) * init_capacity)); + + proc_data_->size = 0; + proc_data_->capacity = init_capacity; + memset (proc_data_->dtors, 0, sizeof (dtor_func) * init_capacity); + } + + void + tls_process_end (bool safe) + { + if (safe) + operator delete (proc_data_); + + DeleteCriticalSection (&cs_); + + if (index_ != TLS_OUT_OF_INDEXES) + { + if (!TlsFree (index_)) + throw win32_exception (); + } + } + + void + tls_thread_end () + { + if (thread_data* d = static_cast (TlsGetValue (index_))) + { + // Call destructors. Implement the pthread semantics in that the + // destructors are called until all the values become 0. + // + for (bool pass (true); pass;) + { + pass = false; + + for (size_t i (0); i < d->size; ++i) + { + if (d->entries[i].dtor != 0 && d->entries[i].value != 0) + { + pass = true; + void* tmp (d->entries[i].value); + d->entries[i].value = 0; + d->entries[i].dtor (tmp); + } + } + } + + operator delete (d); + } + } + + // + // tls_common + // + + std::size_t tls_common:: + _allocate (dtor_func dtor) + { + win32_lock l (cs_); + + size_t n (proc_data_->size); + size_t c (proc_data_->capacity); + + if (n == c) + { + c *= 2; + + process_data* pd ( + static_cast ( + operator new (sizeof (process_data) + sizeof (dtor_func) * c))); + + memcpy (pd->dtors, proc_data_->dtors, n * sizeof (dtor_func)); + memset (proc_data_->dtors + n, 0, sizeof (dtor_func) * (c - n)); + + pd->size = n; + pd->capacity = c; + operator delete (proc_data_); + proc_data_ = pd; + } + + proc_data_->dtors[n] = dtor; + return proc_data_->size++; + } + + void* tls_common:: + _get (std::size_t key) + { + if (thread_data* d = static_cast (TlsGetValue (index_))) + { + if (key < d->size) + return d->entries[key].value; + } + + // Check if this key is valid. + // + win32_lock l (cs_); + + if (key < proc_data_->size) + return 0; + + throw win32_exception (ERROR_INVALID_INDEX); + } + + void tls_common:: + _set (std::size_t key, void* value) + { + thread_data* d (static_cast (TlsGetValue (index_))); + + if (d != 0 && key < d->capacity) + { + if (key >= d->size) + { + // Check if this key is valid. If so then we need to copy + // dtors for new slots. + // + win32_lock l (cs_); + + size_t n (proc_data_->size); + + if (key >= n) + throw win32_exception (ERROR_INVALID_INDEX); + + for (size_t i (d->size); i < n; ++i) + d->entries[i].dtor = proc_data_->dtors[i]; + + d->size = n; + } + + d->entries[key].value = value; + } + else + { + // Check if this key is valid. If so then we need to (re)-allocate + // our storage. + // + win32_lock l (cs_); + + size_t n (proc_data_->size); + + if (key >= n) + throw win32_exception (ERROR_INVALID_INDEX); + + size_t c (proc_data_->capacity); + + thread_data* nd ( + static_cast ( + operator new (sizeof (thread_data) + sizeof (entry) * c))); + + size_t on (d == 0 ? 0 : d->size); + + // Copy over the data. + // + if (on != 0) + memcpy (nd->entries, d->entries, sizeof (entry) * on); + + // Zero out the rest. + // + memset (nd->entries + on, 0, sizeof (entry) * (c - on)); + + // Assign destructors to new slots [on, n). + // + for (size_t i (on); i < n; ++i) + nd->entries[i].dtor = proc_data_->dtors[i]; + + nd->size = n; + nd->capacity = c; + + operator delete (d); + TlsSetValue (index_, nd); + + nd->entries[key].value = value; + } + } + } +} diff --git a/odb/details/win32/tls.hxx b/odb/details/win32/tls.hxx new file mode 100644 index 0000000..b1dce0f --- /dev/null +++ b/odb/details/win32/tls.hxx @@ -0,0 +1,111 @@ +// file : odb/details/win32/tls.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ODB_DETAILS_WIN32_TLS_HXX +#define ODB_DETAILS_WIN32_TLS_HXX + +#include + +#include // std::size_t + +#include +#include + +namespace odb +{ + namespace details + { + class LIBODB_EXPORT tls_common + { + public: + static std::size_t + _allocate (void (*dtor) (void*)); + + static void* + _get (std::size_t key); + + static void + _set (std::size_t key, void* value); + }; + + template + class tls: protected tls_common + { + public: + tls (); + + T& + get () const; + + private: + tls (const tls&); + tls& operator= (const tls&); + + private: + static void + key_init (); + + static void + destructor (void*); + + private: + static once once_; + static std::size_t key_; + }; + + template + class tls: protected tls_common + { + public: + tls (); + + T* + get () const; + + void + set (T* p); + + private: + tls (const tls&); + tls& operator= (const tls&); + + private: + static void + key_init (); + + private: + static once once_; + static std::size_t key_; + }; + + template + inline T& + tls_get (const tls& t) + { + return t.get (); + } + + template + inline T* + tls_get (const tls& t) + { + return t.get (); + } + + template + inline void + tls_set (tls& t, T* p) + { + t.set (p); + } + } +} + +#include +#include + +#include + +#endif // ODB_DETAILS_WIN32_TLS_HXX diff --git a/odb/details/win32/tls.ixx b/odb/details/win32/tls.ixx new file mode 100644 index 0000000..7b0f341 --- /dev/null +++ b/odb/details/win32/tls.ixx @@ -0,0 +1,22 @@ +// file : odb/details/win32/tls.ixx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +namespace odb +{ + namespace details + { + template + inline tls:: + tls () + { + } + + template + inline tls:: + tls () + { + } + } +} diff --git a/odb/details/win32/tls.txx b/odb/details/win32/tls.txx new file mode 100644 index 0000000..f20b6a9 --- /dev/null +++ b/odb/details/win32/tls.txx @@ -0,0 +1,87 @@ +// file : odb/details/win32/tls.txx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include // std::auto_ptr + +#include + +using namespace std; + +namespace odb +{ + namespace details + { + // tls + // + template + once tls::once_; + + template + size_t tls::key_; + + template + T& tls:: + get () const + { + once_.call (key_init); + void* v (_get (key_)); + + if (v != 0) + return *static_cast (v); + + auto_ptr p (new T); + _set (key_, p.get ()); + + T& r (*p); + p.release (); + return r; + } + + template + void tls:: + key_init () + { + key_ = _allocate (destructor); + } + + template + void tls:: + destructor (void* v) + { + delete static_cast (v); + } + + // tls + // + template + once tls::once_; + + template + size_t tls::key_; + + template + T* tls:: + get () const + { + once_.call (key_init); + return static_cast (_get (key_)); + } + + template + void tls:: + set (T* p) + { + once_.call (key_init); + _set (key_, p); + } + + template + void tls:: + key_init () + { + key_ = _allocate (0); + } + } +} -- cgit v1.1