In my earlier article Calling Go Functions from Lisp, I explained the steps for making calls to Go functions from another language, specifically LispWorks Lisp. Today, I want to give a slightly more interesting example showing the use of Go channels through exported functions.
Go is widely admired for its native support for concurrency via Goroutines. Synchronization without explcit locks or condition variables is facilitated by Channels. This greatly simplifies concurrent programming. So, why can’t we take advantage of this powerful feature of Go and use it in another language environment? I am not saying that this alone is sufficient reason for integrating Go runtime with another language program, but it is an appealing reason!
Anyway, without getting into a debate on this topic, let me show you how easy it is to do this.
The following figure shows three Go functions that I have defined for working with a Go Channel.
For simplicity, I am using a single channel of type int. The function createChannel is used to initialize this global channel. The function writeToChannel writes an integer value to the channel and the third function readFromChannel reads an integer from the channel. All three functions are exported.
The next step is to create a LIB file and Header file from the Go source.
> go build -o GoFunctions.lib -buildmode=c-shared GoFunctions.go
As discussed in the article, we need to write appropriate C wrapper functions that get compiled into a DLL. See the following figure.
Building the DLL is a two-step process:
> gcc -c GoFunctionsWrapper.c
> gcc -shared -o GoFunctionsWrapper.dll GoFunctionsWrapper.o GoFunctions.lib
We have to copy GoFunctionsWrapper.dll and GoFunctions.lib to our Lisp project directory (or make sure they are in executable path).
Lisp Layer:
Now we are ready to write our LispWorks Lisp code. As discussed in the earlier article, we have to declare the DLL functions in Lisp and ensure that the DLL is registered properly.
Now comes the interesting part. Since channels are meaningful only in concurrent programs, in order to use and test them, we have to write a multi-threaded Lisp program. Here is the simplest one that uses the Go functions we have implemented:
I am writing to the channel in a separate thread (similar to goroutines), while reading from the same channel in the main thread.
Here is the output when I run the function:
Super. What will happen if we write to the channel (unbuffered) and read from it in the same thread? Deadlock, right? Here is the version I wrote to test that behavior:
And here is output:
As expected, the program is deadlocked because after the first element is written, it cannot be consumed by another thread!
So there we are. This example shows that we are able to use Go’s channels via exported functions in a foreign language, in this case Lisp.
As I mentioned in the beginning of this article, before using Go functions in a foreign language, we have to convince ourselves that this has definite advantages. Remember we now have two runtimes in our code, one from Go and another from Lisp. This can cause subtle problems in a large program, so we have to be careful, especially when passing objects around the two different runtimes.
Download the sources from here. Have a great weekend!
Recent Comments