OUR SITES NetworkRADIUS FreeRADIUS

The parallel Statement

Syntax
parallel [ empty | detach ] {
    [ statements ]
}

The parallel section runs multiple child subsections in parallel. Once all of the statements in the parallel section have finished execution, the parallel section exits. The return code of the parallel section is the same as if each subsection was run sequentially.

The parallel section is most useful for running modules which call external systems and wait for a reply. When modules are run in series, the first module must run to completion, and only then can the second and subsequent modules be executed. With parallel, the first module is run, and then as soon as that module yeilds in order to wait for an event, the second module can immediately run.

Each child subsection is executed using a subrequest. Please see the next subsection for more information about subrequests.

Despite the "parallel" name, the section will only run one module at a time. This limitation ensures that there are no issues with multi-threaded race conditions, locking, etc. The "parallel" name is used in order to highlight the fact that multiple events are being handled at the "same time" inside of a parallel section. The name does not indicate that there are multiple threads of execution.

There are no limits as to the number of subsections which can be placed inside of a parallel section.

Example

The following section sends a RADIUS packet to radius1, and then immediately another RADIUS packet to radius2. The parallel section returns only when both modules have returned. i.e. Either via timeouts, or by receiving replies to the requests.

Each module radius and radius2 is handed a child request which is an identical copy of the parent request.

parallel {
    radius1
    radius2
}

Child Requests are Independent

Each module or subsection runs as a new child request, i.e. a subrequest. Each child request is an identical copy of the parent request. Policies in the child can update the original parent by referencing &parent.request, or &parent.reply. Please see the list syntax for a more complete description of how to refer to parent requests.

The child requests are required because each subsection is run independently, with independent events, timers, etc. That is, each child request has its own state, independent of any other request. This independence is required in order to fully implement the "parallel" nature of this keyword.

Once the child requests are finished, they are freed. Any information stored in them is discarded. Information that is needed after the parallel section is run should be saved in the parent request.

Example

When using complex policies inside of a parallel section, each subsection should be enclosed in a group keyword.

parallel {
    group {
        radius1
        if (fail) {
           linelog1
           &parent.reply += {
               &Reply-Message = "radius1 failed"
           }
        }
    }
    group {
        radius2
        if (fail) {
           linelog2
           &parent.reply += {
               &Reply-Message = "radius2 failed"
           }
        }
    }
}

Subrequests are Synchronous

Execution of the parent request is paused while each child request is running. The parent request continues execution once all of the the child requests have finished.

Unlike the subrequest keyword, the child requests of a parallel section cannot be made asynchronous via the detach keyword.

parallel empty

The parallel empty { …​ } syntax creates empty child requests. These requests contain no attributes. Attributes in the child request must be added manually via an edit statement.

The empty keyword is most useful when it is necessary to manually determine which attributes go into a child request.

The empty keyword cannot be used with the detach keyword. There is no purpose to creating independent child requests which contain nothing.

Example

In this example, the radius1 module sees a User-Name which is modified from the parent User-Name, and a User-Password which is static. The radius1 module sees a User-Name which is modified in a different way from the parent User-Name, and it sees a User-Password which is a copy of the parents User-Password.

parallel empty {
    group {
        &request := {
            &User-Name = "%{&parent.request.User-Name}@example.org"
            &User-Password = "hello"
        }
        radius1
    }
    group {
        &request := {
            &User-Name = "%{&parent.request.User-Name}@example.com"
            &User-Password = &parent.request.User-Password
        }
        radius2
    }
}

parallel detach

The parallel detach { …​ } syntax creates child requests which are immediately detached from the parent request. Each child request contains copies of all attributes in the parent request.

The parent request continues immediately, and independently of the child requests.

The detach keyword cannot be used with the empty keyword. There is no purpose to creating independent child requests which contain nothing.

The return code from the parallel detach { …​ } section is noop. Since all of the child requests are independent of the parent, they cannot convey any information back to the parent. Therefore, the return code is always noop.

The detach keyword is most useful when it is necessary to send packets to multiple destinations, but where there is no need to wait for an answer.

Example

In this example, the parallel detach { …​ } syntax is used to send packets to four different destinations at the same time, in a "fire and forget" manner.

parallel detach {
    radius1
    radius2
    radius3
    radius4
}

Exiting Early from a Parallel Section

In some situations, it may be useful to exit early from a parallel section. For example, to proxy a packet to multiple destinations, and then return as soon as any one of the destinations returns a reply.

The return keyword in a child is used to return from the parallel section, and to stop the execution of all children.

Example
parallel {
    group {
        radius1
        if (ok) {
            return
        }
    }
    group {
        radius2
        if (ok) {
            return
        }
    }
    group {
        radius3
        if (ok) {
            return
        }
    }
    group {
        radius4
        if (ok) {
            return
        }
    }
}