Signals in Crystal

Here’s the translation of the Go code to Crystal, formatted in Markdown suitable for Hugo:

Our first example demonstrates how to handle Unix signals in Crystal. This is useful for scenarios like gracefully shutting down a server when it receives a SIGTERM, or stopping a command-line tool from processing input when it receives a SIGINT.

require "signal"

# Create a channel to receive signal notifications
sigs = Channel(Signal).new(1)

# Register the channel to receive notifications for SIGINT and SIGTERM
Signal::INT.trap do
  sigs.send(Signal::INT)
end

Signal::TERM.trap do
  sigs.send(Signal::TERM)
end

# Create a channel to notify when we're done
done = Channel(Bool).new(1)

# This fiber executes a blocking receive for signals
# When it gets one, it'll print it out and then notify the program that it can finish
spawn do
  sig = sigs.receive
  puts
  puts sig
  done.send(true)
end

# The program will wait here until it gets the expected signal
puts "awaiting signal"
done.receive
puts "exiting"

When we run this program, it will block waiting for a signal. By typing ctrl-C (which the terminal shows as ^C), we can send a SIGINT signal, causing the program to print SIGINT and then exit.

$ crystal run signals.cr
awaiting signal
^C
SIGINT
exiting

In Crystal, we use the Signal module to handle Unix signals. Instead of using a buffered channel like in the original example, we use Crystal’s trap method to set up signal handlers. These handlers send the received signals to a channel.

We use fibers (Crystal’s lightweight threads) instead of goroutines, but the concept is similar. The main fiber waits for a value on the done channel, which is sent when a signal is received.

This example demonstrates Crystal’s concurrency model using channels and fibers, as well as its signal handling capabilities.