diff options
Diffstat (limited to 'odb/details/win32/tls.cxx')
-rw-r--r-- | odb/details/win32/tls.cxx | 236 |
1 files changed, 236 insertions, 0 deletions
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 <boris@codesynthesis.com> +// copyright : Copyright (c) 2009-2010 Code Synthesis Tools CC +// license : GNU GPL v2; see accompanying LICENSE file + +#include <windows.h> +#include <winerror.h> // ERROR_INVALID_INDEX + +#include <new> +#include <cstddef> // std::size_t + +#include <odb/details/win32/lock.hxx> +#include <odb/details/win32/tls.hxx> +#include <odb/details/win32/tls-init.hxx> +#include <odb/details/win32/exceptions.hxx> + +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<process_data*> ( + 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<thread_data*> (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<process_data*> ( + 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<thread_data*> (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<thread_data*> (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<thread_data*> ( + 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; + } + } + } +} |