Developing concurrent programs with Pony
Horse Power
Pony, an object-oriented programming language with static typecasting, trots down well-mapped paths to deliver secure, high-performance code for concurrent applications.
The still young Pony [1] programming language uses the actor model [2] and capabilities [3] to make deadlocks and data races things of the past. In this article, I take Pony for a test ride with an example application that, once it has compiled successfully, logs the consumption of paint and reports the results in a single line.
Figure 1 revisits the problems of concurrent programming in C and C++. To improve performance, the hypothetical program outsources tasks to concurrent threads that access the shared memory area. Locks manage access to prevent different threads editing data simultaneously, falsifying each other's results in the process, and generating race conditions. Nothing good results when programming errors interact and create deadlocks.
Table 1 summarizes the problems associated with implementation across various languages. The comparison with Java is inevitable, because the language offers one of the most successful enhancements of the C and C++ method. A comparison with Rust [4] is also appropriate, because the new kid in town also tries to generate secure and fast code for concurrent applications.
Table 1
Feature Comparison
Language Feature | C/C++ | Java | Rust | Pony |
---|---|---|---|---|
AoT Compilation |
Yes |
No* |
Yes |
Yes |
Memory Secure |
No |
Yes |
Yes |
Yes |
Type Secure |
No |
Yes |
Yes |
Yes |
Avoids Race Conditions |
No |
No |
Yes |
Yes |
Avoids Deadlocks |
No |
No |
No |
Yes |
Actor-Based |
No |
No |
No |
Yes |
*Officially as of Java 9 [5]. |
||||
Except for dynamic loading of classes [6]. |
Both Pony and Rust compile programs ahead of time (AoT) to create executable binary code. The method saves time because the languages identify and rule out type conflicts in advance. Java, on the other hand, usually first converts source code to non-executable bytecode, which a virtual machine (VM) executes and monitors. Pony and Java both leave memory management to a garbage collector, which Pony integrates into the run-time environment of the compiler results because it lacks a VM.
Actor Model
Pony programs are always free of data races and deadlocks through the agency of actors. As Figure 2 shows, the model does without a shared memory area and therefore does not need to use locks. Because internally the threads (aka actors) work sequentially, data races could theoretically still occur between the competing actors; however, applications mutually exchange application data through asynchronous communications. The sender deletes all pointers to variable data before sending, which also successfully prevents data races in this scenario.
Behaviors (asynchronous functions) are used as mailboxes for incoming messages, which Pony calls like normal methods but runs asynchronously. The actor thus continues its work sequentially. The Pony developers did not create the actor model; the programming languages Erlang, Io, D, and Scala also use it.
Installation
The current release of Pony [7] can be installed using the commands in Listing 1 on a 64-bit Debian system. The first line integrates the packet source into the /etc/apt/sources.list
file. The second line updates the Debian package database, and the third line installs Pony. There is no Debian package for the i386 architecture; a manual compile is needed in this case.
Listing 1
Installation on Debian 8
Listing 2 shows the directory tree of the sample application. If you change to the project directory and compile the program with ponyc
, the result is the binary executable, as shown in Figure 3, which you can launch with the ./blue-horses
command.
Listing 2
The .pony Files
The main.pony
file is the entry point for the Pony program (Figure 4). It uses the class from the resource.pony
file to create paint stocks for the primitives (colors) from primitives.pony
and presents these to the actor of the Painter
type from painter.pony
. The actor processes the quantity of paint and reports its consumption in the form of a report to main.pony
.
Classes
Pony summarizes application data in classes, as the Resource
class illustrates in the example in Listing 3, which stores paint types and their respective stock. The class
keyword introduces the class in line 1, and line 2 declares the public field name
of the user-defined Paint
type, which line 6 in Listing 4 again picks up. Thanks to the let
keyword, the field can only be written to once, in contrast to a field initiated as var
. The class stores the paint types and the respective stock.
Listing 3
resource.pony
Listing 4
primitives.pony
Line 3 of Listing 3 defines the private _amount
field as an integer data type and sets its value to
. Private field names are prefixed by an underscore, and only the owning class instance can access them. The constructor of the Resource
class introduces the new
keyword in line 5. The parameter list follows in create()
. The name'
variable field is a pointer to an object of the Paint
type. The next line then saves the name
field permanently as an instance of the class.
The fun
keyword introduces the method declarations for level()
and fill()
(lines 8 and 11). If the parameter list is followed by a colon, the type of the return value follows. For level()
, it is integer type U64
from the private _amount
field.
The value of the expression in the last line of a method is usually its return value, although the use of return
lets developers close methods prematurely. The fill()
method (line 11) increments the value of _amount
by 1
.
Because of the ref
specification in line 11, the method requires write access for a pointer to an instance of the Resource
class. These rights are granted by reference capabilities, which I talk about later; however, before I get to that, I'll look at primitives, which are similar to classes.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
Linux Kernel 6.13 Offers Improvements for AMD/Apple Users
The latest Linux kernel is now available, and it includes plenty of improvements, especially for those who use AMD or Apple-based systems.
-
Gnome 48 Debuts New Audio Player
To date, the audio player found within the Gnome desktop has been meh at best, but with the upcoming release that all changes.
-
Plasma 6.3 Ready for Public Beta Testing
Plasma 6.3 will ship with KDE Gear 24.12.1 and KDE Frameworks 6.10, along with some new and exciting features.
-
Budgie 10.10 Scheduled for Q1 2025 with a Surprising Desktop Update
If Budgie is your desktop environment of choice, 2025 is going to be a great year for you.
-
Firefox 134 Offers Improvements for Linux Version
Fans of Linux and Firefox rejoice, as there's a new version available that includes some handy updates.
-
Serpent OS Arrives with a New Alpha Release
After months of silence, Ikey Doherty has released a new alpha for his Serpent OS.
-
HashiCorp Cofounder Unveils Ghostty, a Linux Terminal App
Ghostty is a new Linux terminal app that's fast, feature-rich, and offers a platform-native GUI while remaining cross-platform.
-
Fedora Asahi Remix 41 Available for Apple Silicon
If you have an Apple Silicon Mac and you're hoping to install Fedora, you're in luck because the latest release supports the M1 and M2 chips.
-
Systemd Fixes Bug While Facing New Challenger in GNU Shepherd
The systemd developers have fixed a really nasty bug amid the release of the new GNU Shepherd init system.
-
AlmaLinux 10.0 Beta Released
The AlmaLinux OS Foundation has announced the availability of AlmaLinux 10.0 Beta ("Purple Lion") for all supported devices with significant changes.