If you’ve looked at some of the concurrency and networking procedures
available in Racket, you might’ve noticed a bunch that follow the
<name>/enable-break and it might not have been
immediately obvious why or when you would use these variants of these
procedures over the regular ones. Let’s look at a snippet from the
If breaking is disabled when
tcp-accept/enable-breakis called, then either ports are returned or the
exn:breakexception is raised, but not both.
So, the procedure guarantees that either it will accept a connection and return a pair of ports, or it will raise a break exception, if breaking is disabled when it is called. Sounds straightforward enough, but it still might not be clear where this would come in handy. The part of the quote about “if breaking is disabled” is an important hint. Consider this simplified piece of code based on my previous post re. the protohackers challenge:
We set up a listener, then enter a loop to start accepting new connections and spawn threads to handle every connection. For every connection-handling thread, we spawn a supervising thread to clean up resources once the handling thread is done.
We want to ensure that a handling thread gets spawned for every
accepted connection, so we want to guarantee that no breaks can be
received between •1 and •2. Likewise, we want to guarantee that every
handler thread has an associated supervision thread, so no breaks
should be allowed between •2 and •3 either. So, we’ve wrapped •1, •2,
and •3 in the
parameterize-break form to disable breaks. However,
accepting a new connection will block the thread until a client
actually tries connecting to the server. This means that we can only
stop this server if we happen to send it a break at exactly the time
after •3 returns, or by killing the process.
By replacing the call to
we can preserve the same guarantees as before while also allowing
breaks to be raised before new connections are accepted.