basic error skel

error_handling
Avril 4 years ago
parent a5d5d1b9fd
commit a293133f29
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -5,7 +5,7 @@ PROJECT=cow
AUTHOR=Avril (Flanchan) <flanchan@cumallover.me> AUTHOR=Avril (Flanchan) <flanchan@cumallover.me>
VERSION_MAJOR=0 VERSION_MAJOR=0
VERSION_MINOR=1.5 VERSION_MINOR=2.0r0
VERSION=$(VERSION_MAJOR).$(VERSION_MINOR) VERSION=$(VERSION_MAJOR).$(VERSION_MINOR)
ifeq ($(PREFIX),) ifeq ($(PREFIX),)
@ -156,4 +156,4 @@ uninstall:
$(PROJECT)-cpp-test: lib$(PROJECT).so $(PROJECT)-cpp-test: lib$(PROJECT).so
g++ -O3 -flto --std=gnu++20 -Iinclude/ -g -Wall -Wextra src/test/*.cpp -o $@ -l:$< -Wl,-flto -Wl,-O3 g++ -O3 -flto --std=gnu++20 -Iinclude/ -g -Wall -Wextra src/test/*.cpp -o $@ -l:$< -Wl,-flto -Wl,-O3
valgrind ./$@ -valgrind ./$@

@ -18,7 +18,13 @@ enum cow_err_kind {
COW_ERR_SIZE, COW_ERR_SIZE,
/// `mmap()` failed. /// `mmap()` failed.
COW_ERR_MAP, COW_ERR_MAP,
_COW_ERR_SIZE,
}; };
// Message string corresponding to this error.
const char* const * cow_err_msg(enum cow_err_kind kind);
// The last error that `libcow` produced on this thread.
enum cow_err_kind cow_err();
// Copy-on-write mapped memory. // Copy-on-write mapped memory.
typedef struct cow_mapped_slice cow_t; typedef struct cow_mapped_slice cow_t;

@ -11,25 +11,7 @@
#include <cow.h> #include <cow.h>
#define LIKELY(ex) __builtin_expect(!!(ex), 1) #include "macros.h"
#define UNLIKELY(ex) __builtin_expect(!!(ex), 0)
#define box(t) aligned_alloc(_Alignof(t), sizeof(t))
#if defined(DEBUG) || defined(COW_TRACE)
#define TRACE(msg, ...) (fprintf(stderr, "<libcow> [TRACE] %s->%s():%d: " msg "\n", __FILE__, __func__, __LINE__, __VA_ARGS__), (void)0)
#else
#define TRACE(msg, ...) ((void)0)
#endif
#if !defined(COW_NO_ASSERT)
#define ASSERT(expr, msg) do { if(!(expr)) die("assertion failed: `" #expr "`: " msg); } while(0)
#else
#define ASSERT(op, msg) ((void)0)
#endif
#define LASSERT(expr, msg) ASSERT(LIKELY(expr), "(unexpected) " msg)
#define UASSERT(expr, msg) ASSERT(UNLIKELY(expr), "(expected) " msg)
// struct cow { ... } // struct cow { ... }
#include "cow_t.h" #include "cow_t.h"
@ -48,6 +30,10 @@ static __attribute__((noreturn)) __attribute__((noinline)) __attribute__((cold))
static inline cow_t* box_value(cow_t v) static inline cow_t* box_value(cow_t v)
{ {
if(UNLIKELY(v.poisoned)) {
TRACE("WARNING: v is poisoned! not boxing { origin = %p, fd = 0x%x, size = %lu } -> 0 (%lu bytes)", v.origin, v.fd, v.size, sizeof(cow_t));
return NULL;
}
cow_t* boxed = box(cow_t); cow_t* boxed = box(cow_t);
LASSERT(boxed != NULL, "aligned_alloc() returned `NULL` for `cow_t`"); LASSERT(boxed != NULL, "aligned_alloc() returned `NULL` for `cow_t`");
@ -69,8 +55,9 @@ static inline int shm_fd(size_t size)
#else #else
int fd = memfd_create("cow_create:shm_fd", 0); int fd = memfd_create("cow_create:shm_fd", 0);
#endif #endif
if(fd<=0) die("cow_create:shm_fd:memfd_create"); SOFT_ASSERT(fd>0, COW_ERR_FDCREATE, -1);
if(ftruncate(fd, size) != 0) die("cow_create:shm_fd:ftruncate"); SOFT_ASSERT(ftruncate(fd, size) == 0, COW_ERR_SIZE, (close(fd), -1));
return fd; return fd;
} }
@ -93,11 +80,16 @@ inline internal cow_t _cow_create_unboxed(size_t size)
{ {
cow_t ret; cow_t ret;
//ret.error = COW_POISON_NONE;
ret.size = size; ret.size = size;
ret.fd = shm_fd(size); if( (ret.poisoned =
((ret.fd = shm_fd(size)) == -1))
) {
ret.origin = NULL;
return ret;
}
ret.origin = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, ret.fd, 0); ret.origin = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, ret.fd, 0);
if(ret.origin == MAP_FAILED) die("cow_create:mmap");
SOFT_ASSERT(ret.origin != MAP_FAILED, COW_ERR_MAP, (ret.poisoned = true, close(ret.fd), ret));
TRACE("mapped new origin cow page of %lu size at %p (memfd %d)", size, ret.origin, ret.fd); TRACE("mapped new origin cow page of %lu size at %p (memfd %d)", size, ret.origin, ret.fd);
return ret; return ret;
@ -109,6 +101,10 @@ cow_t* cow_create(size_t size)
inline internal void _cow_free_unboxed(const cow_t* cow) inline internal void _cow_free_unboxed(const cow_t* cow)
{ {
if(UNLIKELY(cow->poisoned)) {
TRACE("WARNING: attempted to free poisoned object at %p", (const void*)cow);
return;
}
TRACE("unmapping %s cow of %lu size from %p (fd %d, real fd %d)", cow_is_fake(cow) ? "fake" : "and closing fd of origin", cow->size, cow->origin, cow->fd, cow_real_fd(cow)); TRACE("unmapping %s cow of %lu size from %p (fd %d, real fd %d)", cow_is_fake(cow) ? "fake" : "and closing fd of origin", cow->size, cow->origin, cow->fd, cow_real_fd(cow));
munmap(cow->origin, cow->size); munmap(cow->origin, cow->size);
if(!cow_is_fake(cow)) if(!cow_is_fake(cow))
@ -117,17 +113,25 @@ inline internal void _cow_free_unboxed(const cow_t* cow)
void cow_free(cow_t* restrict cow) void cow_free(cow_t* restrict cow)
{ {
if(UNLIKELY(!cow)) return;
_cow_free_unboxed(cow); _cow_free_unboxed(cow);
free(cow); free(cow);
} }
cow_t* cow_clone(const cow_t* cow) cow_t* cow_clone(const cow_t* cow)
{ {
if(UNLIKELY(cow->poisoned)) {
TRACE("WARNING: attempted to clone poisoned object at %p", (const void*)cow);
return NULL;
}
cow_t clone; cow_t clone;
//clone.error = COW_POISON_NONE; //clone.error = COW_POISON_NONE;
clone.poisoned=false;
clone.origin = mmap(cow->origin, cow->size, PROT_READ|PROT_WRITE, MAP_PRIVATE, cow_real_fd(cow), 0); clone.origin = mmap(cow->origin, cow->size, PROT_READ|PROT_WRITE, MAP_PRIVATE, cow_real_fd(cow), 0);
if(clone.origin == MAP_FAILED) die("cow_clone:mmap"); SOFT_ASSERT(clone.origin != MAP_FAILED, COW_ERR_MAP, NULL);
clone.fd = (~INT_MAX) | cow->fd; clone.fd = (~INT_MAX) | cow->fd;
clone.size = cow->size; clone.size = cow->size;

@ -2,11 +2,14 @@
#include <utility> #include <utility>
#include "macros.h"
#include "cow_t.h" #include "cow_t.h"
struct Cow::_inner { struct Cow::_inner {
cow_t cow; cow_t cow;
// NOTE: We can assume cow isn't poisoned here, since the constructors throw if it is.
inline const cow_t* ptr() const { return &cow; } inline const cow_t* ptr() const { return &cow; }
inline cow_t* ptr() { return &cow; } inline cow_t* ptr() { return &cow; }
@ -19,11 +22,18 @@ struct Cow::_inner {
_inner() = delete; _inner() = delete;
}; };
Cow::_inner::~_inner() { Cow::_inner::~_inner() {
if(UNLIKELY(cow.poisoned)) return;
_cow_free_unboxed(ptr()); _cow_free_unboxed(ptr());
cow.poisoned=true;
}
Cow::_inner::_inner(size_t sz) : cow(_cow_create_unboxed(sz)){
//TODO: Real exception type?
if(UNLIKELY(cow.poisoned)) throw "POISONED";
} }
Cow::_inner::_inner(size_t sz) : cow(_cow_create_unboxed(sz)){}
Cow::_inner::_inner(cow_t* ptr) : cow(*ptr) Cow::_inner::_inner(cow_t* ptr) : cow(*ptr)
{ {
if(UNLIKELY(cow.poisoned)) throw "POISONED";
free(ptr); free(ptr);
} }

@ -6,6 +6,8 @@
#ifdef __cplusplus #ifdef __cplusplus
#define restrict __restrict__ #define restrict __restrict__
extern "C" { extern "C" {
#else
#include <stdbool.h>
#endif #endif
#include <stdlib.h> #include <stdlib.h>
@ -20,6 +22,10 @@ struct cow_mapped_slice {
size_t size; // Should be at this offset. size_t size; // Should be at this offset.
int fd; // Will be ORd with ~INT_MAX if it's a clone. Will be >0 if it's the original. int fd; // Will be ORd with ~INT_MAX if it's a clone. Will be >0 if it's the original.
// For unboxed cow_ts. If there was an error constructing, it will be set to `true`.
// If this is true. All resources held by this object will have been freed already.
bool poisoned;
}; // cow_t, *cow }; // cow_t, *cow
#ifdef __cplusplus #ifdef __cplusplus

@ -0,0 +1,3 @@
#include "error.h"
_Thread_local internal enum cow_err_kind _cow_last_error = COW_ERR_NONE;

@ -0,0 +1,46 @@
#include <array>
#include <cow.h>
#include "error.h"
namespace _cow_error {
constexpr const size_t SIZE = (size_t)_COW_ERR_SIZE;
consteval inline void setmsg(std::array<const char*, SIZE>& ar, enum cow_err_kind kind, const char* msg)
{
ar[kind] = msg;
}
consteval inline std::array<const char*, SIZE> gen_msg_table()
{
std::array<const char*, SIZE> ret;
setmsg(ret, COW_ERR_UNKNOWN, "unknown error");
setmsg(ret, COW_ERR_NONE, "success");
setmsg(ret, COW_ERR_FDCREATE, "failed to create shmfd (memfd_create())");
setmsg(ret, COW_ERR_SIZE, "failed to set shmfd size (ftruncate())");
setmsg(ret, COW_ERR_MAP, "failed to map shmfd (mmap())");
return ret;
}
const std::array<const char*, SIZE> _cow_message_table = gen_msg_table();
}
using namespace _cow_error;
extern "C" {
enum cow_err_kind cow_err() {
return _cow_last_error;
}
internal void _cow_set_err(enum cow_err_kind kind) {
_cow_last_error = kind;
}
const char* const * cow_err_msg(enum cow_err_kind kind)
{
auto idx = ((size_t)kind);
if ( idx >= SIZE ) return nullptr;
else return &_cow_message_table[idx];
}
}

@ -9,35 +9,21 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#define _Thread_local thread_local
#define restrict __restrict__ #define restrict __restrict__
#endif #endif
/* void _cow_set_err(enum cow_err_kind kind) internal;
Failed: extern _Thread_local internal enum cow_err_kind _cow_last_error;
typedef struct cow_error cow_error;
union poison {
struct cow_error {
cow_error* prev;
const char* message; #define SOFT_ASSERT(ex, kind, ret) do { if(!(ex)) { return (_cow_last_error = (kind), (ret)); } } while(0)
#define SOFT_LASSERT(ex, kind, ret) SOFT_ASSERT(LIKELY(ex), kind, ret)
enum cow_err kind : 31; #define SOFT_UASSERT(ex, kind, ret) SOFT_ASSERT(UNLIKELY(ex), kind, ret)
/// Should we free this instance?
unsigned char owned : 1;
} e_static;
cow_error* e_global;
};
#define COW_ERROR_NONE ((struct cow_error){ .prev = NULL, .message = NULL, .kind = COW_ERR_SUCCESS, .owned = 0 })
#define COW_POISON_NONE ((union poison){ .e_static = COW_ERROR_NONE})
void _cow_poison(cow_t* restrict cow, enum cow_err kind, const char *msg) internal;
void _cow_poison_ref(cow_t* restrict cow, cow_error* globl) internal;
*/
#ifdef __cplusplus #ifdef __cplusplus
} }
#undef restrict #undef restrict
#undef _Thread_local
#endif #endif
#endif /* _COW_ERROR_H */ #endif /* _COW_ERROR_H */

@ -3,3 +3,24 @@
#define internal __attribute__((visibility("internal"))) #define internal __attribute__((visibility("internal")))
#endif /* internal */ #endif /* internal */
#define LIKELY(ex) __builtin_expect(!!(ex), 1)
#define UNLIKELY(ex) __builtin_expect(!!(ex), 0)
#define box(t) aligned_alloc(_Alignof(t), sizeof(t))
#if defined(DEBUG) || defined(COW_TRACE)
#define TRACE(msg, ...) (fprintf(stderr, "<libcow> [TRACE] %s->%s():%d: " msg "\n", __FILE__, __func__, __LINE__, __VA_ARGS__), (void)0)
#else
#define TRACE(msg, ...) ((void)0)
#endif
#if !defined(COW_NO_ASSERT)
#define ASSERT(expr, msg) do { if(!(expr)) die("assertion failed: `" #expr "`: " msg); } while(0)
#else
#define ASSERT(op, msg) ((void)0)
#endif
#define LASSERT(expr, msg) ASSERT(LIKELY(expr), "(unexpected) " msg)
#define UASSERT(expr, msg) ASSERT(UNLIKELY(expr), "(expected) " msg)

@ -216,5 +216,10 @@ int main()
printf("First byte of: fake = %x\n", clone[0]); printf("First byte of: fake = %x\n", clone[0]);
read_fake(clone); //clone still functions because of refcount on origin. read_fake(clone); //clone still functions because of refcount on origin.
printf("Last error: %d, %s\n", cow_err(), *cow_err_msg(cow_err()));
Cow should_fail(SIZE_MAX);
printf("Last error: %d, %s\n", cow_err(), *cow_err_msg(cow_err()));
Cow::Fake should_fail_clone = should_fail;
printf("Last error: %d, %s\n", cow_err(), *cow_err_msg(cow_err()));
return 0; return 0;
} }

Loading…
Cancel
Save