/* * 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 #include #endif #include #include #include #include #include #include #ifdef WANT_THREADS #include #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 using gc_ptr = T*; #else template 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 using gc_mutable_ptr = T*; #else template 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: unused 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 inline apr_status_t gc_pool_cleanup(void* v) { T* t = (T*)v; t->~T(); return APR_SUCCESS; } template 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, apr_pool_cleanup_null) ; return (T*)(gc_new_ptr); } template inline T* const gc_new(const gc_pool& p) noexcept { return gc_new(pool(p)); } template inline T* const gc_new() noexcept { return gc_new(gc_current_pool()); } template 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 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, apr_pool_cleanup_null) ; return (T*)(gc_anew_ptr + 1); } template inline T* const gc_anew(const gc_pool& p, const size_t n) noexcept { return gc_anew(pool(p), n); } template inline T* const gc_anew(const size_t n) noexcept { return gc_anew(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 class gc_mutable_ref { public: inline gc_mutable_ref() noexcept : ptr(new (gc_new()) 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(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(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 */