Skip to content

Instantly share code, notes, and snippets.

@pervognsen
Created February 26, 2019 08:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pervognsen/2ba7f4acb0e7ab69f673dee5eccde8af to your computer and use it in GitHub Desktop.
Save pervognsen/2ba7f4acb0e7ab69f673dee5eccde8af to your computer and use it in GitHub Desktop.
struct File {
base: Disposable;
libc_file: libc.FILE*;
}
func file_dispose(data: void*) {
self: File* = data;
if (self.libc_file) {
libc.fclose(self.libc_file);
}
libc.free(self);
}
func open(path: char const*): File* {
libc_file := libc.fopen(path, "r");
if (!libc_file) {
return 0;
}
return new File{make_disposable(file_dispose), libc_file};
}
typedef DisposeFunc = func(void*);
typedef DisposeMark = usize;
struct Disposable {
dispose: DisposeFunc;
mark: DisposeMark;
}
@threadlocal
var disposables: Disposable*[];
func make_disposable(dispose: DisposeFunc): Disposable {
return {dispose, alen(disposables)};
}
func secured(data: void*): bool {
if (!data) {
return false;
}
self: Disposable* = data;
mark := self.mark;
return mark < alen(disposables) && disposables[mark] == self;
}
func secure(data: void*): void* {
if (!secured(data)) {
self: Disposable* = data;
self.mark = apush(disposables, self);
}
return data;
}
func unsecure(data: void*): void* {
if (data && secured(data)) {
self: Disposable* = data;
disposables[self.mark] = 0;
self.mark = -1;
}
return data;
}
func dispose(data: void*) {
if (!data) {
return;
}
self: Disposable* = data;
mark := self.mark;
for (i := alen(disposables); i > mark; i--) {
disposable := disposables[i-1];
if (disposable) {
disposable.dispose(disposable);
disposables[i-1] = 0;
}
}
asetlen(disposables, mark);
}
struct Recover {
base: Disposable;
libc_env: libc.jmp_buf;
}
func recover_dispose(data: void*) {
dispose(data);
}
@threadlocal
var temp_ctx: Recover*;
@foreign
func recover(ctx: Recover*): bool {
make_disposable;
recover_dispose;
temp_ctx;
libc.setjmp;
#foreign(preamble = "#define recover(ctx) (std_temp_ctx = (ctx), std_temp_ctx->base = std_make_disposable(std_recover_dispose), setjmp(std_temp_ctx->libc_env) == 0)");
return true;
}
func panic(ctx: Recover*) {
dispose(ctx);
libc.longjmp(ctx.libc_env, 1);
}
// ...
func test_panic(ctx: Recover*, i: int) {
if (i == 0) {
panic(ctx);
} else {
test_panic(ctx, i-1);
}
}
// imagine you're writing top-level parse() entry point to the parser module
func test_disposable(): File* {
ctx: Recover;
if (recover(&ctx)) {
file1 := open("c:/projects/sandbox/dummy.txt");
secure(file1);
file2 := open("c:/projects/sandbox/dummy.txt");
secure(file2);
test_panic(&ctx, 10);
#assert(0);
// panic(&ctx);
// unsecure(file1);
dispose(&ctx);
return file1;
}
#assert(alen(disposables) == 0);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment