| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| /* $Rev$ $Date$ */ |
| |
| #ifndef tuscany_gc_hpp |
| #define tuscany_gc_hpp |
| |
| /** |
| * Garbage collected memory management, using APR memory pools. |
| */ |
| |
| #include "config.hpp" |
| #ifdef WANT_MALLOC_MMAP |
| #include <sys/mman.h> |
| #include <malloc.h> |
| #endif |
| #include <stdlib.h> |
| #include <apr_general.h> |
| #include <apr_pools.h> |
| #include <apr_strings.h> |
| #include <assert.h> |
| #include <new> |
| #ifdef WANT_THREADS |
| #include <pthread.h> |
| #endif |
| |
| namespace tuscany |
| { |
| |
| #ifdef WANT_MAINTAINER_ASSERT |
| |
| /** |
| * Force a core dump on assertion violation. |
| */ |
| inline const bool assertOrFail(const bool expr) { |
| if (!expr) |
| abort(); |
| return true; |
| } |
| |
| #else |
| |
| #define assertOrFail(expr) |
| |
| #endif |
| |
| /** |
| * Pointer to a value. |
| */ |
| #ifdef WANT_RAW_PTR |
| |
| template<typename T> using gc_ptr = T*; |
| |
| #else |
| |
| template<typename T> class gc_ptr { |
| public: |
| inline gc_ptr(T* const ptr = NULL) noexcept : ptr(ptr) { |
| } |
| |
| inline ~gc_ptr() noexcept { |
| } |
| |
| inline gc_ptr(const gc_ptr& r) noexcept : ptr(r.ptr) { |
| } |
| |
| gc_ptr& operator=(const gc_ptr& r) = delete; |
| |
| inline const bool operator==(const gc_ptr& r) const noexcept { |
| if (this == &r) |
| return true; |
| return ptr == r.ptr; |
| } |
| |
| inline const bool operator==(const T* const p) const noexcept { |
| return ptr == p; |
| } |
| |
| inline const bool operator!=(const gc_ptr& r) const noexcept { |
| return !this->operator==(r); |
| } |
| |
| inline const bool operator!=(const T* const p) const noexcept { |
| return !this->operator==(p); |
| } |
| |
| inline T& operator*() const noexcept { |
| return *ptr; |
| } |
| |
| inline T* const operator->() const noexcept { |
| return ptr; |
| } |
| |
| inline operator T* const () const noexcept { |
| return ptr; |
| } |
| |
| private: |
| T* const ptr; |
| }; |
| |
| #endif |
| |
| /** |
| * Mutable pointer to an immutable value. |
| */ |
| #ifdef WANT_RAW_PTR |
| |
| template<typename T> using gc_mutable_ptr = T*; |
| |
| #else |
| |
| template<typename T> class gc_mutable_ptr { |
| public: |
| inline gc_mutable_ptr(T* const ptr = NULL) noexcept : ptr(ptr) { |
| } |
| |
| inline ~gc_mutable_ptr() noexcept { |
| } |
| |
| inline gc_mutable_ptr(const gc_mutable_ptr& r) noexcept : ptr(r.ptr) { |
| } |
| |
| inline gc_mutable_ptr& operator=(T* const p) noexcept { |
| ptr = p; |
| return *this; |
| } |
| |
| inline gc_mutable_ptr& operator=(const gc_mutable_ptr& r) noexcept { |
| if (this == &r) |
| return *this; |
| ptr = r.ptr; |
| return *this; |
| } |
| |
| inline const bool operator==(const gc_mutable_ptr& r) const noexcept { |
| if (this == &r) |
| return true; |
| return ptr == r.ptr; |
| } |
| |
| inline const bool operator==(T* const p) const noexcept { |
| return ptr == p; |
| } |
| |
| inline const bool operator!=(const gc_mutable_ptr& r) const noexcept { |
| return !this->operator==(r); |
| } |
| |
| inline const bool operator!=(T* const p) const noexcept { |
| return !this->operator==(p); |
| } |
| |
| inline T& operator*() const noexcept { |
| return *ptr; |
| } |
| |
| inline T* const operator->() const noexcept { |
| return ptr; |
| } |
| |
| inline operator T* const () const noexcept { |
| return ptr; |
| } |
| |
| private: |
| T* ptr; |
| }; |
| |
| #endif |
| |
| /** |
| * Initialize APR. |
| */ |
| class gc_apr_context_t { |
| public: |
| inline gc_apr_context_t() { |
| apr_initialize(); |
| } |
| } gc_apr_context; |
| |
| /** |
| * Garbage collected APR memory pool. |
| */ |
| class gc_pool { |
| public: |
| inline gc_pool() noexcept : apr_pool(NULL) { |
| } |
| |
| inline gc_pool(apr_pool_t* const p) noexcept : apr_pool(p) { |
| } |
| |
| inline gc_pool(const gc_pool& pool) noexcept : apr_pool(pool.apr_pool) { |
| } |
| |
| gc_pool& operator=(const gc_pool& pool) = delete; |
| |
| private: |
| friend apr_pool_t* const pool(const gc_pool& pool) noexcept; |
| friend class gc_global_pool_t; |
| friend class gc_child_pool; |
| friend class gc_local_pool; |
| friend class gc_scoped_pool; |
| |
| apr_pool_t* const apr_pool; |
| }; |
| |
| /** |
| * Return the APR pool used by a gc_pool. |
| */ |
| inline apr_pool_t* const pool(const gc_pool& pool) noexcept { |
| return pool.apr_pool; |
| } |
| |
| /** |
| * Maintain a stack of memory pools. |
| */ |
| #ifdef WANT_THREADS |
| |
| #ifdef __clang__ |
| |
| class gc_pool_stack_t { |
| public: |
| inline gc_pool_stack_t() noexcept : key(mkkey()) { |
| } |
| |
| inline operator apr_pool_t* const () const noexcept { |
| return (apr_pool_t* const )pthread_getspecific(key); |
| } |
| |
| const gc_pool_stack_t& operator=(apr_pool_t* const p) noexcept { |
| const int rc = pthread_setspecific(key, p); |
| assertOrFail(rc == 0); |
| return *this; |
| } |
| |
| private: |
| const pthread_key_t key; |
| |
| inline const pthread_key_t mkkey() noexcept { |
| pthread_key_t k; |
| const int rc = pthread_key_create(&k, NULL); |
| assertOrFail(rc == 0); |
| return k; |
| } |
| |
| } gc_pool_stack; |
| |
| #else |
| |
| __thread apr_pool_t* gc_pool_stack = NULL; |
| |
| #endif |
| |
| #else |
| |
| apr_pool_t* gc_pool_stack = NULL; |
| |
| #endif |
| |
| /** |
| * Push a pool onto the stack. |
| */ |
| inline apr_pool_t* const gc_push_pool(apr_pool_t* const pool) noexcept { |
| apr_pool_t* const p = gc_pool_stack; |
| gc_pool_stack = pool; |
| return p; |
| } |
| |
| /** |
| * Pop a pool from the stack. |
| */ |
| inline apr_pool_t* const gc_pop_pool(apr_pool_t* const pool) noexcept { |
| apr_pool_t* const p = gc_pool_stack; |
| gc_pool_stack = pool; |
| return p; |
| } |
| |
| /** |
| * Return the current memory pool. |
| */ |
| inline apr_pool_t* const gc_current_pool() noexcept { |
| apr_pool_t* const p = gc_pool_stack; |
| if (p != NULL) |
| return p; |
| |
| // Create a parent pool for the current thread |
| apr_pool_t* pp; |
| apr_pool_create(&pp, NULL); |
| assertOrFail(pp != NULL); |
| gc_push_pool(pp); |
| return pp; |
| } |
| |
| /** |
| * A child memory pool, which will be destroyed when its parent pool is destroyed. |
| */ |
| class gc_child_pool : public gc_pool { |
| public: |
| |
| inline gc_child_pool() noexcept : gc_pool(mkpool()), owner(true) { |
| } |
| |
| inline gc_child_pool(const gc_child_pool& p) noexcept : gc_pool(p.apr_pool), owner(false) { |
| } |
| |
| gc_child_pool& operator=(const gc_child_pool& p) = delete; |
| |
| private: |
| const bool owner; |
| |
| inline apr_pool_t* const mkpool() noexcept { |
| apr_pool_t* p; |
| apr_pool_create(&p, gc_current_pool()); |
| assertOrFail(p != NULL); |
| return p; |
| } |
| }; |
| |
| /** |
| * A local pool scope, which will be destroyed when exiting the current scope. |
| */ |
| class gc_local_pool : public gc_pool { |
| public: |
| |
| inline gc_local_pool() noexcept : gc_pool(mkpool()), owner(true) { |
| } |
| |
| inline ~gc_local_pool() noexcept { |
| if (owner) |
| apr_pool_destroy(apr_pool); |
| } |
| |
| inline gc_local_pool(const gc_local_pool& p) noexcept : gc_pool(p.apr_pool), owner(false) { |
| } |
| |
| gc_local_pool& operator=(const gc_local_pool& p) = delete; |
| |
| private: |
| const bool owner; |
| |
| inline apr_pool_t* const mkpool() noexcept { |
| apr_pool_t* p; |
| apr_pool_create(&p, gc_current_pool()); |
| assertOrFail(p != NULL); |
| return p; |
| } |
| }; |
| |
| /** |
| * A memory pool scope, used to setup a scope in which a particular pool will be |
| * used for all allocations. Will be destroyed when existing the current scope. |
| */ |
| class gc_scoped_pool : public gc_pool { |
| public: |
| |
| inline gc_scoped_pool() noexcept : gc_pool(mkpool()), prev(gc_current_pool()), owner(true) { |
| gc_push_pool(apr_pool); |
| } |
| |
| inline gc_scoped_pool(apr_pool_t* const p) noexcept : gc_pool(p), prev(gc_current_pool()), owner(false) { |
| gc_push_pool(apr_pool); |
| } |
| |
| inline ~gc_scoped_pool() noexcept { |
| if (owner) |
| apr_pool_destroy(apr_pool); |
| gc_pop_pool(prev); |
| } |
| |
| inline gc_scoped_pool(const gc_scoped_pool& p) noexcept : gc_pool(p.apr_pool), prev(p.prev), owner(false) { |
| } |
| |
| gc_scoped_pool& operator=(const gc_scoped_pool& p) = delete; |
| |
| private: |
| apr_pool_t* const prev; |
| const bool owner; |
| |
| inline apr_pool_t* const mkpool() noexcept { |
| apr_pool_t* p; |
| apr_pool_create(&p, gc_current_pool()); |
| assertOrFail(p != NULL); |
| return p; |
| } |
| }; |
| |
| /** |
| * Allocates a pointer to an object allocated from a memory pool and |
| * register a cleanup callback for it. |
| */ |
| template<typename T> inline apr_status_t gc_pool_cleanup(void* v) { |
| T* t = (T*)v; |
| t->~T(); |
| return APR_SUCCESS; |
| } |
| |
| template<typename T> inline T* const gc_new(apr_pool_t* const p) noexcept { |
| void* gc_new_ptr = apr_palloc(p, sizeof(T)); |
| assertOrFail(gc_new_ptr != NULL); |
| apr_pool_cleanup_register(p, gc_new_ptr, gc_pool_cleanup<T>, apr_pool_cleanup_null) ; |
| return (T*)(gc_new_ptr); |
| } |
| |
| template<typename T> inline T* const gc_new(const gc_pool& p) noexcept { |
| return gc_new<T>(pool(p)); |
| } |
| |
| template<typename T> inline T* const gc_new() noexcept { |
| return gc_new<T>(gc_current_pool()); |
| } |
| |
| template<typename T> inline apr_status_t gc_pool_acleanup(void* v) { |
| size_t* m = (size_t*)v; |
| size_t n = *m; |
| T* t = (T*)(m + 1); |
| for (size_t i = 0; i < n; i++, t++) |
| t->~T(); |
| return APR_SUCCESS; |
| } |
| |
| template<typename T> inline T* const gc_anew(apr_pool_t* const p, const size_t n) noexcept { |
| size_t* const gc_anew_ptr = (size_t*)apr_palloc(p, sizeof(size_t) + sizeof(T) * n); |
| assertOrFail(gc_anew_ptr != NULL); |
| *gc_anew_ptr = n; |
| apr_pool_cleanup_register(p, gc_anew_ptr, gc_pool_acleanup<T>, apr_pool_cleanup_null) ; |
| return (T*)(gc_anew_ptr + 1); |
| } |
| |
| template<typename T> inline T* const gc_anew(const gc_pool& p, const size_t n) noexcept { |
| return gc_anew<T>(pool(p), n); |
| } |
| |
| template<typename T> inline T* const gc_anew(const size_t n) noexcept { |
| return gc_anew<T>(gc_current_pool(), n); |
| } |
| |
| /** |
| * Allocate an array of chars. |
| */ |
| inline char* const gc_cnew(apr_pool_t* const p, const size_t n) noexcept { |
| char* const gc_cnew_ptr = (char*)apr_palloc(p, n); |
| assertOrFail(gc_cnew_ptr != NULL); |
| return gc_cnew_ptr; |
| } |
| |
| inline char* const gc_cnew(const size_t n) noexcept { |
| return gc_cnew(gc_current_pool(), n); |
| } |
| |
| /** |
| * Mutable reference to an immutable value. |
| */ |
| template<typename T> class gc_mutable_ref { |
| public: |
| inline gc_mutable_ref() noexcept : ptr(new (gc_new<T>()) T()) { |
| } |
| |
| inline ~gc_mutable_ref() noexcept { |
| } |
| |
| inline gc_mutable_ref(const gc_mutable_ref& r) noexcept : ptr(r.ptr) { |
| } |
| |
| inline gc_mutable_ref(const T& v) noexcept : ptr(new (gc_new<T>()) T(v)) { |
| } |
| |
| inline gc_mutable_ref& operator=(const gc_mutable_ref& r) noexcept { |
| if (this == &r) |
| return *this; |
| ptr = r.ptr; |
| return *this; |
| } |
| |
| inline gc_mutable_ref& operator=(const T& v) noexcept { |
| ptr = new (gc_new<T>()) T(v); |
| return *this; |
| } |
| |
| inline const bool operator==(const gc_mutable_ref& r) const noexcept { |
| if (this == &r) |
| return true; |
| if (ptr == r.ptr) |
| return true; |
| return *ptr == *r.ptr; |
| } |
| |
| inline const bool operator==(const T& v) const noexcept { |
| return *ptr == v; |
| } |
| |
| inline const bool operator!=(const gc_mutable_ref& r) const noexcept { |
| return !this->operator==(r); |
| } |
| |
| inline const bool operator!=(const T& v) const noexcept { |
| return !this->operator==(v); |
| } |
| |
| inline operator T&() const noexcept { |
| return *ptr; |
| } |
| |
| inline operator T* const () const noexcept { |
| return ptr; |
| } |
| |
| private: |
| T* ptr; |
| }; |
| |
| /** |
| * Pool based equivalent of the standard malloc function. |
| */ |
| inline void* gc_pool_malloc(size_t n) { |
| size_t* ptr = (size_t*)apr_palloc(gc_current_pool(), sizeof(size_t) + n); |
| assertOrFail(ptr != NULL); |
| *ptr = n; |
| return ptr + 1; |
| } |
| |
| /** |
| * Pool based equivalent of the standard realloc function. |
| */ |
| inline void* gc_pool_realloc(void* ptr, size_t n) { |
| size_t size = *(((size_t*)ptr) - 1); |
| size_t* rptr = (size_t*)apr_palloc(gc_current_pool(), sizeof(size_t) + n); |
| assertOrFail(rptr != NULL); |
| *rptr = n; |
| memcpy(rptr + 1, ptr, size < n? size : n); |
| return rptr + 1; |
| } |
| |
| /** |
| * Pool based equivalent of the standard free function. |
| */ |
| inline void gc_pool_free(unused void* ptr) { |
| // Memory allocated from a pool is freed when the pool is freed |
| } |
| |
| /** |
| * Pool based equivalent of the standard strdup function. |
| */ |
| inline char* gc_pool_strdup(const char* str) { |
| char* dptr = (char*)gc_pool_malloc(strlen(str) + 1); |
| assertOrFail(dptr != NULL); |
| strcpy(dptr, str); |
| return dptr; |
| } |
| |
| #ifdef WANT_MALLOC_MMAP |
| |
| /** |
| * Mmap based memory allocation functions. |
| */ |
| |
| /** |
| * Mmap based equivalent of the standard malloc function. |
| */ |
| inline void* gc_mmap_malloc(size_t n, unused const void* caller) { |
| //printf("gc_mmap_malloc %d", n); |
| size_t* ptr = (size_t*)mmap(NULL, sizeof(size_t) + n, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); |
| assertOrFail(ptr != NULL); |
| *ptr = n; |
| //printf(" %p\n", ptr + 1); |
| return ptr + 1; |
| } |
| |
| /** |
| * Mmap based equivalent of the standard realloc function. |
| */ |
| inline void* gc_mmap_realloc(void* ptr, size_t n, const void* caller) { |
| if (ptr == NULL) |
| return gc_mmap_malloc(n, caller);; |
| //printf("gc_mmap_realloc %p %d", ptr, n); |
| size_t size = *(((size_t*)ptr) - 1); |
| size_t* rptr = (size_t*)mremap(((size_t*)ptr) - 1, sizeof(size_t) + size, sizeof(size_t) + n, MREMAP_MAYMOVE, NULL); |
| assertOrFail(rptr != NULL); |
| *rptr = n; |
| //printf(" %p\n", rptr + 1); |
| return rptr + 1; |
| } |
| |
| /** |
| * Mmap based equivalent of the standard free function. |
| */ |
| inline void gc_mmap_free(void* ptr, unused const void* caller) { |
| //printf("gc_mmap_free %p\n", ptr); |
| if (ptr == NULL) |
| return; |
| size_t size = *(((size_t*)ptr) - 1); |
| munmap(((size_t*)ptr) - 1, sizeof(size_t) + size); |
| } |
| |
| /** |
| * Mmap based equivalent of the standard memalign function. |
| */ |
| inline void* gc_mmap_memalign(unused size_t alignment, size_t n, unused const void* caller) { |
| //printf("gc_mmap_memalign %d %d\n", alignment, n); |
| return gc_mmap_malloc(n, caller); |
| } |
| |
| /** |
| * Install the mmap based memory allocation functions. |
| */ |
| inline void gc_mmap_init_hook(void) { |
| __malloc_hook = gc_mmap_malloc; |
| __realloc_hook = gc_mmap_realloc; |
| __free_hook = gc_mmap_free; |
| __memalign_hook = gc_mmap_memalign; |
| } |
| |
| #endif |
| |
| } |
| |
| #ifdef WANT_MALLOC_MMAP |
| |
| void (*__malloc_initialize_hook)(void) = tuscany::gc_mmap_init_hook; |
| |
| #endif |
| |
| #endif /* tuscany_gc_hpp */ |