Crystal 1.20.0 is released!
Highlights
-
@[TargetFeature]annotation lang/annotations - Preview: New API for spawning processes and capturing output stdlib/system
-
Preview: Event loop implementation based on
io_uringstdlib/runtime - Kernel TLS stdlib/networking
-
Compiler defaults to
moldorlldif available compiler/codegen
We are announcing a new Crystal release 1.20.0 with several new features and bug fixes.
Pre-built packages are available on GitHub Releases and our official distribution channels. See crystal-lang.org/install for installation instructions.
Stats
Section titled StatsThis release includes 161 changes since 1.19.1 by 21 contributors. We thank all the contributors for all the effort put into improving the language! ❤️
Changes
Section titled ChangesBelow we list the most remarkable changes in the language, compiler and stdlib. For more details, visit the full changelog.
Breaking
Section titled BreakingWe do not expect any breaking changes in existing code. We expect the few bugfixes below to not negatively impact your programs. If you notice any unexpected issues, please let us know in the issue tracker or forum.
- OpenSSL sockets shouldn’t flush on read (#16650)
- Fix
Process#waitto not close@inputbefore waiting (#16620, #16638) - Ensure that heredoc lexing allows only valid identifiers (#16548)
Thanks @straight-shoota, @ysbaddaden, @Sija
Security
Section titled SecurityHTTP::Server accepted requests containing both Content-Length and Transfer-Encoding
headers and prioritized Content-Length. This allowed HTTP request smuggling as per
CWE-444 when the server is behind a vulnerable frontend. Refer to the
advisory
for more details.
HTTP::Server now rejects requests where both headers are present. The HTTP parser now
ignores Content-Length when the Transfer-Encoding header is present. (commit c948b31).
Target features
Section titled Target featuresRFC 0020 introduces a new @[TargetFeature] annotation that allows specifying
CPU features or a CPU model or variant for individual functions. It
complements the --mattr and --mcpu CLI arguments that target the whole
program. This allows embedding multiple optimized functions for different CPU
features into a single executable, for example a portable SIMD
implementation alongside AVX2 and AVX512 alternatives.
The program is responsible for detecting which feature is available and calling the proper function at runtime. Otherwise, the program might crash with SIGILL, for example.
For example:
{% if flag?(:x86_64) %}
@[TargetFeature("+avx2")]
private def foo_avx2
end
private def cpu_supports_avx2?
end
{% end %}
private def foo_portable
end
def foo
{% if flag?(:x86_64) %}
return foo_avx2 if cpu_supports_avx2?
{% end %}
foo_portable
end
While not strictly required, we still recommend wrapping architecture-specific method definitions within macro flag checks.
- Add
@[TargetFeature]annotation (#16717)
Thanks @stakach
Execution contexts
Section titled Execution contextsExecution contexts from RFC 0002 continue as a final preview feature,
enabled with the compiler flags -Dpreview_mt -Dexecution_context. We plan to
enable it by default in Crystal 1.21.
In addition to fixing bugs, we implemented M:N scheduling that allows threads to
be attached to and detached from a context. This avoids blocking other
fibers on certain syscalls, such as getaddrinfo.
- Add
Fiber::ExecutionContext::ThreadPool(#15885, #16750) - Detach execution context scheduler from running thread during blocking syscall (#15871, #16679)
The parallel contexts now automatically start threads to adapt to the actual workload and distribute fibers across more cores when needed.
- Add adaptive scaling to
ExecutionContext::Parallel(#16719)
This effort is part of the ongoing project to improve multi-threading support with the help of 84codes.
Thanks @ysbaddaden
Process API
Section titled Process APIThis release brings a new ergonomic, safe, and portable API for spawning sub-processes, as proposed in RFC 0025.
A significant change is that the modern API treats the command line as an array of strings. The first element is the program to execute, and the remaining elements are its arguments.
# legacy API:
Process.run("crystal", ["tool", "format"])
# modern API:
Process.run(["crystal", "tool", "format"])
The new API also includes convenient methods for capturing process output:
Process.capture and Process.capture_result.
The new API does not have a shell parameter. If you need shell behaviour, we recommend running a shell explicitly.
These are the most significant individual changes:
- Add overloads with combined
argsparameter instead ofcommand, args(#16681, #16739) - Add
Process.run?(#16738) - Add
Process.capture(#16773)
All new methods and overloads are experimental. We expect to stabilize the API for the next release.
Thanks @straight-shoota
io_uring
Section titled io_uringAn io_uring event loop is available for Linux targets and can be selected by
specifying -Devloop=io_uring when compiling. The event loop is highly
experimental and its performance benefits will differ for every program. We don’t
expect it to be faster than epoll, and it may even be slower, except on some
benchmarks with SQPOLL enabled.
SQPOLL can be selected by specifying the idle time for the kernel threads with
-Dio_uring_sq_thread_idle=200 (for 200ms). It is recommended to keep the idle
time low (below 2s), otherwise your CPU cores will regularly spin at 100% usage
when doing nothing.
An interesting but potentially surprising behavior is that any I/O call that could yield the calling fiber will now always yield. One benefit is that an always ready socket won’t block other fibers from progressing. A downside is that there might be lots of context switches.
- Add io_uring event loop (linux) (#16264)
Thanks @ysbaddaden
Kernel TLS
Section titled Kernel TLSKernel TLS is now supported by default on Linux and FreeBSD as long as support
has been compiled into OpenSSL (which should be the default) and enabled on the
system (likely disabled by default), for example with modprobe tls on Linux.
- Add Kernel TLS support to custom
OpenSSL::BIO(#16646)
Thanks @ysbaddaden
Macros
Section titled Macros- Add
#selectand#rejecttoHashLiteralandNamedTupleLiteral(#16558) - Refine error message for unsupported named arguments in macros (#16576)
Thanks @Blacksmoke16, @hahwul
Standard library
Section titled Standard libraryOptionParser now supports option bundling and can parse -ncfoo
as -n -c foo.
- Add short option bundling for
OptionParser(#16770)
Thanks @Qard
The OAuth module expected a success response on HTTP OK statuses, but multiple
OAuth providers send error responses with an HTTP OK status (sic).
Thanks @jgaskins
HTTP::WebSocket can now negotiate sub-protocols.
- Add
Sec-WebSocket-Protocolchecks toHTTP::WebSocket::Protocol(#16600, #16671) - Add support for
Sec-WebSocket-ProtocoltoHTTP::WebSocketHandler(#16574)
Thanks @antondalgren, @straight-shoota
StringScanner saw multiple improvements, ranging from performance to subtle
niceties for debugging.
- Add
StringScanner#scan,#check, and#skipoverloads forInt(#16557) - Add
StringScanner#peek_behind(#16593) - Add convenience methods to
StringScanner(#16595) - Improve cursor window format in
StringScanner#inspect(#16594)
Thanks @jneen
The memory management model for libxml2 changed in Crystal 1.17 to become manual instead of integrating the GC. The change introduced another memory leak that is now fixed.
- Call
xmlFreefor the string pointer fromxmlNodeGetContent(#16688)
Thanks @toddsundsted
Some additional niceties:
- Add
IO#read_greedy(#16535) - Add
FileUtils#rm_f(#12832) - Add
Pointer#align_upand#align_down(#16585) - Add
HTTP::Server::Response#content_typeand#content_lengthas convenient accessors (#16712)
Thanks @BlobCodes, @CTC97, @zw963
Compiler
Section titled Compiler- Prefer modern linker (
moldorlld) (#16696)
Thanks @straight-shoota
- Parse
MacroVarwith empty expressions:%var{}(#16772) - Parse
ProcNotationwith empty arg parenthesis:() ->(#16741) - Add
--base-pathforcrystal docs(#16091)
Thanks @straight-shoota, @matiasgarciaisaia
Dependencies
Section titled Dependencies- Support for LLVM 22.1 and 23.0 (#16631)
Thanks @HertzDevil
Deprecations
Section titled DeprecationsThere is a single deprecation in this release, and it is a trivial one: rename
Mutex to Sync::Mutex to use the new algorithm introduced in Crystal 1.19.
We’re experimenting with a two-step mechanism to deprecate a feature. First, we
soft deprecate the method in the documentation with a DEPRECATE: callout, then
after a few releases we’ll hard deprecate by adding the @[Deprecated]
annotation.
- Soft deprecate
Mutexin favor ofSync::Mutex(#16737)
Thanks @ysbaddaden
We have been able to do all of this thanks to the continued support of 84codes and every other sponsor. To maintain and increase the development pace, donations and sponsorships are essential. OpenCollective is available for that.
Reach out to crystal@manas.tech if you’d like to become a direct sponsor or find other ways to support Crystal. We thank you in advance!
Contribute