Repository navigation
Kiln 1.1.0
Memory comes back while a program runs
Every allocation used to live until the process exited. A loop building one
short text per iteration held every text it had ever built: two million of them
came to 220MB, and a server ticking at 20Hz reached that in minutes. There was
no way to write a long-running program in Kiln that did not eventually die.
There is now a collector — conservative mark-and-sweep, tracing the machine
stack, the registers and your module variables. Nothing in the language
changed: you still never free anything, two names for one record are still the
same fields, and no annotation appeared anywhere.
- 2,000,000 short texts: 220MB → 8MB, and 0.72s → 0.58s. Freed memory
is reused memory, and reused memory is in cache. - Ten times the iterations: the same 8MB.
- A 20,000-player world with a busy tick loop: 1.76GB → 18.7MB.
Two new commands, memory_in_use() and collect_garbage(), for programs that
know when they are idle. KILN_GC_TRACE=1 reports every collection,
KILN_GC_MIN_HEAP moves the trigger, and KILN_GC=0 restores the old
behaviour. See Memory.
The honest part: collection stops the program, and the pause grows with the
heap — a program holding tens of megabytes pauses for tens of milliseconds.
Fine for a server, visible in a game at 60Hz. Incremental collection is not
written yet.
use db — SQLite and MySQL, and no way to concatenate
A new bundled library. Four things a login path needs — a SELECT, an INSERT, an
UPDATE, a DELETE — with one rule: every value is a bound parameter. There is
no db_exec(h, sql) without a parameter list, so the shortest call is the safe
one and a library whose easy path teaches injection cannot exist here.
let h: int = db_open("sqlite:accounts.db")
call db_exec(h, "insert into people values (?, ?)", ["Ada", "36"])
let rows: int = db_query(h, "select name, age from people where name = ?", ["Ada"])
while db_next(rows)
call print_text("{db_text(rows, 1)} is {db_int(rows, 2)}")
end
sqlite: opens a file or :memory:; mysql://user:pass@host/db opens MySQL or
MariaDB. The _n forms bind SQL NULL, which text[] cannot say. Without the
client libraries installed the library still builds and every command answers
its sentinel with a message naming what to install.
Binary protocols over TCP
tcpserver and tcpclient gained receive_bytes and send_bytes. Delivery
used to go through a C string, so a frame truncated at its first NUL — which
made every binary protocol unwritable. A byte-set carries every byte.
Alongside it, bytes and ptr now bridge both ways: bytes_from_ptr,
bytes_copy_to_ptr and bytes_concat, so a buffer from a C library becomes a
byte-set and back without a copy you have to reason about.
kiln install
kiln install copies the toolchain to /usr/local (or --user, or
--prefix), symlinks kiln and kiln-studio onto PATH, and leaves a tree
that still finds its own runtime. --dry-run writes nothing and says what it
would do; replacing an existing install needs --force.
Kate, KWrite and KDevelop
A KSyntaxHighlighting definition ships in editors/kate/, and
kiln install --editors puts it where Kate looks. It also gives a .kiln
document a highlighting mode named "Kiln", which is what Kate's LSP client keys
its server entry on — without it the language server never starts.
Also
- 357 commands, up from 337.
- The website stopped shipping OpenEPL: the landing page's screenshots were
still the pre-rename Studio, and the handbook's favicon was still the old
logo.tools/shots.shnow keeps all three copies in step. - A rebuilt landing page — dark, and it shows the source and the window it
builds side by side instead of describing the pairing. - Debug information is emitted as
llvm.dbg.declarebelow LLVM 19, so the
toolchain builds on Ubuntu 24.04's clang 18.