process-queue
1 Example usage
2 Process queue interface
process-queue?
process-queue-empty?
process-queue-enqueue
process-queue-wait
process-queue-active-count
process-queue-waiting-count
process-queue-set-data
process-queue-get-data
process-info
process-info/  c
process-will/  c
3 Imperative process queues
3.1 Plain imperative process queue
make-process-queue
3.2 Imperative process priority queue
make-process-queue
4 Functional process queues
4.1 Plain functional process queue
make-process-queue
4.2 Functional process priority queue
make-process-queue
8.13.0.2

process-queue🔗ℹ

Lukas Lazarek <lukas dot lazarek at eecs dot northwestern dot edu>

 (require process-queue) package: process-queue

This library implements a queue to manage the execution of many OS level processes in parallel.

Processes are described by a function that launches something and returns information about the launched thing. Specifically, the function returns process-info, which packages together
  • a function to control the process as it runs, in the style of process,

  • a process will, and

  • data to associate with the process

The process will describes what to do with a process when it terminates. For instance, the will might collect the results of the process, perform cleanup, and even enqueue a new process to "follow up" on the one that just terminated. In detail, the will is a function accepting the current state of the process queue and the process’s process-info and producing an updated process queue.

The process queue keeps track of the processes actively running and those waiting to run. The operations on the process queue add processes to the waiting list, manage process termination and will execution, and wait for all processes to terminate.

For convenience, the process queue also has a field for storing client data. This is useful for storing information related to processes on the side, since the queue is accessible to process wills.

This library provides several implementations of this basic concept which all conform to the same Process queue interface. First, Functional process queues implements the basic process queue in a functional style (such that the process queue is immutable). Second, Functional process priority queue implements a priority-queue version in a functional style. Third, Imperative process queues implements an imperative version of the basic process queue. Finally, Imperative process priority queue implements a priority-queue version in an imperative style.

1 Example usage🔗ℹ

This is an illustrative example of the usage of the Imperative process queues implementation.

It creates a queue that runs up to two processes simultaneously, and then enqueues three processes to be run (1, 2, and 4).

The first two are launched immediately. The second process terminates quickly, and its process will enqueues a followup process (3).

Process 4 is then launched, and after it terminates process 3 is launched and terminates.

Finally process 1, which had been running since the start, terminates.

Examples:
> (require process-queue)
> (define (simple-process cmd will)
    (match-define (list stdout stdin pid stderr ctl) (process cmd))
    (close-output-port stdin)
    (close-input-port stderr)
    (process-info stdout
                  ctl
                  will))
> (define (will:show-result/close-ports q info)
    (define stdout (process-info-data info))
    (displayln (port->string stdout))
    (close-input-port stdout)
    q)
> (define q (make-process-queue 2))
> (begin
    (process-queue-enqueue q
                           (λ ()
                             (displayln "launch 1")
                             (simple-process "sleep 5; echo done 1"
                                             will:show-result/close-ports)))
    (process-queue-enqueue q
                           (λ ()
                             (displayln "launch 2")
                             (simple-process "sleep 1; echo done 2"
                                             (λ (q info)
                                               (will:show-result/close-ports q info)
                                               (process-queue-enqueue
                                                q
                                                (λ ()
                                                  (displayln "launch 3")
                                                  (simple-process "echo done 3"
                                                                  will:show-result/close-ports)))))))
    (process-queue-enqueue q
                           (λ ()
                             (displayln "launch 4")
                             (simple-process "echo done 4"
                                             will:show-result/close-ports)))
  
    (void (process-queue-wait q)))

launch 1

launch 2

done 2

launch 4

done 4

launch 3

done 3

done 1

2 Process queue interface🔗ℹ

All of the implementations below provide the following operations on process queues.

procedure

(process-queue? q)  boolean?

  q : any/c
The predicate recognizing process queues.

procedure

(process-queue-empty? q)  boolean?

  q : process-queue?
A process queue is empty if it has no actively running processes and no waiting processes.

procedure

(process-queue-enqueue q launch [extra-data])  process-queue?

  q : process-queue?
  launch : (-> process-info/c)
  extra-data : any/c = #f
Enqueues a process on the queue. launch should launch the process (e.g. with process, but not necessarily) and return its process-info.

extra-data provides optional extra information that may or may not be used depending on the implementation (for example, a priority value).

Returns the updated process queue.

Blocks waiting for all of the processes in the queue to terminate, handling the process wills of processes as they terminate.

Returns the number of actively running processes at the time of call.

Returns the number of waiting processes at the time of call.

procedure

(process-queue-set-data q data)  process-queue?

  q : process-queue?
  data : any/c
Sets the value of the queue’s data field.

procedure

(process-queue-get-data q)  any/c

  q : process-queue?
Gets the value of the queue’s data field.

struct

(struct process-info (data ctl will))

  data : any/c
  ctl : ((or/c 'status 'wait 'interrupt 'kill) . -> . any)
  will : process-will/c
The struct packaging together information about a running process.

contract

process-info/c : contract?

 = 
(struct/c process-info
          any/c
          ((or/c 'status 'wait 'interrupt 'kill) . -> . any)
          process-will/c)
The contract for process-infos.
The contract for process wills.

3 Imperative process queues🔗ℹ

These imperative process queue implementations mutate a single process queue in-place. Hence, all of the Process queue interface operations that return a new process queue simply return the input queue after mutating it. (The interface returns the queue to support the Functional process queues.)

3.1 Plain imperative process queue🔗ℹ

procedure

(make-process-queue active-limit 
  [data 
  #:kill-older-than process-timeout-seconds]) 
  (and/c process-queue? process-queue-empty?)
  active-limit : positive-integer?
  data : any/c = #f
  process-timeout-seconds : (or/c positive-real? #f) = #f
Creates an empty imperative process queue.

active-limit is the maximum number of processes that can be active at once.

data initializes the data field of the queue which can be accessed with process-queue-get-data and process-queue-set-data.

process-timeout-seconds, if non-false, specifies a "best effort" limit on the real running time of each process in seconds. Best effort here means that the timeout is not strictly enforced in terms of timing — i.e. a process may run for longer than process-timeout-seconds. Instead, the library checks for over-running processes periodically while performing other operations on the queue and kills any that it finds. This is useful as a crude way to ensure that no process runs forever. If you need precise/strict timeout enforcement, you might consider using the timeout unix utility or other racket tools, and using process-timeout-seconds as a fallback.

3.2 Imperative process priority queue🔗ℹ

 (require process-queue/imperative-priority)
  package: process-queue

Imperative process priority queues prioritize processes to run first using a sorting function, provided when creating the queue. These queues use the third argument of process-queue-enqueue as the priority, which defaults to 0 if not provided.

procedure

(make-process-queue active-limit 
  [data 
  priority> 
  #:kill-older-than process-timeout-seconds]) 
  (and/c process-queue? process-queue-empty?)
  active-limit : positive-integer?
  data : any/c = #f
  priority> : (any/c any/c . -> . boolean?) = >
  process-timeout-seconds : (or/c positive-real? #f) = #f
Creates an empty imperative process priority queue, which prioritizes processes according to priority>.

See Plain imperative process queue for more details on the remaining arguments.

4 Functional process queues🔗ℹ

These implementations mirror the imperative versions, but all queue operations functionally transform the queue instead of mutating it in-place.

That means that you (the user of this library) must thread the queue around your program and take care never to use a stale queue: only the latest queue is valid, because queue values reflect the state of external and stateful processes. Hence, the functional implementations are a little strange, and you’re probably better off using the Imperative process queues. That said, sometimes a functional interface fits better with the rest of one’s program structure, caveats and all.

4.1 Plain functional process queue🔗ℹ

 (require process-queue/functional) package: process-queue

procedure

(make-process-queue active-limit 
  [data 
  #:kill-older-than process-timeout-seconds]) 
  (and/c process-queue? process-queue-empty?)
  active-limit : positive-integer?
  data : any/c = #f
  process-timeout-seconds : (or/c positive-real? #f) = #f
Creates an empty functional process queue.

See Plain imperative process queue for more details on the remaining arguments.

4.2 Functional process priority queue🔗ℹ

 (require process-queue/priority) package: process-queue

Functional process priority queues prioritize processes to run first using a sorting function, provided when creating the queue. These queues use the third argument of process-queue-enqueue as the priority, which defaults to 0 if not provided.

procedure

(make-process-queue active-limit 
  [data 
  priority> 
  #:kill-older-than process-timeout-seconds]) 
  (and/c process-queue? process-queue-empty?)
  active-limit : positive-integer?
  data : any/c = #f
  priority> : (any/c any/c . -> . boolean?) = >
  process-timeout-seconds : (or/c positive-real? #f) = #f
Creates an empty functional process priority queue, which prioritizes processes according to priority>.

See Plain imperative process queue for more details on the remaining arguments.