# How to implement parallel-or?

**URL:** <https://racket.discourse.group/t/how-to-implement-parallel-or/3987>\
**Category:** Questions & Answers\
**Tags:** question, parallelism, futures\
**Created:** [October 27, 2025, 3:29pm UTC](https://racket.discourse.group/t/how-to-implement-parallel-or/3987 "2025-10-27T15:29:09Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![ohqo](https://avatars.discourse-cdn.com/v4/letter/o/76d3ee/32.png) [@ohqo](https://racket.discourse.group/u/ohqo)\
**Post date:** [October 27, 2025, 3:29pm UTC](https://racket.discourse.group/t/how-to-implement-parallel-or/3987/1 "2025-10-27T15:29:09Z")

</div>

The parallel-or operation allows you to run two computations in parallel and either immediately returns true if one of the computations halts with true or returns false if both computations halt with false.

I couldn't figure out a way of doing this with `future` and `touch` and I wonder if I missed something or there are other primitives I'm supposed to use.

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [October 27, 2025, 4:57pm UTC](https://racket.discourse.group/t/how-to-implement-parallel-or/3987/2 "2025-10-27T16:57:23Z")

</div>

This is difficult to do with futures, because futures can potentially block, making no more parallel progress until `touch`ed, and there is no mechanism analogous to `choice-evt` to select whichever future is ready first.

But the new parallel threads coming in Racket 9.0 (test a candidate from [https://pre-release.racket-lang.org](https://pre-release.racket-lang.org)!) will make `parallel-or` very convenient to express:

```scheme
#lang racket

(module+ main
  (parallel-or (begin (println 'a) 'a)
               (begin (println 'b) 'b)))

(define-syntax-rule (parallel-or a-expr b-expr)
  (do-parallel-or (λ () a-expr) (λ () b-expr)))

(define (do-parallel-or a-thunk b-thunk)
  (define (spawn thunk)
    (define cust (make-custodian))
    (values (parameterize ([current-custodian cust])
              (thread #:pool 'own #:keep 'results thunk))
            cust))
  (define-values [a-thd a-cust]
    (spawn a-thunk))
  (define-values [b-thd b-cust]
    (spawn b-thunk))
  (define ready-thd
    (sync a-thd b-thd))
  (cond
    [(thread-wait ready-thd)
     => (λ (rslt)
          (custodian-shutdown-all (if (equal? ready-thd a-thd)
                                      b-cust
                                      a-cust))
          rslt)]
    [else
     (define-values [ready-cust other-thd other-cust]
       (if (equal? ready-thd a-thd)
           (values a-cust b-thd b-cust)
           (values b-cust a-thd a-cust)))
     (custodian-shutdown-all ready-cust)
     (cond
       [(thread-wait other-thd)]
       [else
        (custodian-shutdown-all other-cust)
        #f])]))

```

The dance with custodians is intended to arrange that a computation that either returns `#f` or is not chosen has all its resources cleaned up, but also to _not_ shut down any resources from a computation that _is_ chosen, since it might be useful to have `parallel-or` return a custodian-managed resource.

This implementation doesn't consider potential exceptions coming from one (or both) of the parallel computations. You could configure the threads' initial exception handlers to communicate exceptions, or further enhancements to `thread` (and/or integrating the new features into `delay/thread`) might make things more convenient:

> [@Help test via snapshots: parallel threads](https://racket.discourse.group/t/help-test-via-snapshots-parallel-threads/3920/23):
>
> > [@Help test via snapshots: parallel threads](https://racket.discourse.group/t/help-test-via-snapshots-parallel-threads/3920/22):
> >
> > Potentially a `#:keep 'result/exn` option could be added later.
> 
> Yes, I agree. I intended to add that, but haven't gotten to it.

---

<div class="post-metadata">

**Author:** ![ohqo](https://avatars.discourse-cdn.com/v4/letter/o/76d3ee/32.png) [@ohqo](https://racket.discourse.group/u/ohqo)\
**Post date:** [October 27, 2025, 8:20pm UTC](https://racket.discourse.group/t/how-to-implement-parallel-or/3987/3 "2025-10-27T20:20:12Z")

</div>

That looks great! Is there a good way of managing multiple racket installations? I'd like to try out the new feature but would still like to have an easy way of reverting back to the stable release for other stuff.

The use case I have in mind is purely functional. I'm not familiar with custodians and I wonder how much the code could be simplified if both thunks are pure.

To provide some context, I was doing a coding exercise which involves a matrix of letters and the goal is to check whether you can find a string. I coded up a purely functional solution which ended up twice as slow as an imperative solution. I want to try parallelizing the algorithm to see if the functional data structures pay off with more cores. Parallel-or seems to make sense in this case because the short circuiting could give a performance boost.

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [October 27, 2025, 11:42pm UTC](https://racket.discourse.group/t/how-to-implement-parallel-or/3987/4 "2025-10-27T23:42:20Z")

</div>

> [@ohqo](#):
>
> Is there a good way of managing multiple racket installations? I'd like to try out the new feature but would still like to have an easy way of reverting back to the stable release for other stuff.

There are several good options.

From a download site like [https://pre-release.racket-lang.org](https://pre-release.racket-lang.org), you can have the installer perform an in-place installation (or select a distribution packaged as a tarball), putting the whole installation in a directory like `~/racket-8.18.0.19/`, so that the `racket` executable is at `~/racket-8.18.0.19/bin/racket`. That works especially well for a one-off short-term use like this, given that the Racket 9.0 release should be [on Friday](https://racket.discourse.group/t/racket-v9-0-branching-is-done/3973), give or take.

The [`raco cross`](https://docs.racket-lang.org/raco-cross/index.html) tool can serve as a more automated way of doing basically the same thing, even if you don't need its additional features for cross-compilation and such.

You can also install multiple Racket versions on a machine long-term, similar to the way `/lib/qt5/` and `/lib/qt6/` both exist on my laptop. Details will vary depending on your system. This is less likely to be useful for Racket, though, because Racket's strong emphasis on compatibility means you should almost never have to change your code to upgrade to the latest Racket release.

> [@ohqo](#):
>
> The use case I have in mind is purely functional. I'm not familiar with custodians and I wonder how much the code could be simplified if both thunks are pure.

You can simplify somewhat (see below), but you need to substitute `kill-thread` for one of the `custodian-shutdown-all` calls, because, if one thread terminates with a non-false value, you surely want to terminate the other thread.

Custodians can manage more general resource-cleanup issues. For example, imagine you wanted to look up the weather in Chicago, and wanted to use `parallel-or` to try both the [National Weather Service API](https://www.weather.gov/documentation/services-web-api) and [Open-Meteo](https://open-meteo.com/), returning the result from whichever supplies it fastest. Using `thread-kill` would stop the thread, freeing CPU resources, but it could potentially leave open file descriptors or other OS-level resources implementing an HTTP connection. More generally, a given thread could have launched other threads to perform its work. Using custodians can reliably reclaim all potentially-used resources.

```scheme
(define (do-parallel-or a-thunk b-thunk)
  (define (spawn thunk)
    (thread #:pool 'own #:keep 'results thunk))
  (define a-thd
    (spawn a-thunk))
  (define b-thd
    (spawn b-thunk))
  (define ready-thd
    (sync a-thd b-thd))
  (define other-thd
    (if (equal? ready-thd a-thd)
        b-thd
        a-thd))
  (cond
    [(thread-wait ready-thd)
     => (λ (rslt)
          (kill-thread other-thd)
          rslt)]
    [else
     (thread-wait other-thd)]))

```

> [@ohqo](#):
>
> To provide some context, I was doing a coding exercise which involves a matrix of letters and the goal is to check whether you can find a string. I coded up a purely functional solution which ended up twice as slow as an imperative solution. I want to try parallelizing the algorithm to see if the functional data structures pay off with more cores. Parallel-or seems to make sense in this case because the short circuiting could give a performance boost.

Outside of an exercise, these find-a-string problems are very well studied, so you probably would want to make sure you're implementing one of the current state-of-the-art algorithms (though I couldn't say off-hand what you want). But your results from adding parallelism, whether to an optimal algorithm or not, might be useful to help evaluate the still-quite-new implementation of parallel threads:

> [@Help test via snapshots: parallel threads](https://racket.discourse.group/t/help-test-via-snapshots-parallel-threads/3920/):
>
> We expect that the next Racket release (tentatively v9.0) will have additional support for parallelism. We need your help to get it right. Racket has always provided lightweight threads for concurrency, but Racket threads have never taken advantage of multiple processor cores to run in parallel, unlike threads provided by an operating system or in many other languages. Racket provides places and futures for parallelism, but those constructs have restrictions that make them less easy to use than…
