Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/libuv'
Browse files Browse the repository at this point in the history
  • Loading branch information
TimWhiting committed Oct 11, 2024
2 parents cbfc83a + e65a43f commit 5cadee3
Show file tree
Hide file tree
Showing 61 changed files with 4,965 additions and 43 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ bench
test/bench
.koka
scratch
.cache
17 changes: 17 additions & 0 deletions doc/dev-learnings.kk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# What Tim Learned Designing LibUV

- Autogenerated bindings are a good idea (they are less work to maintain, more consistent and less error prone)
- However, they lack the flexibility of hand-written bindings (including some marginal efficiency gain in rare cases)
- In particular for synchronous APIs (like the filesystem API when you do not provide a callback),
we can use stack allocated `uv_fs_req` objects.
It would be require more work on the compiler to do the same and make the guarantees we need to not escape the stack.
- Additionally autogenerated bindings typically end up with a large file, which often takes a while to compile. Not sure if that is partially due to the C compiler instead of the Koka compiler.
- Most apis with callbacks require us to define a
callback function in C for the binding anyways.
To do the same from Koka we would need a specialized type signaling to the compiler to emit a c callback wrapper function without a `kk_context_t` parameter for us. (Getting the `kk_context` instead from the thread local variable). I believe these sorts of functions are called trampolines. The trampoline also needs to know how to get data from it's environment (like the `uv_fs_req` object) to call the appropriate Koka closure, and we need to set it beforehand.
- Handle types differ from request types in that they are long lived, and their callbacks can be called multiple times. Currently this is awkward in some cases when managing memory (from Koka). The handwritten bindings have less of a problem here.
- We should really merge something like (https://github.com/koka-lang/koka/pull/494) for strongly typing external objects, and allowing those external objects to be reference counted and managed by Koka.
- When managing stream like objects that also require state or buffering, a named handler works rather well.
- We really could use conditional imports for different platforms. Especially needed if we want to support autogenerated bindings, without having to create stubs in all of the externs for wasm for example.
- Most bindings don't require many complicated types or conversions, and so we can get away with simple extern definitions. Most types can be interacted with solely via abstraction (opaque pointers), and
the interface in (https://github.com/koka-lang/koka/pull/494) is needed for that. More to the point however, we need a really robust and performant bytes and string buffer library. I wouldn't complain about string interpolation as well.
1 change: 1 addition & 0 deletions kklib/include/kklib.h
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ typedef struct kk_context_s {
kk_yield_t yield; // inlined yield structure (for efficiency)
int32_t marker_unique; // unique marker generation
kk_block_t* delayed_free; // list of blocks that still need to be freed
void* loop; // a reference to an event loop (e.g. uv_loop_t* or NULL)
kk_integer_t unique; // thread local unique number generation
size_t thread_id; // unique thread id
kk_box_any_t kk_box_any; // used when yielding as a value of any type
Expand Down
9 changes: 9 additions & 0 deletions kklib/include/kklib/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ static inline kk_bytes_t kk_bytes_dup(kk_bytes_t b, kk_context_t* ctx) {
// Adds a terminating zero at the end. Return the raw buffer pointer in `buf` if non-NULL
kk_decl_export kk_bytes_t kk_bytes_alloc_len(kk_ssize_t len, kk_ssize_t plen, const uint8_t* p, uint8_t** buf, kk_context_t* ctx);
kk_decl_export kk_bytes_t kk_bytes_adjust_length(kk_bytes_t p, kk_ssize_t newlen, kk_context_t* ctx);
kk_decl_export kk_bytes_t kk_bytes_advance(kk_bytes_t p, kk_ssize_t count, kk_context_t* ctx);

// allocate uninitialized bytes
static inline kk_bytes_t kk_bytes_alloc_buf(kk_ssize_t len, uint8_t** buf, kk_context_t* ctx) {
Expand Down Expand Up @@ -157,7 +158,15 @@ static inline const char* kk_bytes_cbuf_borrow(const kk_bytes_t b, kk_ssize_t* l
return (const char*)kk_bytes_buf_borrow(b, len, ctx);
}

static inline int8_t kk_bytes_at(kk_bytes_t p, uint64_t i, kk_context_t* ctx){
const uint8_t* buf = kk_bytes_buf_borrow(p, NULL, ctx);
return (int8_t)buf[i];
}

static inline void kk_bytes_set(kk_bytes_t p, uint64_t i, int8_t b, kk_context_t* ctx){
uint8_t* buf = (uint8_t*)kk_bytes_buf_borrow(p, NULL, ctx);
buf[i] = (uint8_t)b;
}

/*--------------------------------------------------------------------------------------------------
Length, compare
Expand Down
15 changes: 15 additions & 0 deletions kklib/src/bytes.c
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,21 @@ kk_bytes_t kk_bytes_adjust_length(kk_bytes_t b, kk_ssize_t newlen, kk_context_t*
}
}

kk_bytes_t kk_bytes_advance(kk_bytes_t b, kk_ssize_t count, kk_context_t* ctx) {
kk_ssize_t len;
const uint8_t* s = kk_bytes_buf_borrow(b,&len,ctx);
if (len == count) {
kk_bytes_drop(b, ctx);
return kk_bytes_empty();
} else {
// copy the rest
kk_ssize_t newlen = len - count;
kk_bytes_t tb = kk_bytes_alloc_dupn(newlen, s + count, ctx);
kk_bytes_drop(b, ctx);
return tb;
}
}


/*--------------------------------------------------------------------------------------------------
Compare
Expand Down
2 changes: 1 addition & 1 deletion kklib/src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void kk_free_fun(void* p, kk_block_t* b, kk_context_t* ctx) {

kk_string_t kk_get_host(kk_context_t* ctx) {
kk_unused(ctx);
kk_define_string_literal(static, host, 5, "libc", ctx);
kk_define_string_literal(static, host, 4, "libc", ctx);
return kk_string_dup(host,ctx);
}

Expand Down
3 changes: 3 additions & 0 deletions kklib/src/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -865,20 +865,23 @@ kk_string_t kk_string_trim_right(kk_string_t str, kk_context_t* ctx) {
kk_unit_t kk_println(kk_string_t s, kk_context_t* ctx) {
// TODO: set locale to utf-8?
puts(kk_string_cbuf_borrow(s, NULL, ctx)); // todo: allow printing embedded 0 characters?
fflush(stdout);
kk_string_drop(s, ctx);
return kk_Unit;
}

kk_unit_t kk_print(kk_string_t s, kk_context_t* ctx) {
// TODO: set locale to utf-8?
fputs(kk_string_cbuf_borrow(s, NULL, ctx), stdout); // todo: allow printing embedded 0 characters?
fflush(stdout);
kk_string_drop(s, ctx);
return kk_Unit;
}

kk_unit_t kk_trace(kk_string_t s, kk_context_t* ctx) {
fputs(kk_string_cbuf_borrow(s, NULL, ctx), stderr); // todo: allow printing embedded 0 characters?
fputs("\n", stderr);
fflush(stdout);
kk_string_drop(s, ctx);
return kk_Unit;
}
Expand Down
Loading

0 comments on commit 5cadee3

Please sign in to comment.