Skip to content

Commit

Permalink
[mycpp] Slightly different API for return value rooting
Browse files Browse the repository at this point in the history
It looks like this:

    RootsFrame _r{FUNC_NAME};
    RootsFrame _for{LOOP};

    NO_ROOTS_FRAME(FUNC_NAME);
    NO_ROOTS_FRAME(LOOP);

This seems inconsistent, but I want to emphasize that the first case
does work in non-coverage mode, and the second one doesn't.

In coverage mode, they both do work.
  • Loading branch information
Andy C committed Nov 13, 2022
1 parent cd06476 commit 2dba7b1
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 36 deletions.
28 changes: 16 additions & 12 deletions cpp/leaky_core.cc
Expand Up @@ -26,7 +26,7 @@ Tuple2<int, int> WaitPid() {
}

Tuple2<int, int> Read(int fd, int n, List<Str*>* chunks) {
RootingScope _r;
RootsFrame _r{FUNC_NAME};
Str* s = OverAllocatedStr(n); // Allocate enough for the result

int length = ::read(fd, s->data(), n);
Expand Down Expand Up @@ -62,11 +62,11 @@ Str* ReadLine() {
}

Dict<Str*, Str*>* Environ() {
RootingScope _r;
RootsFrame _r{FUNC_NAME};
auto d = NewDict<Str*, Str*>();

for (char** env = environ; *env; ++env) {
RootingScope _for;
RootsFrame _for{LOOP};
char* pair = *env;

char* eq = strchr(pair, '=');
Expand All @@ -88,7 +88,8 @@ Dict<Str*, Str*>* Environ() {
}

int Chdir(Str* dest_dir) {
// RootingScope NO FRAME - only deals with integers
NO_ROOTS_FRAME(FUNC_NAME);

if (chdir(dest_dir->data_) == 0) {
return 0; // success
} else {
Expand All @@ -97,7 +98,8 @@ int Chdir(Str* dest_dir) {
}

Str* GetMyHomeDir() {
RootingScope _r;
RootsFrame _r{FUNC_NAME};

uid_t uid = getuid(); // always succeeds

// Don't free this. (May return a pointer to a static area)
Expand All @@ -111,7 +113,7 @@ Str* GetMyHomeDir() {
}

Str* GetHomeDir(Str* user_name) {
RootingScope _r;
RootsFrame _r{FUNC_NAME};
// Don't free this. (May return a pointer to a static area)
struct passwd* entry = getpwnam(user_name->data_);
if (entry == nullptr) {
Expand All @@ -123,7 +125,7 @@ Str* GetHomeDir(Str* user_name) {
}

Str* GetUserName(int uid) {
RootingScope _r;
RootsFrame _r{FUNC_NAME};
Str* result = kEmptyString;

if (passwd* pw = getpwuid(uid)) {
Expand All @@ -137,7 +139,7 @@ Str* GetUserName(int uid) {
}

Str* OsType() {
RootingScope _r;
RootsFrame _r{FUNC_NAME};
Str* result = kEmptyString;

utsname un = {};
Expand Down Expand Up @@ -268,7 +270,8 @@ bool IsValidCharEscape(int c) {
}

Str* ChArrayToString(List<int>* ch_array) {
RootingScope _r;
RootsFrame _r{FUNC_NAME};

int n = len(ch_array);
Str* result = NewStr(n);
for (int i = 0; i < n; ++i) {
Expand Down Expand Up @@ -306,7 +309,8 @@ Str* ShowAppVersion(Str* app_name, _ResourceLoader* loader) {
}

Str* BackslashEscape(Str* s, Str* meta_chars) {
RootingScope _r;
RootsFrame _r{FUNC_NAME};

int upper_bound = len(s) * 2;
Str* buf = OverAllocatedStr(upper_bound);
char* p = buf->data_;
Expand All @@ -324,9 +328,9 @@ Str* BackslashEscape(Str* s, Str* meta_chars) {
}

Str* strerror(IOError_OSError* e) {
RootingScope _r;
NO_ROOTS_FRAME(FUNC_NAME);

Str* s = StrFromC(::strerror(e->errno_));
gHeap.RootOnReturn(s);
return s;
}

Expand Down
4 changes: 2 additions & 2 deletions cpp/qsn.h
Expand Up @@ -31,7 +31,7 @@ inline bool IsPlainChar(Str* ch) {
}

inline Str* XEscape(Str* ch) {
RootingScope _r;
RootsFrame _r{FUNC_NAME};
assert(len(ch) == 1);
StackRoots _roots({&ch});
Str* result = NewStr(4);
Expand All @@ -45,7 +45,7 @@ inline Str* UEscape(int codepoint) {
// 3 for \u{
// 6 for codepoint
// 1 for }
RootingScope _r;
RootsFrame _r{FUNC_NAME};
Str* result = OverAllocatedStr(10);
int n = sprintf(result->data(), "\\u{%x}", codepoint);
result->SetObjLenFromStrLen(n); // truncate to what we wrote
Expand Down
45 changes: 39 additions & 6 deletions mycpp/gc_heap.h
Expand Up @@ -12,28 +12,61 @@
#define PRINT_GC_MODE_STRING() printf(" -- GC_MODE :: cheney\n")
#endif

class RootingScope {
class RootsFrame {
// Create an instance of this in every function. This lets the GC now when
// functions return, so it can remove values from the root set.
//
// Example:
//
// RootsFrame _r{FUNC_NAME};
// RootsFrame _r{LOOP};
//
// You must use braced initialization!
//
// RootsFrame _r(FUNC_NAME); // WRONG because it sometimes expands to:
// RootsFrame _r(); // MOST VEXING PARSE: a function prototype,
// // not a variable

public:
RootingScope() {
#if !defined(BUMP_LEAK)
#ifdef COLLECT_COVERAGE
explicit RootsFrame(const char* description) {
log(">>> %s", description);
#ifndef BUMP_LEAK
gHeap.root_set_.PushScope();
#endif
}
#endif

RootsFrame() {
#ifndef BUMP_LEAK
gHeap.root_set_.PushScope();
#endif
}
~RootingScope() {
#if !defined(BUMP_LEAK)
~RootsFrame() {
#ifndef BUMP_LEAK
gHeap.root_set_.PopScope();
#endif
}
};

// Explicit annotation for "skipped frame" optimization, and the like

#define NO_ROOTS_FRAME(description)

#ifdef COLLECT_COVERAGE
#define FUNC_NAME __PRETTY_FUNCTION__
// TODO: create a different string for loops
#define LOOP __PRETTY_FUNCTION__
#else
#define FUNC_NAME
#define LOOP
#endif

// Variadic templates:
// https://eli.thegreenplace.net/2014/variadic-templates-in-c/
template <typename T, typename... Args>
T* Alloc(Args&&... args) {
// RootingScope omitted for PASS THROUGH
NO_ROOTS_FRAME(FUNC_NAME);

assert(gHeap.is_initialized_);
void* place = gHeap.Allocate(sizeof(T));
Expand Down
9 changes: 6 additions & 3 deletions mycpp/gc_str.h
Expand Up @@ -101,7 +101,8 @@ inline int len(const Str* s) {
//

inline Str* NewStr(int len) {
// RootingScope omitted for PASS THROUGH
NO_ROOTS_FRAME(FUNC_NAME);

int obj_len = kStrHeaderSize + len + 1;

// only allocation is unconditionally returned
Expand All @@ -122,7 +123,8 @@ inline Str* OverAllocatedStr(int len) {
}

inline Str* StrFromC(const char* data, int len) {
// RootingScope omitted for PASS THROUGH
NO_ROOTS_FRAME(FUNC_NAME);

Str* s = NewStr(len);
memcpy(s->data_, data, len);
assert(s->data_[len] == '\0'); // should be true because Heap was zeroed
Expand All @@ -131,7 +133,8 @@ inline Str* StrFromC(const char* data, int len) {
}

inline Str* StrFromC(const char* data) {
// RootingScope omitted for PASS THROUGH
NO_ROOTS_FRAME(FUNC_NAME);

return StrFromC(data, strlen(data));
}

Expand Down
5 changes: 3 additions & 2 deletions mycpp/leaky_containers.cc
Expand Up @@ -102,7 +102,7 @@ Str* Str::index_(int i) {

// s[begin:end]
Str* Str::slice(int begin, int end) {
RootingScope _r;
RootsFrame _r{FUNC_NAME};

int len_ = len(this);
begin = std::min(begin, len_);
Expand Down Expand Up @@ -151,7 +151,8 @@ Str* Str::slice(int begin, int end) {

// s[begin:]
Str* Str::slice(int begin) {
// RootingScope omitted because PASS THROUGH
NO_ROOTS_FRAME(FUNC_NAME);

// log("slice(begin) -> %d frames", gHeap.root_set_.NumFrames());

int len_ = len(this);
Expand Down
2 changes: 1 addition & 1 deletion mycpp/marksweep_heap.cc
Expand Up @@ -90,7 +90,7 @@ void* MarkSweepHeap::Allocate(size_t num_bytes) {
bytes_allocated_ += num_bytes;

// Allocate() is special: we use RootInCurrentFrame because it's a LEAF, and
// this function doesn't have RootingScope to do PushScope/PopScope
// this function doesn't have RootsFrame to do PushScope/PopScope
#if RET_VAL_ROOTING
gHeap.RootInCurrentFrame(static_cast<Obj*>(result));
static_cast<Obj*>(result)->heap_tag_ = Tag::Opaque; // it is opaque to start!
Expand Down
2 changes: 1 addition & 1 deletion mycpp/marksweep_heap.h
Expand Up @@ -64,7 +64,7 @@ class RootSet {
}

// Called in 2 situations:
// - the "leaf" Allocate(), which does not have a RootingScope
// - the "leaf" Allocate(), which does not have a RootsFrame
// - when catching exceptions:
// catch (IOError e) { gHeap.RootInCurrentFrame(e); }
void RootInCurrentFrame(Obj* root) {
Expand Down
18 changes: 9 additions & 9 deletions mycpp/marksweep_heap_test.cc
Expand Up @@ -321,14 +321,14 @@ TEST root_set_big_test() {
}

int f() {
RootingScope r2;
RootsFrame r2;

// Can't assert in this non-test function
return gHeap.root_set_.NumFrames();
}

Str *g(Str *left, Str *right) {
RootingScope _r;
RootsFrame _r;

// TODO: call str_concat, NewList, etc.
Str *ret = left;
Expand All @@ -349,13 +349,13 @@ int count_old(Str *a, Str *b) {
return result;
}

// Like the above, but instead of rooting variables, we create a RootingScope
// Like the above, but instead of rooting variables, we create a RootsFrame
// instance. It doesn't return a heap-allocated object, so we don't need
// gHeap.RootOnReturn(). Functions that allocate like Alloc<T> are responsible
// for that.

int count_new(Str *a, Str *b) {
RootingScope _r;
RootsFrame _r;

int result = 0;
if (a) {
Expand Down Expand Up @@ -392,7 +392,7 @@ TEST old_slice_demo() {
}

TEST new_slice_demo() {
RootingScope _r;
RootsFrame _r;

Str *s = StrFromC("spam");
log("s %p heap_tag_ %d", s, s->heap_tag_);
Expand All @@ -419,13 +419,13 @@ TEST new_slice_demo() {
}

TEST root_set_stress_test() {
RootingScope _r;
RootsFrame _r;

for (int i = 0; i < 10; ++i) {
// NewStr needs to root; also needs RootingScope
// NewStr needs to root; also needs RootsFrame
Str *s = StrFromC("abcdef");

// slice() needs to root; also eneds RootingScope
// slice() needs to root; also eneds RootsFrame
Str *t = g(s->slice(1), s->slice(2));

log("t = %s", t->data());
Expand All @@ -437,7 +437,7 @@ TEST root_set_stress_test() {
TEST rooting_scope_test() {
ASSERT_EQ_FMT(1, gHeap.root_set_.NumFrames(), "%d");

RootingScope r1;
RootsFrame r1;
ASSERT_EQ_FMT(2, gHeap.root_set_.NumFrames(), "%d");

int f_num_frames = f();
Expand Down

0 comments on commit 2dba7b1

Please sign in to comment.