Skip to content

Function call implicitly serializes build #116

Description

@cspiel

On my 4-way Linux box calling an OMake function in a rule body
serializes an otherwise parallel build. The following OMakefile
reproduces the behavior.

.LANGUAGE: program


iota(n) =
        iter(a, x) =
                if ge(x, n)
                        return a
                else
                        return iter(array(a, x), add(x, 1))
        init[] =
        return iter(init, int(0))


write_script_file(a_filename) =
        fprint(a_filename, $'''#! /bin/sh

sleep 1
cp $1 $2
''')


filenames = addprefix($'a', iota(8))

source_ext = $'.s'
direct_target_ext = $'.d'
procedure_target_ext = $'.p'
function_target_ext = $'.f'

sources = addsuffix(source_ext, filenames)
direct_targets = addsuffix(direct_target_ext, filenames)
procedure_targets = addsuffix(procedure_target_ext, filenames)
function_targets = addsuffix(function_target_ext, filenames)


.LANGUAGE: make


lock_file = lock
script_file = sleep-and-copy.sh
command = flock --no-fork --timeout=0.25 --verbose $(lock_file) /bin/sh $(script_file)


.PHONY: gen
.DEFAULT: gen
gen:
        touch $(sources)
        write_script_file($(script_file))


.PHONY: clean
clean:
        rm -f *$(direct_target_ext)
        rm -f *$(function_target_ext)
        rm -f *$(procedure_target_ext)
        rm -f $(lock_file)


.PHONY: distclean
distclean: clean
        rm -f *$(source_ext)
        rm -f $(script_file)


.PHONY: direct
direct: $(direct_targets)


.PHONY: proc
proc: $(procedure_targets)


.PHONY: func
func: $(function_targets)


##  omake -j1 direct           # Always works ok because of serial execution.
##  omake -j9 direct           # Intentionally fails, which proves the base
                               # case, i.e. the mutex correctly catches for the
                               # tests proper.

%$(direct_target_ext): %$(source_ext)
        $(command) $< $@


f(a_source, a_target) =
        $(command) $(a_source) $(a_target)
        return                 # This `return' statement is necessary to expose
                               # the quirk.  Without it, omake(1) complains
                               # `command not found in PATH: ' when using
                               # function-call syntax (but not procedure-call
                               # syntax).

##  Using procedure-call syntax `function(arg...)' makes the function
##  call *not* interfere with parallelization is expected; it mimics
##  direct execution.
##
##  omake -j1 proc             # Again, always works ok because of serial
##                             # execution.
##  omake -j9 proc             # Fails as expected.

%$(procedure_target_ext): %$(source_ext)
        f($<, $@)              # Respects parallel execution.


##  Using function-call syntax `$(function arg ...)' blocks
##  parallelization.
##
##  omake -j1 func             # Serially builds the targets as its `direct'
##                             # equivalent.
##  omake -j9 func             # Implicitly serializes the build, thus the
                               # build succeeds, where it should fail instead.

%$(function_target_ext): %$(source_ext)
        $(f $<, $@)            # Ignore any returned value from `f'?

To verify a truly parallel command execution at shell level, we call
flock with a command that certainly takes longer than the
timeout of flock. Use omake gen to create a starting
configuration.

Phony target direct evaluates command without any function call
interposed, so if everything is set up correctly

omake -j1 direct

builds all .d files and

omake -j9 direct

fails because the mutex ("lock") cannot be acquired.

Target func does not call command, but function f, which in turn
calls command. Here, both

omake -j1 func

and

omake -j9 func

succeed and build all .f files.

Curiously, in our example, phony target proc, which calls function
f with "procedure call" syntax

f(arg...)

in contrary to "function call" syntax of func

$(f arg...)

duplicates the findings of direct, this is, does not interfere with
parallel execution, as expected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions