Releases: ponylang/ponyc
0.59.0
Add ability to trace runtime events in chromium json format #4649
We have added the ability to trace runtime events for pony applications. Runtime tracing is helpful in understanding how the runtime works and for debugging issues with the runtime.
The tracing can either write to file in the background or work in a "flight recorder" mode where events are stored into in memory circular buffers and written to stderr in case of abnormal program behavior (SIGILL, SIGSEGV, SIGBUS, etc).
Traces are written in chromium json format and trace files can be viewed with the perfetto trace viewer.
Links:
Chromium json format
perfetto trace viewer
Prevent memory explosion when using ponynoblock in some programs
We have enhanced the runtime to prevent memory explosion in some programs when --ponynnoblock
is used.
The following is an example of such a program:
use "collections"
use "random"
use "time"
class Notify is TimerNotify
let _a: A
var _m: Main
new iso create(a: A, m: Main) =>
_a = a
_m = m
fun ref apply(timer: Timer, count: U64): Bool =>
_a.done(_m)
// returning false from apply in a TimerNotify
// instructs the runtime that the timer should be
// cancelled which allows for the owning Timers
// actor to potentially be reaped if there are no
// references to it and/or if the cycle detector
// decides it is safe to be reaped
false
actor A
// acquires a reference to the Main actor via an ORCA
// message to prevent the Main actor from being reaped
// until after this actor is done with the reference to
// it. This reference will be released the next time GC
// runs for this actor since this actor doesn't keep a
// reference to it
new create(m: Main, n: USize) =>
// acquires a reference to the Timers actor implicitly
// because this actor creates it. The reference will
// be freed after the next GC of this actor since this
// actor doesn't keep a reference to it
let timers = Timers
// passing a reference to the Main and A actors via the
// Notify class delays/prevents them from being reaped
// until after the references are no longer used either
// because the Notify object is destroyed or the Timers
// actor is reaped
let timer = Timer(Notify(this, m), n.u64() * 1_000_000)
timers(consume timer)
// acquires a reference to the Main actor via an ORCA
// message (if the previous reference has already been
// released) to prevent the Main actor from being reaped
// until after this actor is done with the reference to
// it. This reference will be released the next time GC
// runs for this actor since this actor doesn't keep a
// reference to it
// After this behavior completes, this actor can
// potentially be reaped if there are no references to it
// and/or if the cycle detector decides it is safe to be
// reaped
be done(m: Main) =>
m.done(this)
actor Main
let n: USize = 100000
var e: USize = 0
let max_num_oustanding: USize = 13
var num_outstanding: USize = 0
let s: SetIs[A] = s.create()
let rand: Random
new create(env: Env) =>
let t = Time.seconds()
rand = Rand(t.u64())
spawn()
be spawn() =>
if e < n then
while num_outstanding < max_num_oustanding do
let i = rand.int[USize](2)
// passing a reference to the Main actor
// delays/prevents it from being reaped
// until after the reference is no longer
// used
let a = A(this, i)
// saving references to A actors in the set
// delays/prevents them from being reaped
// until after the reference has been deleted
s.set(a)
e = e + 1
num_outstanding = num_outstanding + 1
end
end
be done(a: A) =>
// deletes a reference to an A actor but the actor
// can only be reaped after GC is run for the Main
// actor as that is when the ORCA message releasing
// the reference is sent
s.unset(a)
num_outstanding = num_outstanding - 1
spawn()
before:
root@5babe01f566f:/workspaces/ponyc# /usr/bin/time ./before --ponynoblock
35.91user 7.08system 0:05.91elapsed 727%CPU (0avgtext+0avgdata 1716480maxresident)k
0inputs+0outputs (3major+428831minor)pagefaults 0swaps
after:
root@5babe01f566f:/workspaces/ponyc# /usr/bin/time ./after --ponynoblock
41.16user 2.73system 0:05.89elapsed 744%CPU (0avgtext+0avgdata 13440maxresident)k
0inputs+0outputs (3major+3137minor)pagefaults 0swaps
Make GC for an actor more aggressive if an actor GC frees many actor references
We have enhanced the runtime to make actor GC more aggressive if an actor GC cycle deleted many references to other actors (> 100). This could be due to a program design where an actor creates many other actors and keeps references to them for a bit before replacing those references with ones for other newly created actors. This change will make GC for these actors more aggressive in order to try and limit memory explosion that could potentially occur otherwise.
Update to LLVM 17.0.1
We've updated the LLVM version used to build Pony to 17.0.1.
[0.59.0] - 2025-04-26
Fixed
- Prevent memory explosion when using ponynoblock in some programs (PR #4666)
- Make GC for an actor more aggressive if an actor GC frees many refs (PR #4662)
Added
- Add ability to trace runtime events in chromium json format (PR #4649)
Changed
0.58.13
Make sure systematic testing doesn't switch to suspended thread
Previously, the systematic testing next thread logic would consider switching to the pinned actor thread even if it was suspended. This was very wasteful since the suspended pinned actor thread would not do any meaningful work and almost immediately re-suspend itself.
We have changed things so that the next thread logic properly takes into account whether the pinned actor thread is suspended or not when selecting the next thread to activate.
Fix race condition in epoll ASIO system
We've fixed a race condition in the epoll ASIO subsystem that could result in "unexpected behavior" when using one-shot epoll events. At the time the bug was fixed, this means that only the TCPConnection
actor was impacted part of the standard library.
[0.58.13] - 2025-03-09
Fixed
0.58.12
Don't duplicate match checks for inherited trait bodies
Due to how we were doing match checks, some usages of match in default method bodies of traits and interfaces could end up failing checking once they were "inherited" by the implementing class.
For example, the following failed to compile:
primitive Prim
fun ignore() => None
trait A
fun union(): (Prim | None)
fun match_it(): Bool =>
match union()
| let p: Prim =>
p.ignore()
true
| None =>
false
end
class B is A
fun union(): Prim =>
Prim
We've updated to only do the match checks checking the trait's default method bodies.
Don't duplicate visibility tests for inherited trait bodies
Due to how we were implementing name lookups, some usages of calling private methods on a trait from within the body of a default method on the same trait would fail if a class from outside the package tried to implement the trait.
For example, if you defined the following trait:
trait T
fun _override(): (T | None)
fun _with_default() =>
"""
Will compile if #4613 remains fixed. Otherwise, this will fail to
compiled IFF a class from outside this package implements this trait
and inherits this default method
"""
match _override()
| let t: T =>
t._with_default()
| None =>
None
end
And then in another package tried to create a class that implemented the trait and inherited the method body for _with_default
, you would get a compilation error stating that you can't lookup the private method _with_default
from outside the package. This error happened because the call to t._with_default()
in the trait was "losing the context of the call". This simple class was all that was needed to trigger the bug:
class C is T
fun _override(): (T | None) =>
None
We've updated the compiler to only check name visibility at the time the trait is not type checked, not each time a class that has inherited a default method body from a trait is checked.
[0.58.12] - 2025-03-01
Fixed
0.58.11
Fix use-after-free triggered by fast actor reaping when the cycle detector is active
The logic in the actor run and the cycle detector work together to enable fast reaping of actors with rc == 0. This relies on atomics and protecting the relevant areas of logic with critical sections. This logic unfortunately suffered from a use-after-free bug due to a race between the cycle detector receiving the block message and destroying the actor and the actor cycle detector critical flag being release as identified in #4614 which could sometimes lead to memory corruption.
This commit changes things to remove the need to protect the logic with critical sections. It achieves this by ensuring that an actor with rc == 0 that the cycle detector knows about will never be rescheduled again even if the cycle detector happens to send it a message and the cycle detector is free to reap the actor when it receives the block message. The cycle detector ensures that the actor's message queue is empty or that the only messages pending are the expected ones from the cycle detector so it can safely destroy the actor.
Fix incorrect printing of runtime stats
When the runtime was compiled with the runtime stats option on, some stats were being printed to standard out without them having been requested. We've fixed the issue so stats will only be printed if the option is turned on by the user.
Fix memory leak
In the process of fixing a "in theory could be use after free" that tooling found, we created a real memory leak. We've reverted back to the "in theory bad, in practice not" code from before and fixed the leak.
Fix being unable to compile programs that use runtime info on Windows
We accidentally weren't properly exporting the Pony runtime methods needed by the "runtime_info" package so that they could be linked on Windows.
[0.58.11] - 2025-02-22
Fixed
0.58.10
Change stack_depth_t to size_t on OpenBSD
The definition of stack_depth_t was changed from int to size_t to support compiling on OpenBSD 7.6.
ponyc may not be compilable on earlier versions of OpenBSD.
Make sure scheduler threads don't ACK the quiescence protocol CNF messages if they have an actor waiting to be run
Prior to this, the pinned actor thread could cause early termination/quiescence of a pony program if there were only pinned actors active. This change fixes the issue to ensure that pony programs with only pinned actors active will no longer terminate too early.
Apply default options for a CLI parent command when a sub command is parsed
In the CLI package's parser, a default option for a parent command was ignored when a subcommand was present. This fix makes sure that parents' defaults are applied before handling the sub command.
Fix compiler crash from match
with extra parens around let
in tuple
When matching on a tuple element within a union type, the compiler would crash when extra parentheses were present.
The following code fails to compile because of (123, (let x: I32))
in the match
. The extra parentheses should be ignored and treated like (123, let x: I32)
.
let value: (I32 | (I32, I32)) = (123, 42)
match value
| (123, (let x: I32)) => x
end
Fix soundness problem when matching iso
variables
We previously switched our underlying type system model. In the process, a
soundness hole was introduced. The following code that should not compile was accepted by the compiler:
class Bad
let _s: String iso
new iso create(s: String iso) =>
_s = consume s
fun ref take(): String iso^ =>
match _s
| let s': String iso => consume s'
end
The code should not compile because let s': String iso
is aliasing _s
an iso field. Consuming an aliased iso shouldn't return an iso^.
The take-away is that "very bad things could happen" and the data race freedom guarantees of the Pony compiler were being violated.
We've closed the soundness hole. We recommend all Pony users update to the this release as soon as possible.
[0.58.10] - 2025-02-01
Fixed
- Make sure scheduler threads don't ACK the quiescence protocol CNF messages if they have an actor waiting to be run (PR #4583)
- Apply default options for a CLI parent command when a sub command is parsed (PR #4593)
- Fix compiler crash from
match
with extra parens aroundlet
in tuple (PR #4595) - Fix soundness problem when matching
iso
variables (PR #4588)
Changed
- Change stack_depth_t to size_t on OpenBSD (PR #4575)
0.58.9
Fixed an issue that caused the actor_pinning
documentation to not be built
We fixed an issue that caused the actor_pinning
documentation to not be built. This issue has been resolved and the documentation is now available.
[0.58.9] - 2024-12-29
Fixed
- Fixed an issue that caused the
actor_pinning
documentation to not be built
0.58.8
Add Fedora 41 as a supported platform
We've added Fedora 41 as a supported platform. We'll be building ponyc releases for it until it stops receiving security updates in November 2025. At that point, we'll stop building releases for it.
Drop Fedora 39 support
Fedora 39 has reached its end of life date. We've dropped it as a supported platform. That means, we no longer create prebuilt binaries for installation via ponyup
for Fedora 39.
We will maintain best effort to keep Fedora 39 continuing to work for anyone who wants to use it and builds ponyc
from source.
Update Pony musl Docker images to Alpine 3.20
We've updated our ponylang/ponyc:latest-alpine
, ponylang/ponyc:release-alpine
, and ponylang/ponyc:x.y.z-alpine
images to be based on Alpine 3.20. Previously, we were using Alpine 3.18 as the base.
Fix rare termination logic failures that could result in early shutdown
There was a very rare edge case in the termination logic that could result in early shutdown resulting in a segfault.
The edge cases have been addressed and the shutdown/termination logic has been overhauled to make it simpler and more robust.
Add support for pinning actors to a dedicated scheduler thread
Pony programmers can now pin actors to a dedicated scheduler thread. This can be required/used for interfacing with C libraries that rely on thread local storage. A common example of this is graphics/windowing libraries.
The way it works is that an actor can request that it be pinned (which may or may not happen immediately) and then it must wait and check to confirm that the pinning was successfully applied (prior to running any workload that required the actor to be pinned) after which all subsequent behaviors on that actor will run on the same scheduler thread until the actor is destroyed or the actor requests to be unpinned.
Caveat
Due to the fact that Pony uses cooperative scheduling of actors and that all pinned actors run on a single shared scheduler thread, any "greedy" actors that monopolize the cpu (with long running behaviors) will negatively inmpact all other pinned actors by starving them of cpu.
Example program
// Here we have the Main actor that upon construction requests a PinUnpinActorAuth
// token from AmbientAuth and then requests that it be pinned. It then recursively
// calls the `check_pinned` behavior until the runtime reports that it has
// successfully been pinned after which it starts `do_stuff` to do whatever
// work it needs to do that requires it to be pinned. Once it has completed all
// of its work, it calls `done` to request that the runtime `unpin` it.
use "actor_pinning"
actor Main
let _env: Env
let _auth: PinUnpinActorAuth
new create(env: Env) =>
_env = env
_auth = PinUnpinActorAuth(env.root)
ActorPinning.request_pin(_auth)
check_pinned()
be check_pinned() =>
if ActorPinning.is_successfully_pinned(_auth) then
// do stuff that requires this actor to be pinned
do_stuff(10)
else
check_pinned()
end
be do_stuff(i: I32) =>
if i < 0 then
done()
else
do_stuff(i - 1)
end
be done() =>
ActorPinning.request_unpin(_auth)
[0.58.8] - 2024-12-27
Fixed
- Fix rare termination logic failures that could result in early shutdown (PR #4556)
Added
- Add Fedora 41 as a supported platform (PR #4557)
- Add support for pinning actors to a dedicated scheduler thread (PR #4547)
Changed
0.58.7
Make actor heap allocations more efficient by recycling freed memory
Prior to this change, the actor heap garbage collection process would return freed memory back to the pony runtime at the end of a garbage collection run. The returning of memory to the pony runtime and allocating of new memory from the pony runtime are both expensive operations. This change makes it so that the actor garbage collection process keeps old freed memory chunks around with the expectation that the actor will very likely need memory again as it continues to run behaviors. This avoids the expensive return to and reallocation of memory from the pony runtime. It is possible that the overall application might end up using more memory as any freed memory chunks can only be reused by the actor that owns them and the runtime and other actors can no longer reuse that memory as they previously might have been able to.
Correctly find custom-built llc
during build process
Previously our CMake build was failing to find our custom-built llc
binary, but builds worked by accident if there was a system llc
. The build system now finds our custom llc
correctly.
Fix minor buffer out of bounds access bug in compiler
Previously, the compiler error reporting had a minor buffer out of bounds access bug where it read one byte past the end of a buffer as part of an if condition before checking that the offset was smaller than the buffer length. This was fixed by switching the order of the if condition checks so that the check that the offset is smaller than the buffer length happens before reading the value from the buffer to prefer the out of bounds access issue.
Fix bug in ASIO shutdown
There was a bug during our shutdown process that could cause a segmentation fault coming from our asynchrnous I/O subsystem. This has been fixed.
Fix early quiescence/termination bug
Previously, the logic to detect quiescence had a race condition in relation to dynamic scheduler scaling and it was possible for the runtime to incorrectly detect quiescence and termninate early if a scheduler thread suspended at just the right time.
We have now changed the quiescence logic to keep an accurate track of exactly how many scheduler threads are active at the time the quiescence detection protocol begins so it can ensure that any scheduler threads suspending or unsuspending can no longer cause an incorrect determination that might lead to early termination of the runtime.
[0.58.7] - 2024-11-30
Fixed
- Correctly find custom-built
llc
(PR #4537) - Fix buffer out of bounds access issue (PR #4540)
- Fix bug in ASIO shutdown (PR #4548)
- Fix early quiescence/termination bug (PR #4550)
Changed
- Recycle actor heap chunks after GC instead of returning to pool (PR #4531)
0.58.6
Fix use after free bug in actor heap finalisation that can lead to a segfault
The 0.45.2 release introduced an improvement to handling of objects with finalisers to make them more efficient to allocate on actor heaps. However, in the process it also introduced a potential for use after free situations that could lead to segfaults when running finalisers. With this change, we've reworked the implementation of the actor heap garbage collection to avoid the potential for use after free situations entirely.
Fix actor heap chunk size tracking bug that could cause a segfault
The 0.55.1 release included some internal actor heap implementation optimizations. Unfortunately, there was a small bug that could potentially cause a segfault due to not properly clearing some bits before setting them for some heap chunks. This change corrects that oversight to ensure the relevant bits are properly cleared before being set to ensure they final result can never be incorrect.