.. default-domain:: chpl .. _primers-syncsingle: Sync / Singles ============== `View syncsingle.chpl on GitHub `_ This primer illustrates Chapel's sync and single variables. ``Sync`` and ``single`` are type qualifiers that can be applied to all Chapel primitive types except ``strings``, ``complex``, and ``record``. Sync and single variables have an associated state that is either ``full`` or ``empty``. Single variables are sync variables that can only be written once. If a sync or single variable is declared with an initializing expression, its state is set to ``full`` and its value is that of the expression. Without an initializing expression, a sync or single variable's state is set to ``empty`` and its value is the default value for the base type. .. code-block:: chapel config const n = 7; var sy$: sync int=1; // state = full, value = 1 var si$: single bool; // state = empty, value = false By convention, sync and single variable names end in a ``$`` to add a visual cue indicating that reads and writes are possibly expensive operations and can potentially block the task. For both sync and single variables, the state must be ``empty`` before the value can be written. When the write has completed, the state is set to ``full``. .. code-block:: chapel si$ = true; // state = full, value = true For both sync and single variables, the state must be ``full`` before the value can be read. When the read has completed, the state of a sync variable is set to ``empty``, and the state of a single variable remains ``full``. .. code-block:: chapel var sy = sy$; // sy$: state = empty var si = si$; // si$: state = full If a sync or single variable is not in the correct state for reading (``full``) or writing (``empty``), the current task blocks. The following creates a new task via a :ref:`begin ` statement and declares a variable ``sy`` that is initialized to ``sy$``. The initialization statement will block until ``sy$`` is ``full``. The last statement in the ``begin`` block sets ``done$`` to ``full``. .. code-block:: chapel var done$: sync bool; writeln("Launching new task"); begin { var sy = sy$; // This statement will block until sy$ is full writeln("New task unblocked, sy=", sy); done$ = true; } Recall that execution proceeds immediately following the ``begin`` statement after task creation. .. code-block:: chapel writeln("After launching new task"); When ``sy$`` is written, its state will be set to ``full`` and the blocked task above will continue. .. code-block:: chapel sy$ = n; This next statement blocks until the last statement in the above ``begin`` completes. .. code-block:: chapel done$; Example: simple split-phase barrier for tasks .. code-block:: chapel var count$: sync int = n; // counter which also serves as a lock var release$: single bool; // barrier release coforall t in 1..n { var myc = count$; // read the count, grab the lock (state = empty) if myc!=1 { // still waiting for others write("."); count$ = myc-1; // update the count, release the lock (state = full) // we could do some work while waiting release$; // wait for everyone } else { // last one here release$ = true; // release everyone first (state = full) writeln("done"); } } There are a number of methods defined for sync and single variables. The ``reset()`` method, defined for sync variables, sets the value of the variable to the default value for the type and the state to ``empty``. .. code-block:: chapel sy$.reset(); // value = 0, state = empty The ``isFull`` method returns ``true`` if the sync or single variable is in the ``full`` state, ``false`` otherwise. .. code-block:: chapel writeln(sy$.isFull); writeln(si$.isFull); The ``writeEF()`` method, defined for sync and single variables, blocks until the state is ``empty`` and then assigns the value argument to the variable and then sets the state to ``full``. Assignment of sync and single variables are performed using ``writeEF()``. .. code-block:: chapel sy$.writeEF(2*n); // equivalent to: sy$ = 2*n; The ``readFE()`` method, defined for sync variables, blocks until the state is ``full`` and then reads the value of the variable, sets the state to ``empty``, and then returns the value. Normal reads of sync variables are performed using ``readFE()``. .. code-block:: chapel var sy2 = sy$.readFE(); // equivalent to: var sy2 = sy$; writeln(sy2); The ``readFF()`` method, defined for sync and single variables, blocks until the state is ``full`` and then reads the value of the variable and returns the value. The state remains ``full``. Normal reads of single variables are performed using ``readFF()``. .. code-block:: chapel var si2 = si$.readFF(); writeln(si2); The ``writeXF()`` method, defined for sync variables, assigns the value argument to the variable and then sets the state to ``full``. This method does not block. .. code-block:: chapel sy$.writeXF(3*n); The ``readXX()`` method, defined for sync and single variables, returns the value of the variable regardless of the state. This method does not block and the state is unchanged. .. code-block:: chapel var sy3 = sy$.readXX(); var si3 = si$.readXX(); writeln(sy3); writeln(si3); The ``writeFF()`` method, defined for sync variables, blocks until the state is ``full`` and then and then assigns the value argument to the variable. The state is unchanged. .. code-block:: chapel sy$.writeFF(4*n); sy$.reset(); Sync and single arguments are passed by reference. As a result, the state of the variable does not change. .. code-block:: chapel writeln(sy$.isFull); f_withSyncIntFormal(sy$); writeln(si$.isFull); f_withSingleBoolFormal(si$); sy$ = 4*n; When a sync or single variable is passed as an argument to a function that expects the base type of the variable, the value is read before being passed to the function. Therefore, the task will block until the state of the variable is ``full``. .. code-block:: chapel f_withIntFormal(sy$); f_withBoolFormal(si$); When passing a sync or single variable to a generic formal, whether with a ``ref`` intent or a default intent, the variable is passed by reference. The state of the variable does not change and sync operations are available. .. code-block:: chapel f_withGenericDefaultIntentFormal(sy$); f_withGenericDefaultIntentFormal(si$); f_withGenericRefFormal(sy$); f_withGenericRefFormal(si$); sy$ = 5*n; Currently, sync and single variables cannot be written out directly. We need to extract the value, for example using ``readFE()`` or ``readFF()``. .. code-block:: chapel writeln(sy$.readFE()); writeln(si$.readFF()); Definitions of functions used above .. code-block:: chapel proc f_withSyncIntFormal(x: sync int) { writeln(x.isFull); } proc f_withSingleBoolFormal(x: single bool) { writeln(x.isFull); } proc f_withIntFormal(x: int) { writeln(x); } proc f_withBoolFormal(x: bool) { writeln(x); } proc f_withGenericDefaultIntentFormal(x) { writeln("the full bit is: ", x.isFull); } proc f_withGenericRefFormal(ref x) { writeln("readXX returns: ", x.readXX()); }