Timeouts in Racket

Timeouts are important for programs that connect to external resources or that otherwise need to bound execution time. Implementing timeouts in Racket can be achieved using threads and channels.

#lang racket

(require racket/async-channel)

(define (main)
  ; For our example, suppose we're executing an external
  ; call that returns its result on a channel c1
  ; after 2s. We use an async-channel to simulate
  ; the non-blocking behavior of Go's buffered channels.
  (define c1 (make-async-channel))
  (thread
   (lambda ()
     (sleep 2)
     (async-channel-put c1 "result 1")))

  ; Here we implement a timeout using thread-receive-evt.
  ; We create an event that will be ready after 1 second,
  ; and use sync to wait for either the result or the timeout.
  (define result
    (sync
     (async-channel-get-evt c1)
     (handle-evt
      (alarm-evt (+ (current-inexact-milliseconds) 1000))
      (lambda (_) 'timeout))))
  
  (if (eq? result 'timeout)
      (displayln "timeout 1")
      (displayln result))

  ; If we allow a longer timeout of 3s, then the receive
  ; from c2 will succeed and we'll print the result.
  (define c2 (make-async-channel))
  (thread
   (lambda ()
     (sleep 2)
     (async-channel-put c2 "result 2")))

  (define result2
    (sync
     (async-channel-get-evt c2)
     (handle-evt
      (alarm-evt (+ (current-inexact-milliseconds) 3000))
      (lambda (_) 'timeout))))
  
  (if (eq? result2 'timeout)
      (displayln "timeout 2")
      (displayln result2)))

(main)

Running this program shows the first operation timing out and the second succeeding.

$ racket timeouts.rkt
timeout 1
result 2

In this Racket version:

  1. We use async-channel to simulate Go’s buffered channels.
  2. Threads are created using the thread function, which is similar to goroutines in Go.
  3. The sync function is used to implement the select behavior, waiting for either a result or a timeout.
  4. We use alarm-evt to create a timeout event, which becomes ready after a specified number of milliseconds.
  5. The handle-evt function is used to transform the timeout event into a value we can check.

This example demonstrates how to implement timeouts in Racket, providing functionality similar to the original Go example.