Abstract

Chapel is a programming language designed for productive parallel computing at scale. It simplifies parallel programming through elegant parallel programming constructs. As Chapel can utilize a system to its fullest potential, it seems like one of the ideal solutions to handle millions of network requests.

Currently, we can do network programming by using C Interoperability, but those constructs are not intuitive and good enough for users to easily do their tasks.

let’s begin

My Project

My Google Summer of Code project with Chapel was focused on creating a standard “Socket Library”. This project aimed to ensure concurrent, parallel safe, and easy to use constructs for networking.

The project was built on top of C System Calls, saving users from their complexity to perform even the most trivial tasks.

This blog post is for my final submission of the GSoC Project.

Under this project, the following four major parts needed to be managed:

  1. Getting review over design for the socket library from other developers.
  2. Adding and updating runtime functions that can then be used in Socket Module.
  3. Nonblocking but synchronous procedures that allow other tasks to run freely.
  4. Creating network constructs for UDP and TCP that align themselves except for the I/O differences.

Pull requests created in the period of GSoC:

PR TitleReview StatusMerge Status
Design: Socket LibraryApprovedDraft
Socket Construct Additions Sys ModuleApprovedMerged
File Descriptor Getter for fileApprovedMerged
Qthread SelectApprovedMerged
Update Addrinfo ConstructsApprovedMerged
Socket LibraryApprovedOpen

Work Done

story time

Design

Even though my GSoC Proposal talked about how the Socket Module should look like its design was entirely based on Python and C System Calls which aren’t the best ones and don’t align well with how we wanted it to look for Chapel.

My initial task was to look over how other modern-day programming languages implement their Socket Modules. I was recommended to look over at Julia, Go, Python, D, etc. I made did my analysis and brought together a simple gist as suggested to me by my mentors.

Finally, after analyzing implementations of other languages and multiple design discussion sessions with Chapel developers, I made a design pull-request describing the API.

[https://github.com/chapel-lang/chapel/pull/17899]

Changes to Runtime

The module that is storing most of the extern procs, records, and constants is the Sys standard module. There were several changes made to the module which were as follows:

  • Add in new constants to be used as flags in C System Calls e.g. F_GETFL, SOL_SOCKET to be used in fcntl and setsockopt or getsockopt.
  • New opaque types sys_in_addr_t and sys_in6_addr_t which are for standard network addresses like loopback, any, and broadcast.
  • Standard Sizes for address and service buffer length to be used in getnameinfo.
  • Add in generic getters and setters for sockaddr_storage record.

All this was done as a part of the below PR and soon will be moved to Socket Module when it gets added in.

%[https://github.com/chapel-lang/chapel/pull/17932]

To treat socket connections like chapel file we needed access to their file descriptor for all the operations for this a minor addition was made in runtime to fetch the file descriptor. This allowed us to use Chapel I/O in the Socket Module which handles buffered I/O and keeps the I/O methods consistent across chapel modules.

%[https://github.com/chapel-lang/chapel/pull/17988]

Finally, to resolve Domain Name Service from user inputs like https://summerofcode.withgoogle.com/" I made some fixes to getaddrinfo and constructs related to it whose old implementation had a few issues:

  • Some of the function definitions were not correct which were resulting in segmentation fault and memory leak.
  • addrinfo wasn’t using the new capabilities of chapel runtime and was not interacting with C code it was defined separately.
  • As sys_addrinfo_ptr was an opaque type we can’t compare it with nil which was necessary for checking the end of the linked list returned by getaddrinfo.

%[https://github.com/chapel-lang/chapel/pull/18072]

NonBlocking Procedures

The architecture for the socket library was decided to be synchronous but nonblocking as that’s how most chapel procedures work ( async is a possibility for the future ). If they need to block they will yield the main thread so that other tasks of the process can run without being blocked.

For this, I decided to use I/O multiplexing using select system calls where the procedure waits until it becomes ready which can mean it becomes readable, writable or a timeout occurred.

blocking.png

The issue was that calling in normal select will cause the pthread on which chapel qthreads are scheduled to block. Hence, to counter this qthreads library provides its own set of system calls that will block but not on the current pthread instead are transferred to another helper pthread where they do blocking.

The outcome was making use of qt_select inside of our sys_select call.

Qt_select.png

%[https://github.com/chapel-lang/chapel/pull/18019]

There were certain hurdles and PR remained stale for some time because we faced hurdles due to bugs in error informing inside of qthread's library procedures, which were resolved by collaborating with qthread's core team. The following is the issue that describes the bugs in detail.

%[https://github.com/Qthreads/qthreads/issues/88]

Socket Module

The socket module was designed as stated in the design PR and had a lot of modifications being made to the exposed API as reviews were coming in.

The main intent was to hide the complexity associated with the C System Calls under custom procedures and records.

The major records in Socket Module that do all the heavy lifting include

  • ipAddr
  • tcpConnection
  • tcpListener
  • udpSocket

Along the lines of Julia utility procedures were provided in the module to user which included bind, naggle, delayAck, getsockname, getpeername, setSockopt and getSockOpt.

The PR has been rebased and changes are being made to it since the finalization of the design for the Module. It consists of all the tests, documentation, and the module itself as well.

Currently, all the tests are passing and PR is under review while performance testing is being done.

%[https://github.com/chapel-lang/chapel/pull/17960]

Libevent Integration

Libevent exposes APIs that are more performant and handle more open file descriptors or connections than select call ever could. The reason being that libevent uses kernel methods that have a time complexity of O(1) unlike select which is O(N).

Libevent works using callbacks but we have to ensure to make it work in a non-blocking and synchronous manner. Integrating libevent was something that was never done in the chapel so it was a completely new experience for me.

The architecture can be best explained with diagrams, so here goes.

connect with event loop.png

The events are handled by the event loop which runs a callback whenever an event ( read/ write/ timeout ) occurs on the file. It writes back the event’s value to a sync variable (passed as an argument to the callback function) transitioning to full state and the procedure waiting on the sync variable is finally woken up.

event loop.png

Usage of Socket Module

A TCP Client in Chapel is as follows:

use Socket;
use Time;

proc main() {
  config const host:string = "chapel-lang.org", port:uint(16) = 80;
  const x = connect(host, port);
  const writer = x.writer();
  writer.writeln("hello world!");
  writer.close();
  x.close();
}

To serve a basic TCP Server using Socket Module will look as follows:

use Socket;

proc main() {
  var address = ipAddr.create("127.0.0.1", 8000, IPFamily.IPv4);
  var server = listen(address, backlog = 6000);
  writeln("Listening for connections on ", server.addr.host, ":", server.addr.port);
  defer server.close();

  while (true) {
    var conn = server.accept();
    const address = getpeername(conn);
    const writer = conn.writer();
    writer.writeln(address, " connected ");
    writer.close();
    conn.close();
  }
}

We can utilize chapel begin to demonstrate UDP connections in a single program.

begin creates a new task or user-level thread which has a separate execution context from the main task/thread providing us capabilities for task parallelization.

use Socket;

proc main() throws {
  var address = ipAddr.ipv4(IPv4Localhost, 8111);
  var receiver = new udpSocket();
  bind(receiver, address);

  var sender = new udpSocket();

  var sent = b"hello world!";
  begin {
    var sentBytes = sender.send(sent, address);
  }
  var received:bytes;
  received = receiver.recv(sent.size);
  writeln(received);
}

Weekly Updates

Updates for all the weekly meetings that took place can be found on this notion site.

Future Work

future possibilities

  • Integrating libevent with the chapel channels through an I/O plugin.
  • Running performance tests to analyze the network calls and I/O latency.
  • Supporting opaque types with, the LLVM backend.
  • Providing users with libevent as a dependency if they don’t have it on their system.

Learning

learning

  • The major learning outcome from this project was good coding practice. I learned how to write DRY, reusable code following SOLID principles. I also learned how to write good tests/specs covering all possible scenarios.
  • When writing the Design for the project, I had long discussions with my mentors trying to decide on exposed APIs. Coming up with good names for methods, variables, and hiding the complexity from end-users.
  • Through this journey, I learned a lot about how open source projects work, getting involved with the community, and contributing to the projects.
  • The major tech takeaway for me was how familiar I got in working with C System Calls, Pointer Exceptions, and Chapel itself.
  • I am still exploring Asynchronous I/O through libevent for the project which is something that powers fast servers like NodeJS, Julia, and Go.

Special Thanks

thank you

It was wonderful working with the Chapel community throughout the GSoC project. I would like to thank Michael, Krishna, and Ankush for solving my queries, debugging issues with me, giving constructive suggestions and guiding me through the program. Thanks to Brad, Lydia, Engin, Greg, Paul, and the members of the Chapel Community for helping with their suggestions.

I would like to thank Shivansh Saini, @Nishant Mittal, and @Ashish Kumar for motivating and guiding me in applying and getting through the whole program.

Final thanks to Google for organizing this amazing program. I feel GSoC made it easier for me to get started with open source contributions. It was a great lesson on how projects are built and maintained with great quality. I am excited to continue contributing to the open-source community

the end

Thanks for reading :) and please share your thoughts. I always like to meet more enthusiastic people, so do reach out to me on Twitter or Linkedin to share your feedback or for any queries. I’d be more than happy to help and connect.