Add basic exception supports and tagged refcounting.
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void RuntimeAssertFailed(const char* location, const char* message) {
|
||||
// TODO: produce stacktrace and such.
|
||||
fprintf(stderr, "%s: runtime assert: %s\n", location, message);
|
||||
abort();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef RUNTIME_ASSERT_H
|
||||
#define RUNTIME_ASSERT_H
|
||||
|
||||
void RuntimeAssertFailed(const char* location, const char* message);
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
#define RuntimeAssert(condition, message) \
|
||||
if (!(condition)) { \
|
||||
RuntimeAssertFailed( __FILE__ ":" TOSTRING(__LINE__), message); \
|
||||
}
|
||||
|
||||
#endif // RUNTIME_ASSERT_H
|
||||
@@ -0,0 +1,29 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Assert.h"
|
||||
#include "Exceptions.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void ThrowNullPointerException() {
|
||||
void* pc = __builtin_return_address(1);
|
||||
char message[100];
|
||||
snprintf(message, sizeof(message), "NullPointerException at %p", pc);
|
||||
fprintf(stderr, "%s\n", message);
|
||||
// TODO: throw it for real.
|
||||
RuntimeAssert(false, "Throwing is unsupported");
|
||||
}
|
||||
|
||||
void ThrowArrayIndexOutOfBoundsException() {
|
||||
void* pc = __builtin_return_address(1);
|
||||
char message[100];
|
||||
snprintf(message, sizeof(message), "ArrayIndexOutOfBoundsException at %p", pc);
|
||||
fprintf(stderr, "%s\n", message);
|
||||
// TODO: throw it for real.
|
||||
RuntimeAssert(false, "Throwing is unsupported");
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef RUNTIME_EXCEPTIONS_H
|
||||
#define RUNTIME_EXCEPTIONS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Throws null pointer exception. Context is evaluated from caller's address.
|
||||
void ThrowNullPointerException();
|
||||
// Throws array index out of bounds exception.
|
||||
// Context is evaluated from caller's address.
|
||||
void ThrowArrayIndexOutOfBoundsException();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // RUNTIME_NAMES_H
|
||||
@@ -12,7 +12,7 @@ ArenaContainer::ArenaContainer(uint32_t size) {
|
||||
ArenaContainerHeader* header = reinterpret_cast<ArenaContainerHeader*>(
|
||||
calloc(size + sizeof(ArenaContainerHeader), 1));
|
||||
header_ = header;
|
||||
header->ref_count_ = 1;
|
||||
header->ref_count_ = CONTAINER_TAG_INCREMENT;
|
||||
header->current_ = reinterpret_cast<uint8_t*>(header_) + sizeof(ArenaContainerHeader);
|
||||
header->end_ = header->current_ + size;
|
||||
}
|
||||
@@ -21,7 +21,7 @@ void ObjectContainer::Init(const TypeInfo* type_info, uint32_t elements) {
|
||||
header_ = reinterpret_cast<ContainerHeader*>(
|
||||
calloc(sizeof(ContainerHeader) + sizeof(ObjHeader) +
|
||||
type_info->size_ * elements, 1));
|
||||
header_->ref_count_ = 1;
|
||||
header_->ref_count_ = CONTAINER_TAG_INCREMENT;
|
||||
SetMeta(GetPlace(), type_info);
|
||||
}
|
||||
|
||||
|
||||
+45
-162
@@ -1,16 +1,29 @@
|
||||
#ifndef RUNTIME_MEMORY_H
|
||||
#define RUNTIME_MEMORY_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "Assert.h"
|
||||
#include "TypeInfo.h"
|
||||
|
||||
typedef enum {
|
||||
FRAME_SCOPE = 0,
|
||||
GLOBAL_SCOPE = 1,
|
||||
ARENA_SCOPE = 2
|
||||
SCOPE_FRAME = 0,
|
||||
SCOPE_GLOBAL = 1,
|
||||
SCOPE_ARENA = 2
|
||||
} PlacementHint;
|
||||
|
||||
// Must fit in two bits.
|
||||
typedef enum {
|
||||
// Container is normal thread local container.
|
||||
CONTAINER_TAG_NORMAL = 0,
|
||||
// Container shall not be refcounted (const data, frame locals).
|
||||
CONTAINER_TAG_NOCOUNT = 1,
|
||||
// Container shall be atomically refcounted.
|
||||
CONTAINER_TAG_SHARED = 2,
|
||||
// Actual value to increment/decrement conatiner by. Tag is in lower bits.
|
||||
CONTAINER_TAG_INCREMENT = 1 << 2,
|
||||
//
|
||||
CONTAINER_TAG_MASK = (CONTAINER_TAG_INCREMENT - 1)
|
||||
} ContainerTag;
|
||||
|
||||
// Could be made 64-bit for large memory configs.
|
||||
typedef uint32_t container_offset_t;
|
||||
|
||||
@@ -29,7 +42,7 @@ struct ArrayHeader : public ObjHeader {
|
||||
struct ContainerHeader {
|
||||
// Reference counter of container. Maybe use some upper bit of counter for
|
||||
// container type (for polymorphism in ::Release()).
|
||||
uint32_t ref_count_;
|
||||
volatile uint32_t ref_count_;
|
||||
};
|
||||
|
||||
struct ArenaContainerHeader : public ContainerHeader {
|
||||
@@ -44,17 +57,36 @@ struct ArenaContainerHeader : public ContainerHeader {
|
||||
// to objects.
|
||||
inline void AddRef(ContainerHeader* header) {
|
||||
// Looking at container type we may want to skip AddRef() totally
|
||||
// (non-escaping stack objects).
|
||||
header->ref_count_++;
|
||||
// (non-escaping stack objects, constant objects).
|
||||
switch (header->ref_count_ & CONTAINER_TAG_MASK) {
|
||||
case CONTAINER_TAG_NORMAL:
|
||||
header->ref_count_ += CONTAINER_TAG_INCREMENT;
|
||||
break;
|
||||
case CONTAINER_TAG_NOCOUNT:
|
||||
break;
|
||||
case CONTAINER_TAG_SHARED:
|
||||
__sync_fetch_and_add(&header->ref_count_, CONTAINER_TAG_INCREMENT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void FreeObject(ContainerHeader* header);
|
||||
|
||||
inline void Release(ContainerHeader* header) {
|
||||
// Looking at container type we may want to skip Release() totally
|
||||
// (non-escaping stack objects, permanent objects).
|
||||
if (--header->ref_count_ == 0) {
|
||||
FreeObject(header);
|
||||
switch (header->ref_count_ & CONTAINER_TAG_MASK) {
|
||||
case CONTAINER_TAG_NORMAL:
|
||||
if ((header->ref_count_ -= CONTAINER_TAG_INCREMENT) == CONTAINER_TAG_NORMAL) {
|
||||
FreeObject(header);
|
||||
}
|
||||
break;
|
||||
case CONTAINER_TAG_NOCOUNT:
|
||||
break;
|
||||
case CONTAINER_TAG_SHARED:
|
||||
if (__sync_sub_and_fetch(
|
||||
&header->ref_count_, CONTAINER_TAG_INCREMENT) == CONTAINER_TAG_SHARED) {
|
||||
FreeObject(header);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +148,7 @@ class ArenaContainer : public Container {
|
||||
|
||||
~ArenaContainer() {
|
||||
if (header_) {
|
||||
assert(header_->ref_count_ == 0);
|
||||
RuntimeAssert(header_->ref_count_ == 0, "Non-zero refcount");
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
@@ -149,155 +181,6 @@ class ArenaContainer : public Container {
|
||||
}
|
||||
};
|
||||
|
||||
// Raw reference to data, meaning T*, invented only for cleaness of intentions.
|
||||
template <class T>
|
||||
class RawRef {
|
||||
private:
|
||||
T* ptr_;
|
||||
public:
|
||||
RawRef(T* ptr) : ptr_(ptr) {}
|
||||
const T& get() const { return *ptr_; }
|
||||
void set(const T& value) { *ptr_ = value; }
|
||||
};
|
||||
|
||||
// Object reference, adds reference counting in container and type information.
|
||||
class AnyObjRef {
|
||||
protected:
|
||||
ObjHeader* ptr_;
|
||||
|
||||
explicit AnyObjRef(ObjHeader* ptr) : ptr_(ptr) {
|
||||
if (ptr_) {
|
||||
AddRef(container_header());
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
~AnyObjRef() {
|
||||
if (ptr_) {
|
||||
Release(container_header());
|
||||
}
|
||||
}
|
||||
|
||||
ContainerHeader* container_header() const {
|
||||
return reinterpret_cast<ContainerHeader*>(
|
||||
reinterpret_cast<uint8_t*>(ptr_) - ptr_->container_offset_negative_);
|
||||
}
|
||||
|
||||
const TypeInfo* type_info() const {
|
||||
return ptr_->type_info_;
|
||||
}
|
||||
|
||||
// Accesses raw data inside object specified by offset. Typing by M is optional and
|
||||
// will be replaced by translator typing.
|
||||
template<typename M, int offset>
|
||||
RawRef<M> at() const {
|
||||
return RawRef<M>(
|
||||
reinterpret_cast<M*>(reinterpret_cast<uint8_t*>(any_ref()) + offset));
|
||||
}
|
||||
|
||||
// Assign reference to certain object. Releases currently held object in its container and
|
||||
// adds reference to container storing given object.
|
||||
void Assign(const AnyObjRef& other) {
|
||||
// TODO: optimize for an important case where containers match?
|
||||
if (ptr_) {
|
||||
Release(container_header());
|
||||
}
|
||||
ptr_ = other.ptr_;
|
||||
if (ptr_) {
|
||||
AddRef(container_header());
|
||||
}
|
||||
}
|
||||
|
||||
// Returns pointer to the raw data referred by this reference.
|
||||
uint8_t* any_ref() const {
|
||||
if (!ptr_) return nullptr;
|
||||
return reinterpret_cast<uint8_t*>(ptr_) + sizeof(ObjHeader);
|
||||
}
|
||||
|
||||
// Uses pointer stored in object's field to create a reference to that object.
|
||||
AnyObjRef any_obj_at(int offset) const {
|
||||
assert(ptr_);
|
||||
return AnyObjRef(*reinterpret_cast<ObjHeader**>(any_ref() + offset));
|
||||
}
|
||||
|
||||
// Checks if given reference has null value.
|
||||
bool null() const { return ptr_ == nullptr; }
|
||||
};
|
||||
|
||||
// Returns typeinfo for array of type T. Specialize for types which are allowed as array elements.
|
||||
template <typename T>
|
||||
const TypeInfo* GetArrayTypeInfo() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Reference to an object with particular memory layout specified by T.
|
||||
// In real runtime will be compile time only type, on runtime all references are
|
||||
// AnyObjRef.
|
||||
template <class T>
|
||||
class ObjRef : public AnyObjRef {
|
||||
private:
|
||||
explicit ObjRef(ObjHeader* ptr) : AnyObjRef(ptr) {}
|
||||
|
||||
// Reference to raw data in owned class.
|
||||
T* ref() const {
|
||||
if (!ptr_) return nullptr;
|
||||
return reinterpret_cast<T*>(any_ref());
|
||||
}
|
||||
|
||||
template <typename T1> friend class ArrayRef;
|
||||
|
||||
public:
|
||||
// Assigns reference, compile time type-safe.
|
||||
ObjRef(const ObjRef& other) : AnyObjRef(nullptr) {
|
||||
Assign(other);
|
||||
}
|
||||
void Assign(const ObjRef<T>& other) {
|
||||
AnyObjRef::Assign(other);
|
||||
}
|
||||
|
||||
// Copies data bits to another place, reference counting is properly accounted for
|
||||
// by consulting type information.
|
||||
void CopyTo(ObjRef<T> other) const;
|
||||
|
||||
// Clones object to given container.
|
||||
ObjRef<T> Clone(ArenaContainer* container) {
|
||||
ObjRef<T> result = Alloc(container);
|
||||
CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Takes typed object reference at offset.
|
||||
template<typename M, int offset>
|
||||
ObjRef<M> obj_at() const {
|
||||
return ObjRef<M>(reinterpret_cast<M*>(any_ref() + offset));
|
||||
}
|
||||
|
||||
// Allocates properly typed object in container.
|
||||
static ObjRef<T> Alloc(ArenaContainer* container) {
|
||||
return ObjRef<T>(container->PlaceObject(T::GetTypeInfo()));
|
||||
}
|
||||
};
|
||||
|
||||
// This is an array of value types only, no object references here.
|
||||
template <class T>
|
||||
class ArrayRef : public AnyObjRef {
|
||||
protected:
|
||||
explicit ArrayRef(ArrayHeader* ptr) : AnyObjRef(ptr) {}
|
||||
ArrayHeader* header() { return reinterpret_cast<ArrayHeader*>(ptr_); }
|
||||
|
||||
public:
|
||||
static ArrayRef<T> Alloc(ArenaContainer* container, int count) {
|
||||
auto result = ArrayRef<T>(container->PlaceArray(GetArrayTypeInfo<T>(), count));
|
||||
result.header()->count_ = count;
|
||||
return result;
|
||||
}
|
||||
|
||||
RawRef<T> element_at(int index) const {
|
||||
assert(header() && index >= 0 && index < header()->count_);
|
||||
return reinterpret_cast<T*>(any_ref() + index * sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
#ifndef RUNTIME_NAMES_H
|
||||
#define RUNTIME_NAMES_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdint.h>
|
||||
|
||||
// All names in system are stored as hashes (or maybe, for debug builds,
|
||||
// as pointers to uniqued C strings containing names?).
|
||||
// There are two types of hashes:
|
||||
// - local hash, must be unique per class (CityHash64 is being used)
|
||||
// - local hash, must be unique per class/scope (CityHash64 is being used)
|
||||
// - global hash, must be unique globally (SHA1 is being used)
|
||||
// Generic guideline is that global hash is being used in global persistent context, while local
|
||||
// hashes are more local in scope.
|
||||
// Generic guideline is that global hash is being used in global persistent
|
||||
// context, while local hashes are more local in scope.
|
||||
// Local hash.
|
||||
typedef uint64_t LocalHash;
|
||||
// Hash of field name.
|
||||
|
||||
Reference in New Issue
Block a user