Add basic exception supports and tagged refcounting.

This commit is contained in:
Nikolay Igotti
2016-10-14 11:10:19 +03:00
parent 25c5349004
commit 5786f5a2af
7 changed files with 121 additions and 168 deletions
+8
View File
@@ -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();
}
+14
View File
@@ -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
+29
View File
@@ -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
+19
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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
+4 -4
View File
@@ -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.