Created
February 26, 2019 08:47
-
-
Save pervognsen/2ba7f4acb0e7ab69f673dee5eccde8af to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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