git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@1419985 13f79535-47bb-0310-9956-ffa450edef68
640 lines
15 KiB
C++
640 lines
15 KiB
C++
/*
|
|
* 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* 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* 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 noexcept {
|
|
return (apr_pool_t*)pthread_getspecific(key);
|
|
}
|
|
|
|
inline const gc_pool_stack_t& operator=(apr_pool_t* p) noexcept {
|
|
pthread_setspecific(key, p);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
pthread_key_t key;
|
|
|
|
pthread_key_t mkkey() {
|
|
pthread_key_t k;
|
|
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* 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* 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;
|
|
|
|
apr_pool_t* const mkpool() {
|
|
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;
|
|
|
|
apr_pool_t* const mkpool() {
|
|
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* 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;
|
|
|
|
apr_pool_t* const mkpool() {
|
|
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 */
|