Concurrency in Ballerina (Workers, Send, Receive, Flush, Wait …)

Vinod Kavinda
Ballerina Swan Lake Tech Blog
4 min readDec 29, 2018

--

Ballerina has first class support for concurrent programming. Developers can easily spawn new threads of execution whenever required. In Ballerina 0.990.0 release, a number of significant improvements have been done related to concurrency including introducing brand new constructs.

In this post, the constructs available for concurrent programming in Ballerina will be discussed.

In Ballerina, there are two ways that you can do parallel execution. By using workers or calling an asynchronous function.

Asynchronous Function calls

Any function in Ballerina can be invoked asynchronously using the start keyword. An asynchronous call returns a future object. This future can be used to check the status of the future, cancel the execution or to extract the result. Run following program to see how it works.

Workers

In Ballerina every execution belongs to a worker. By default, all the functions run in the default worker. Developers can run code parallelly by defining new workers within functions. When there is a function call from a worker it also runs on the same worker (same strand) unless it is an asynchronous function invocation. Let’s see a simple Ballerina code with workers.

You may aware that every ballerina code can be shown through a sequence diagram. Have a look at this diagram generated by Ballerina VSCode plugin. It explains how the code will run.

Sequence digram generated for workers.bal

Worker Send/Receive

Workers can send and receive messages from other sibling workers. When there is a worker receive, the execution halts until the message is available. If the sending worker results in an error or panic before sending the message, the receiver will get the error message or it also will be panicked. Ballerina compiler guarantee that worker interactions are always valid and make sure it won’t result in a deadlock.

Worker Sync Send

Worker sends action just send the message and continues the execution. The Sync send action sends the message and wait until the message is fetched by the receiver. The sync send action has a return value as well. If the message is successfully received, it will return nil or if the receiver returned an error without receiving the message, the sync send also will return the same error. If the receiver ended up in a panic, sync send also will panic.

Wait Action

While the workers are getting executed parallelly, at some point you may need to wait until certain tasks are completed by a different worker. wait can be used to pause the execution until given futures are completed. There are three variations of the wait as wait for a single future to complete, wait for all the given futures to complete and wait for anyone of given futures to complete. While waiting for a future to complete, if the future returns an error or panics, the result of the wait also is an error or a panic.

When waiting for multiple workers, wait returns the error or panic of the first future that fails regardless of what happens in other futures. In wait for any, the return type of the wait expression is the union of all the waiting futures’ return types. In wait for al,l the return value can be assigned to a record type or a map. If not specified, names of the futures become the keys for the returning map or record. Please refer the following sample for more info.

Flush Action

In worker send action, it sends the message and continues execution regardless of the message is received from the other end or not. But at some point, a worker may be interested whether the sent messages are received from the other end. flush can be used to wait until all the messages sent to one or more workers are successfully received. In the flush action, you may either specify a worker to be flushed or a flush without a worker identifier will flush all the message sends from the current worker. Flush returns nil if the messages were successfully received. If the receiver fails with an error or panic, same will be propagated to flush action’s result as well. When flushing several workers, if any of receivers failed, the first failure’s result will be the result of the flush. So the return type of flush is always either nil or error.

Summary

So these are the concurrency related constructs in a ballerina. Always try to assign your work to concurrent workers for efficient resource usage and performance improvements. Even, when a worker is halted for some event to happen, ballerina runtime make sure that particular thread is released and restarts the execution only when it’s ready to continue. This avoids any thread starvation. But, you need to be aware that there can be race conditions when you use shared resources between workers. Use the lock construct in that scenarios as appropriate for thread-safe variable access. Here is my post on locks for more details.

Happy dancing with Ballerina!

--

--

Vinod Kavinda
Ballerina Swan Lake Tech Blog

Engineer @WSO2 | Developing the brilliant @ballerinalang | Trying to learn the art of writing here!