blob: 2b65132588033b979a00bb05e123cf2e797db0ad [file] [log] [blame]
/*
* 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 */