diff --git a/TODO b/TODO
index 916a8a4b4287db2a0b5741a23abe3744d9c0cabe..23b75c77756bc8a8f9f8cbbf8ef5e205f6da21bf 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,5 @@
 * Make sure that variables assigned to anonymous PVs are only updated
-  before when() clauses if monitored or if they are a pvGet completions.
+  before when() clauses if monitored or if a pvGet completes.
   This is important so we can exchange an anonymous for a named PV and vice
   versa w/o disrupting the program logic.
 
diff --git a/documentation/Reference.txt b/documentation/Reference.txt
index 615307a934d73b0c03ad69cec649665a36693126..9aaa996cfdedde95be0cbca4ad7aa4b25970564f 100644
--- a/documentation/Reference.txt
+++ b/documentation/Reference.txt
@@ -47,8 +47,6 @@ literals is exactly as in C, including automatic string
 concatenation, character literals, and octal, decimal, and
 hexadecimal integer literals.
 
-Note that array and struct initializers are not supported.
-
 Embedded C Code
 ^^^^^^^^^^^^^^^
 
@@ -75,17 +73,18 @@ Line Markers
 ^^^^^^^^^^^^
 
 .. productionlist::
-   line_marker :: "#" line_number "\n"
-   line_marker :: "#" line_number file_name "\n"
-   line_number :: <non-empty sequence of decimals>
-   file_name :: <like string_literal, without automatic string concatenation>
+   line_marker: "#" line_number "\n"
+   line_marker: "#" line_number file_name "\n"
+   line_number: <non-empty sequence of decimals>
+   file_name: <like string_literal, without automatic string concatenation>
 
-Line markers are interpreted by *snc* exactly as in C, i.e. they
-indicate that the following symbols are really located in the given
-source file (if any) at the given line.
+Line markers are interpreted by *snc* exactly as in C, i.e. they indicate
+that the following symbols are really located in the given source file (if
+any) at the given line.
 
-Note that line_number may only contain decimal numbers, and that
-file_name must be a single string (no automatic string concatenation).
+.. note:: :token:`line_number` may only contain decimal numbers, and
+   :token:`file_name` must be a single string (no automatic string
+   concatenation).
 
 Line markers are typically generated by preprocessors, such as CPP.
 
@@ -98,9 +97,9 @@ Program
    program: "program" `identifier` `program_param` `global_defns` `entry` `state_sets` `exit` `c_codes`
 
 This is the overall structure of an SNL program. After the keyword
-"program" comes the name of the program, followed by an optional
-program parameter, global definitions, an optional entry block, the
-state sets, an optional exit block, and finally some embedded c code.
+"program" comes the name of the program, followed by an optional program
+parameter, global definitions, an optional entry block, the state sets, an
+optional exit block, and finally some embedded C code.
 
 Program Name and Parameter
 ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -118,20 +117,19 @@ time parameters`).
 
 The program name may be followed by an optional string enclosed in
 parentheses. The string content must be a list of comma-separated
-parameters in the same form as they are specified on the command line
-(see :ref:`SpecifyingRunTimeParameters`). Command-line parameters
-override those specified here.
+parameters in the same form as they are specified on the command line (see
+:ref:`run time parameters`).
+
+.. note:: Command-line parameters override those specified in the program.
 
 .. productionlist::
    global_defns: `global_defns` `global_defn`
-   global_defns: `global_defn`
+   global_defns: 
    global_defn: `assign`
    global_defn: `monitor`
    global_defn: `sync`
    global_defn: `syncq`
-   global_defn: `decl`
-   global_defn: `evflag_decl`
-   global_defn: `foreign_decl`
+   global_defn: `declaration`
    global_defn: `option`
    global_defn: `c_code`
 
@@ -148,15 +146,14 @@ Global Entry and Exit Blocks
    exit: "exit" `block`
    exit: 
 
-A state program may specify optional entry code to run prior to state
-set thread creation, and exit code to run prior to thread deletion.
-Both are run in the context of the first state set thread, before the
-other threads are created, resp. after they have been deleted. The
+A state program may specify optional entry code to run prior to running
+the state sets, and exit code to run after all state sets have exited.
+Both are run in the context of the first state set thread. The
 entry or exit code is a regular SNL code block an thus can contain
-local definitions. However, no process variable access functions
-may be called within the entry code.
-
-.. todo:: check if we can relax this restriction
+local definitions. Contrary to older versions, there are no restrictions
+w.r.t. calling built-in pv functions. If the global :option:`+c` is in
+effect, the entry code is executed only after all channels have been
+connected and monitored channels have received their first monitor event.
 
 Global entry and exit blocks should not be confused with the entry and
 exit blocks of a state, which have the same syntax, but are executed at
@@ -170,8 +167,8 @@ Final C Code Block
    c_codes: 
    c_code: `embedded_c_code`
 
-At the end of the program may come any number of embedded
-C code blocks. See `Escape to C Code`_.
+Any number of embedded C code blocks may appear after state sets and the
+optional exit block. See `Escape to C Code`_.
 
 .. _Definitions:
 
@@ -181,12 +178,42 @@ Definitions
 Declarations
 ^^^^^^^^^^^^
 
-Scalars
-~~~~~~~
+.. productionlist::
+   declaration: `type` `init_declarators` ";"
+   init_declarators: `init_declarator`
+   init_declarators: `init_declarators` "," `init_declarator`
+   init_declarator: `declarator`
+   init_declarator: `declarator` "=" `init_expr`
+   declarator: "*" `declarator`
+   declarator: `direct_declarator`
+   direct_declarator: `variable`
+   direct_declarator: "(" `declarator` ")"
+   direct_declarator: `direct_declarator` `subscript`
+   init_expr: "{" `init_exprs` "}"
+   init_expr: `expr`
+   init_exprs: `init_exprs` "," `init_expr`
+   init_exprs: `init_expr`
+   init_exprs: 
+
+Variable declarations are quite similar to C. Since version 2.1 you can
+declare more than one variable in a single declaration (comma
+separated) and add pointer and array markers (subscripts)
+ad libitum as well as initializers.
+
+The remain some limitations:
+
+* arrays must have a defined size (i.e. the integer in the
+  subscript brackets is not optional as in C)
+* you cannot declare new types or type synonyms
+* you cannot declare functions or use function types
+* only certain types are allowed, see below
+
+Some of these restrictions may be lifted in future versions.
+
+Types
+~~~~~
 
 .. productionlist::
-   decl: `type` `identifier` ";"
-   decl: `type` `identifier` "=" `expr` ";"
    type: "char"
    type: "short"
    type: "int"
@@ -198,114 +225,163 @@ Scalars
    type: "float"
    type: "double"
    type: "string"
+   type: "evflag"
+   type: "foreign"
 
-Variable declarations are similar to C but limited in a number of
-ways:
+These are the allowed base types, of which you can declare variables,
+or pointers or arrays etc. The list contains the usual numeric types,
+as well as three types that don't exist in C: "string", "evflag" and
+"foreign". These are explained below.
 
-*  only integers, floating point numbers are allowed as base types
-*  only scalar initialization is permitted
-*  only one variable may be declared per declaration
+Not supported are
 
-The type ``string`` can be viewed as if it were defined in C as::
+* function, struct, union, and enum types
+* typedefs
+* void
+* qualifiers ("static", "extern", "const" etc. other than "unsigned")
 
-   typedef char string[MAX_STRING_SIZE];
+An additional type (probably called "enumeration") is planned, but
+not yet implemented. It will make it easier and more robust to
+interact with process variables of type ``DBF_ENUM`` or ``DBF_MENU``.
 
-where ``MAX_STRING_SIZE`` is supposed to be defined in one of the
-included header files form EPICS base. The exact value depends on
-the EPICS base version (but in all versions is at least 40).
+.. todo:: Allow "const" for non-channel variables.
 
-Arrays and Pointers
-~~~~~~~~~~~~~~~~~~~
+Strings
+~~~~~~~
 
-.. productionlist::
-   decl: `type` `identifier` `subscript` ";"
-   decl: `type` `identifier` `subscript` `subscript` ";"
-   decl: `type` "*" `identifier` ";"
-   decl: `type` "*" `identifier` `subscript` ";"
+The type :token:`string` is defined in C as::
+
+   typedef char string[MAX_STRING_SIZE];
 
-SNL allows declaration of one- or two-dimensional arrays of scalar
-elements, as well as pointers to scalars, and (one-dimensional)
-arrays of such pointers.
+where ``MAX_STRING_SIZE`` is a constant defined in one of the
+included header files from EPICS base. I know of no EPICS version
+where it is different from 40, but to be on the safe side I
+reccommend not to rely too much on the numeric value. (You can use
+``sizeof(string)`` in SNL expressions.)
 
-Note that arrays of strings and event flags are not supported.
+.. note:: In contrast to C, in SNL ``string s`` is *not* a synonym for
+   ``char s[MAX_STRING_SIZE]``, since variables of type :token:`string`
+   are treated differently when it comes to interacting with PVs: the
+   former gets requested with type DBR_STRING and a count of one, while
+   the latter gets requested with type DBR_CHAR and a count of
+   ``MAX_STRING_SIZE``.
 
 Event Flags
 ~~~~~~~~~~~
 
-.. productionlist::
-   evflag_decl: "evflag" `identifier` ";"
+Event flags are values of an abstract data type with four operations
+defined on them: :c:func:`efSet`, :c:func:`efClear`, :c:func:`efTest`,
+and :c:func:`efTestAndClear`.
 
-This declares an event flag, see :ref:`EventFlags`.
+An event flag ``e`` can act as a binary semaphore, allowing exactly one
+state set to continue, if ``when(efTestAndClear(e))`` is used to wait
+and ``efSet(e)`` to signal. Event flags can be coupled to changes of a
+PV using the :token:`sync` clause.
 
+You cannot declare arrays of or pointers to event flags, since
+event flags are not translated to C variables in your program.
 
-Foreign Declarations
-~~~~~~~~~~~~~~~~~~~~
+See :ref:`EventFlags`.
 
-.. productionlist::
-   foreign_decl: "declare" `identifier` ";"
+.. _foreign entities:
+
+Foreign Entities
+~~~~~~~~~~~~~~~~
 
-This declares the named C variable or CPP macro, so the SNL compiler
-knows about it. No warning will be issued if such a variable (or macro)
-is used in the program.
+The pseudo type "foreign" is used to declare a C variable or
+struct tag or CPP macro that have been defined outside the SNL program.
+No warning will be issued if such a variable, struct tag, or macro is
+used in the program even if warnings are enabled.
 
-.. _VariableScope:
+You cannot declare arrays of or pointers to foreign entities.
 
-Variable Scope
-^^^^^^^^^^^^^^
+.. _variable scope:
+
+Variable Scope and Life Time
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-Global variables, that is, variables declared at the top-level (before
+Variables are statically scoped, i.e. they are visible to the program
+only inside the smallest enclosing :token:`block`. This is similar to
+C and other statically scoped programming languages.
+
+However, contrary to other languages, life time does not strictly
+follow static scope.
+
+Global variables, i.e. variables declared at the top-level (before
 any state sets) are visible throughout the program, and persist as long
-as the program is running. We say they have *program life time*.
+as the program is running. We say they have *global life time*.
 
-Local variable declarations come in two flavours, depending on where
-they appear:
+For local variables, life time depends on where they are declared:
 
-#. Variables of *program life time* are global variables and those
+#. Variables of *global life time* are global variables and those
    which are local to a state set or a state clause. Only variables of
    this sort can be assigned to a process variable, monitored, synced
    etc.
-#. Variables declared in any other block have lifetime *limited to the
-   enclosing block*, they disappear when the block exits, just as block
+#. Variables declared in any other block have life time *limited to the
+   enclosing block*; they disappear when the block exits, just as block
    local variables in C. They can *not* be assigned to process
-   variables.
+   variables. If they have an initializer, the variable will be
+   initialized every time the block is entered. If not, their value is
+   undefined until they become initialized e.g. by an assignment.
+
+.. note:: In :ref:`safe mode` changes made to a global variable are *not*
+   immediately visible to other state sets.
 
 Process Variables
 ^^^^^^^^^^^^^^^^^
 
-assign
-~~~~~~
+The following are definitions that allow program variables to interact
+with externally defined process variables.
+
+assign/connect
+~~~~~~~~~~~~~~
 
 .. productionlist::
-   assign: "assign" `identifier` `to` `string` ";"
-   assign: "assign" `identifier` `subscript` `to` `string` ";"
-   assign: "assign" `identifier` `to` "{" `strings` "}" ";"
+   assign: `connect` `variable` `to` `string` ";"
+   assign: `connect` `variable` `subscript` `to` `string` ";"
+   assign: `connect` `variable` `to` "{" `strings` "}" ";"
+   assign: `connect` `variable` ";"
+   connect: "assign"
+   connect: "connect"
    to: "to"
-   to:
+   to: 
    strings: `strings` "," `string`
    strings: `string`
    subscript: "[" `integer_literal` "]"
+   variable: `identifier`
 
-This assigns program variables to named process variables.
+This assigns or connects program variables to named or anonymous process
+variables.
 
-There are three variants of the :token:`assign` statement. The first one
+There are four variants of the :token:`assign` statement. The first one
 assigns a (scalar or array) variable to a single process variable.
 The second one assigns a single element of an array variable to a
 single process variable. The third one assigns elements of an array
 variable to separate process variables.
 
-Assigned variables must be of program lifetime, see
-:ref:`VariableScope`. Assigned variables, or separately assigned
+The fourth one is new in version 2.1; it serves as an abbreviation for
+the first variant, in the special case where the PV name is empty (``""``).
+
+Assigned variables must be of global life time, see
+:ref:`variable scope`. Assigned variables, or separately assigned
 elements of an array, can be used as argument to built-in ``pvXXX``
 procedures (see `Built-in Functions`_). This is the primary means of
 interacting with process variables from within a SNL program.
 
+Only certain types of variables can be assigned to a PV: allowed
+are numeric types (char, short, int, long, and their unsigned variants)
+and strings (these are sometimes referred to as *scalar types*), as well
+as one or two dimensional arrays of these.
+
 The process variable name may contain macro names enclosed in
 braces, as in ``"{sys}{sub}voltage"``. Macros are named following
 the same rules as C language variables. Macros are defined via
 command line argument, or program parameter (see `Program Name and
-Parameter`_). If process variable name is an empty string, then no
-actual assignment is performed, but the variable is marked for
-potential (dynamic) assignment with :c:func:`pvAssign`.
+Parameter`_). If the process variable name is an empty string, then no
+actual assignment to any process variable is performed, but the variable
+is marked for potential (dynamic) assignment with :c:func:`pvAssign`.
+In :ref:`safe mode` (new in version 2.1) such variables are assigned
+to :ref:`anonymous pvs`.
 
 An array variable assigned wholesale to one process variable (using
 the first syntactic variant above) or an element of a
@@ -313,16 +389,29 @@ two-dimensional variable assigned to an array process variable
 (using the second syntactic variant) will use either the length of
 the array (resp. sub-array) or the native count for the underlying
 variable, whichever is smaller, when communicating with the
-underlying process variable. The native count is determined when the
-initial connection is established.
+underlying process variable. The native count is determined when a
+connection is established. For anonymous PVs, the length of the array is
+used.
 
 Pointer types may not be assigned to process variables.
 
+.. note:: An :token:`assign` clause using an empty string for the PV name
+   is interpreted differently in :ref:`safe mode`.
+
+The keyword "connect" is new in version 2.1 and is synonymous
+with "assign". It is offered as an alternative because the traditional
+term "assign" is too easily confused with the assignment
+statement (``var = expression;``), even though these notions have
+nothing in common. However, for compatibility and the
+fact that most SNL programmers are used to it, I have refrained from
+making any disruptive change here. Thus, the documentation still talks
+about "assigned variables", the "assign clause" etc.
+
 monitor
 ~~~~~~~
 
 .. productionlist::
-   monitor: "monitor" `identifier` `opt_subscript` ";"
+   monitor: "monitor" `variable` `opt_subscript` ";"
    opt_subscript: `subscript`
    opt_subscript: 
 
@@ -335,20 +424,28 @@ sync
 ~~~~
 
 .. productionlist::
-   sync: "sync" `identifier` `opt_subscript` `to` `identifier` ";"
+   sync: "sync" `variable` `opt_subscript` `to` `event_flag` ";"
+   syncq: "syncQ" `variable` `opt_subscript` `to` `event_flag` `syncq_size` ";"
+   event_flag: `identifier`
+
+An event flag can be associated with an :token:`assign`\ed program
+variable. The variable may be an array, even one associated with
+several process variables. Also, multiple :token:`sync` clauses
+can mention the same event flag but different program variables.
 
-An event flag can be associated with an SNL variable (which may be
-an array, and thus associated with several process
-variables). When a monitor is posted on any of the associated
-process variables, the corresponding event flag is set (even
-if it was already set).
+When a monitor is posted on any of the associated
+process variables, or when an asynchronous get operation completes,
+the corresponding event flag is set.
 
+.. note:: Since version 2.1 it is allowed to :token:`sync` an event flag to
+   more than one variable and that such an association can be made and
+   removed at runtime using :c:func:`pvSync`.
 
 syncQ
 ~~~~~
 
 .. productionlist::
-   syncq: "syncQ" `identifier` `opt_subscript` `to` `identifier` `syncq_size` ";"
+   syncq: "syncQ" `variable` `opt_subscript` `to` `event_flag` `syncq_size` ";"
    syncq_size: `integer_literal`
    syncq_size: 
 
@@ -359,10 +456,11 @@ queue size defaults to 100 but can be overridden on a per-queue
 basis. When a monitor is posted on any of the associated process
 variables, the variable's value is written to the end of the
 queue and the corresponding event flag is set. If the queue is
-already full, the last entry is overwritten. Only scalar items can
-be accommodated in the queue (if the variable is array-valued, only
-the first item will be saved). The :c:func:`pvGetQ` function reads items
-from the queue.
+already full, the last entry is overwritten. The :c:func:`pvGetQ`
+function reads items from the queue.
+
+Since version 2.1 you can use "syncq" (all lower case) as keyword
+instead of "syncQ".
 
 .. _option definition:
 
@@ -413,7 +511,7 @@ State Set Local Definition
    ss_defn: `monitor`
    ss_defn: `sync`
    ss_defn: `syncq`
-   ss_defn: `decl`
+   ss_defn: `declaration`
 
 Inside state sets are allowed variable declarations and process variable
 definitions (:token:`assign`, :token:`monitor`, :token:`sync`, and
@@ -447,7 +545,7 @@ State Local Definition
    state_defn: `monitor`
    state_defn: `sync`
    state_defn: `syncq`
-   state_defn: `decl`
+   state_defn: `declaration`
    state_defn: `option`
 
 .. _StateOption:
@@ -462,10 +560,9 @@ The state options are:
 
 :option:`+t`
    Reset delay timers each time the state is entered, even if entered
-   from the same state.
+   from the same state. This is the default.
 
 :option:`-t`
-
    Don't reset delay timers when entering from the same state. In other
    words, the :c:func:`delay` function will return whether the specified
    time has elapsed from the moment the current state was entered from a
@@ -490,13 +587,13 @@ The state options are:
 
 For example::
 
-  state low {
-    option -e; /* Do entry{} every time ... */
-    option +x; /* but only do exit{} when really leaving */
-    entry { ... }
-    ...
-    exit { ... }
-  }
+   state low {
+      option -e; /* Do entry{} every time ... */
+      option +x; /* but only do exit{} when really leaving */
+      entry { ... }
+      ...when ()...
+      exit { ... }
+   }
 
 .. _state entry and exit blocks:
 
@@ -517,9 +614,8 @@ conditions for state transitions are evaluated.
 Exit blocks are executed when the state is left, after the transition
 block that determines the next state.
 
-Note that state options can be used to control whether entry/exit
-blocks get executed even if the new state is the same as the current
-one.
+.. note:: State options can be used to control whether entry/exit blocks
+   get executed even if the new state is the same as the current one.
 
 Transitions
 ~~~~~~~~~~~
@@ -544,7 +640,7 @@ Block
    block: "{" `block_defns` `statements` "}"
    block_defns: `block_defns` `block_defn`
    block_defns: 
-   block_defn: `decl`
+   block_defn: `declaration`
    block_defn: `c_code`
 
 Blocks are enclosed in matching (curly) braces. They may contain any
@@ -576,12 +672,12 @@ Statements
 As can be seen, most C statements are supported. Not supported are
 the switch/case statement and the return statement.
 
-The *state change statement* is not borrowed from C; it is only available in a
-state transition action block (i.e. after a when) and has the effect of
-immediately jumping out of the action block, overriding the statically
+The *state change statement* is not borrowed from C; it is only available
+in a state transition action block (i.e. after a when) and has the effect
+of immediately jumping out of the action block, overriding the statically
 specified new state (given after the block) with its state argument.
 
-.. todo:: find out of the compiler guards against state changes in other
+.. todo:: find out if the compiler guards against state changes in other
    action blocks.
 
 Expressions
@@ -623,12 +719,14 @@ Primary Expression Operators
 These are: function call, array subscript, record selection, pointer to
 record selection, and postfix operators (increment and decrement).
 
-Note that :token:`exit` is listed explicitly because
-it is a keyword, not an identifier, but can also be used as a function.
+.. note:: :token:`exit` is listed explicitly because it is a keyword, not
+   an identifier, but can also be used as a function.
 
-Note also that SNL makes no use of the semantics of structure member
-access (a side-effect that you may notice is that the state notation
-compiler will warn that structure tags are unused variables).
+.. note:: SNL makes no use of the semantics of structure member access and
+   that struct tags are treated as expressions (variables, in fact). A
+   side-effect is that *snc* will warn that structure tags are "used but
+   not declared", which can be silenced with a :ref:`foreign entities`
+   declaration, just as with variables.
 
 Unary Prefix Operators
 ~~~~~~~~~~~~~~~~~~~~~~
@@ -716,10 +814,12 @@ interpretation of the parameters to these functions. Therefore,
 some are either not available through escaped C code or their use
 in escaped C code is subject to special rules.
 
-An argument specified as *assigned_var* refers to any SNL variable that is
-assigned to a process variable. When using such a variable as a function
-argument, the function is automatically given access to the details of the
-underlying process variable.
+An argument specified as *assigned_var* refers to any SNL variable or
+array element that has been assigned to a (possibly anonymous) process
+variable. The compiler statically checks this. Similarly, *queued_var*
+refers to a variable that has been assigned to a PV and also associated
+with an event queue with the :token:`syncq` clause. This is also
+statically checked by the compiler.
 
 Several of these functions are primarily intended to be called only from
 :token:`when` clauses or only from action code. It is safe to call them in
@@ -728,16 +828,13 @@ calling :c:func:`delay` in action code does *not* introduce a delay at this
 point. Later versions of *snc* may output a warning or even stop with an
 error.
 
-Functions returning ``int`` actually return a :ref:`pvStat` error
-code unless otherwise spacified.
-
 delay
 ^^^^^
 
 .. c:function::
-  int delay(double delay_in_seconds)
+   boolean delay(double delay_in_seconds)
 
-Returns ``TRUE`` if the specified time has elapsed since entering the
+Returns whether the specified time has elapsed since entering the
 state. It should be used only within a :token:`when` expression.
 
 The :option:`-t` state option (see `State Option`_) controls whether the
@@ -749,57 +846,84 @@ pvPut
 ^^^^^
 
 .. c:function::
-  int pvPut(assigned_var)
-  int pvPut(assigned_var, SYNC)
-  int pvPut(assigned_var, ASYNC)
+  pvStat pvPut(assigned_var)
+  pvStat pvPut(assigned_var, SYNC)
+  pvStat pvPut(assigned_var, ASYNC)
+
+Puts (or writes) the value of an SNL variable to the underlying process
+variable. Returns the status from the PV layer (e.g. ``pvStatOK`` for
+success).
+
+If the variable is an array and associated with multiple PVs, then all the
+elements of the array are written to their respective PVs.
 
-Puts (or writes) the value of an SNL variable to the
-underlying process variable. Returns the status
-from the PV layer (e.g. ``pvStatOK`` for success).
+.. todo:: Seems this was never true. I like it but it breaks
+   compatibility. This is just too bad. (Similar for pvGet).
 
-By default, :c:func:`pvPut` does not wait for the put to be complete;
+By default, :c:func:`pvPut` is un-confirmed "fire and forget";
 completion must be inferred by other means. The optional ``SYNC``
 argument causes it to block on completion with a hard-coded timeout
-of 10s. The optional ``ASYNC`` argument allows completion to be
-checked via a subsequent call to :c:func:`pvPutComplete` (typically in a
-:token:`when` clause).
+of 10s. The optional ``ASYNC`` argument allows the program to
+continue but still check for completion via a subsequent call to
+:c:func:`pvPutComplete` (typically in a :token:`when` clause).
+
+Note that SNL allows only one pending :c:func:`pvPut` per variable and state set
+to be active. As long as a ``pvPut(var,ASYNC)`` is pending completion,
+further calls to ``pvPut(var,ASYNC)`` from the same state set immediately
+fail and an error message is printed; whereas further calls to ``pvPut(var,SYNC)``
+are *delayed* until the previous operation completes. Thus ::
+
+   pvPut(var,ASYNC);
+   pvPut(var,SYNC);
+
+and ::
+
+   pvPut(var,SYNC);
+   pvPut(var,SYNC);
 
-Note that, when using channel access, the ``SYNC`` and ``ASYNC``
-arguments result in use of ``ca_put_callback``; if neither
-optional argument is specified, ``ca_put`` is called as with
-previous versions.
+are equivalent. Whereas in ::
+
+   pvPut(var,ASYNC);
+   pvPut(var,ASYNC);
+
+the second pvPut fails.
+
+In safe mode, :c:func:`pvPut` can be used with anonymous PVs (variables assigned
+to "") to communicate between state sets. This makes sense only with global
+variables as only those can be referenced in more than one state set. The
+behaviour for anonymous PVs exactly mirrors that of named PVs. Note, however, that
+for anonymous PVs completion is always immediate, so the ``SYNC`` and ``ASYNC``
+options are not very useful.
 
 pvPutComplete
 ^^^^^^^^^^^^^
 
 .. c:function::
-  int pvPutComplete(assigned_var)
-  int pvPutComplete(array_name)
-  int pvPutComplete(array_name, long any)
-  int pvPutComplete(array_name, long any, long *pComplete)
+  boolean pvPutComplete(assigned_var)
+  boolean pvPutComplete(assigned_var, boolean any)
+  boolean pvPutComplete(assigned_var, boolean any, boolean *pComplete)
 
-Returns ``TRUE`` if the last put of this process variable has completed.
-This call is appropriate only if :c:func:`pvPut` 's optional ``ASYNC``
-argument was used.
+Returns whether the last asynchronous :c:func:`pvPut` to this process
+variable has completed.
 
 The first form is appropriate when the SNL variable is a scalar.
 However, it can also be an array (each of whose elements may be
 assigned to a different process variable). In this case, the
-single argument form returns ``TRUE`` if the last puts of all the
-elements of the array have completed (the missing arguments are
-implicitly ``0`` and ``NULL`` respectively). If ``any`` is ``TRUE``, then
-the function returns ``TRUE`` if any put has completed since the last
-call. If ``pComplete`` is non-NULL, it should be a ``long`` array of
-the same length as the SNL variable and its elements will be set to
-``TRUE`` if and only if the corresponding put has completed.
+single argument form returns whether *all* :c:func:`pvPut` operations
+for elements of the array have completed (missing arguments are
+implicitly set to ``0``). If ``any`` is ``TRUE``, then
+the function returns whether *any* put has completed since the last
+call. If ``pComplete`` is non-NULL, it should be an array of
+at least the array length of the SNL variable and its elements indicate
+whether the corresponding :c:func:`pvPut` has completed.
 
 pvGet
 ^^^^^
 
 .. c:function::
-  int pvGet(assigned_var)
-  int pvGet(assigned_var, SYNC)
-  int pvGet(assigned_var, ASYNC)
+  pvStat pvGet(assigned_var)
+  pvStat pvGet(assigned_var, SYNC)
+  pvStat pvGet(assigned_var, ASYNC)
 
 Gets (or reads) the value of an SNL variable from the underlying process
 variable. Returns the status from the PV layer (e.g.
@@ -818,84 +942,113 @@ pvGetComplete
 ^^^^^^^^^^^^^
 
 .. c:function::
-  int pvGetComplete(assigned_var)
+  boolean pvGetComplete(assigned_var)
 
-Returns ``TRUE`` if the last get of this process variable has completed,
+Returns whether the last get of this process variable has completed,
 i.e. the value in the variable is current. This call is appropriate only if
 the asynchronous (:option:`+a`) compile option is specified or
 :c:func:`pvGet` 's optional ``ASYNC`` argument was used.
 
+In safe mode, the update of the variable with the value from the PV layer
+happens as a side effect of this call (if TRUE is returned).
+
 Unlike :c:func:`pvPutComplete`, :c:func:`pvGetComplete` doesn't support
 arrays.
 
+..todo:: check if this is correct; i think it is not.
+
 pvGetQ
 ^^^^^^
 
 .. c:function::
-  int pvGetQ(assigned_var)
-  int pvGetQ(array_name)
-
-Removes the oldest value from a SNL variable's monitor queue (the variable
-should have been associated with a queue and an event flag via the
-:token:`syncq` statement) and updates the corresponding SNL variable.
-Despite its name, this function is really closer to
-:c:func:`efTestAndClear` than it is to :c:func:`pvGet`. It returns
-``TRUE`` if the queue was not empty.
+  boolean pvGetQ(queued_var)
 
-If the SNL variable is an array then the behavior is the same
-regardless of whether the array name or an array element name is
-specified. This is because a single queue is associated with the
-entire array.
+If the queue associated with this variable is not empty, remove its first
+(oldest) value and update the variable with it. Returns whether there was
+an element in the queue (and the variable got updated).
 
 pvFreeQ
 ^^^^^^^
 
 .. c:function::
-  void pvFreeQ(assigned_var)
+  void pvFreeQ(queued_var)
 
-Deletes all entries from an SNL variable's queue and
-clears the associated event flag (the variable should have been
-associated with a queue and an event flag via the :token:`syncq`
-statement).
+Deletes all entries from a queued variable's queue and
+clears the associated event flag. Since version 2.1 queue
+elements are no longer dynamically allocated, so this
+simply flushes the queue.
 
-As with :c:func:`pvGetQ`, if the SNL variable is an array then the
-behavior is the same regardless of whether the array name or an
-array element name is specified.
+pvFlushQ
+^^^^^^^^
+
+.. c:function::
+  void pvFlushQ(queued_var)
+
+Flush the queue associated with this variable, so it is empty afterwards.
+This function is new in version 2.1. and is an alias for :c:func:`pvFreeQ`.
+
+pvAssign
+^^^^^^^^
+
+.. c:function::
+  pvStat pvAssign(assigned_var, process_variable_name)
+
+Assigns or re-assigns the SNL variable ``var`` to
+``process_variable_name``. If ``process_variable_name`` is an empty
+string then ``assigned_var`` is de-assigned (not associated with any process
+variable). In safe mode, it causes assignment to an anonymous PV.
+
+As usual, ``assigned_var`` can also be an array element.
+
+.. note:: :c:func:`pvAssign` can only be called on variables (or array
+   elements) that have been statically marked as process variables using
+   the :token:`assign` syntax. An empty string may be used for the initial
+   assignment, or (from version 2.1. onward) the simplified form
+   ``assign var``.
+
+.. note:: If a variable gets de-assigned from a non-empty to an empty
+   name, the corresponding channel is destroyed, which may result in
+   memory being dynamically allocated gets freed. If your
+   system cannot handle dynamic memory allocation without fragmentation,
+   care should be taken that assignment and de-assignment do not
+   alternate too often.
+
+A better name for this function would be ``pvReassign``.
 
 pvMonitor
 ^^^^^^^^^
 
 .. c:function::
-  int pvMonitor(assigned_var)
+  pvStat pvMonitor(assigned_var)
+
+Initiates a monitor on the underlying process variable.
 
-Initiates a monitor on the underlying process
-variable.
+See :token:`monitor` clause.
 
 pvStopMonitor
 ^^^^^^^^^^^^^
 
 .. c:function::
-  int pvStopMonitor(assigned_var)
+  pvStat pvStopMonitor(assigned_var)
 
-Terminates a monitor on the underlying process
-variable.
+Terminates a monitor on the underlying process variable.
 
-pvFlush
-^^^^^^^
+pvSync
+^^^^^^
 
 .. c:function::
-  void pvFlush()
+  void pvSync(assigned_var, event_flag)
 
-Causes the PV layer to flush its input-output buffer.
-It just might be needed if performing asynchronous operations
-*within* an action block (note that the buffer is always flushed on
-exit from an action block).
+Synchronizes a variable with an event flag, or removes such a
+synchronization if ``event_flag`` is zero.
+
+See :token:`sync` clause.
 
 pvCount
 ^^^^^^^
 
 .. c:function::
-  int pvCount(assigned_var)
+  unsigned pvCount(assigned_var)
 
 Returns the element count associated with the process variable.
 
@@ -931,58 +1084,54 @@ Therefore, variable declarations for this type should be in escaped
 C code. This will generate a compiler warning, which can be
 ignored.
 
-pvAssign
-^^^^^^^^
-
-.. c:function::
-  int pvAssign(var, process_variable_name)
-
-Assigns or re-assigned the SNL variable ``var`` to
-``process_variable_name``. If ``process_variable_name`` is an empty
-string then ``var`` is de-assigned (not associated with any process
-variable).
-
-As usual, ``var`` can also be an array element.
-
-Note: :c:func:`pvAssign` can only be called on variables (or array elements)
-that have been statically marked as process variables using the
-:token:`assign` syntax. An empty string may be used for the initial
-assignment.
-
-A better name for this function would be ``pvReassign``.
-
 pvAssigned
 ^^^^^^^^^^
 
 .. c:function::
-  int pvAssigned(assigned_var)
+  boolean pvAssigned(assigned_var)
 
-Returns ``TRUE`` if the SNL variable is currently
+Returns whether the SNL variable is currently
 assigned to a process variable.
 
 pvConnected
 ^^^^^^^^^^^
 
 .. c:function::
-  int pvConnected(assigned_var)
+  boolean pvConnected(assigned_var)
 
-Returns ``TRUE`` if the underlying process
+Returns whether the underlying process
 variable is currently connected.
 
 pvIndex
 ^^^^^^^
 
 .. c:function::
-  int pvIndex(assigned_var)
+  unsigned pvIndex(assigned_var)
 
 Returns the index associated with a process
 variable. See `User Functions within the State Program`_.
 
+pvFlush
+^^^^^^^
+
+.. c:function::
+  void pvFlush()
+
+Causes the PV layer to flush its send buffer. This is only
+necessary if you need to make sure that CA operations are
+started *before* the action block finishes. The buffer is
+always automatically flushed before the sequencer waits
+for events, that is, after a state's entry block is executed
+(or on entry to the state if there is no entry block).
+The buffer is also flushed after initiating a synchronous
+operation that waits for a callback (i.e. ``pvPut(var,SYNC)``
+and ``pvGet(var,SYNC)``).
+
 pvChannelCount
 ^^^^^^^^^^^^^^
 
 .. c:function::
-  int pvChannelCount()
+  unsigned pvChannelCount()
 
 Returns the total number of process variables
 associated with the state program.
@@ -991,14 +1140,15 @@ pvAssignCount
 ^^^^^^^^^^^^^
 
 .. c:function::
-  int pvAssignCount()
+  unsigned pvAssignCount()
 
 Returns the total number of SNL variables in this
 program that are assigned to underlying process variables.
-Note: if all SNL variables are assigned then the following
+
+For instance, if all SNL variables are assigned then the following
 expression is ``TRUE``::
 
-  pvAssignCount() == pvChannelCount()
+   pvAssignCount() == pvChannelCount()
 
 Each element of an SNL array counts as variable for the purposes of
 :c:func:`pvAssignCount`.
@@ -1007,13 +1157,15 @@ pvConnectCount
 ^^^^^^^^^^^^^^
 
 .. c:function::
-  int pvConnectCount()
+  unsigned pvConnectCount()
 
 Returns the total number of underlying process
-variables that are connected. Note: if all variables are connected
-then the following expression is ``TRUE``::
+variables that are connected.
 
-  pvConnectCount() == pvChannelCount()
+For instance, if all assigned variables are connected then the following
+expression is ``TRUE``::
+
+   pvConnectCount() == pvAssignCount()
 
 efSet
 ^^^^^
@@ -1021,35 +1173,35 @@ efSet
 .. c:function::
   void efSet(event_flag)
 
-Sets the event flag and causes the execution of the
-:token:`when` statements for all state sets that are pending on this event
-flag.
+Sets the event flag and causes evaluation of the :token:`when`
+clauses for all state sets that are pending on this event flag.
 
 efTest
 ^^^^^^
 
 .. c:function::
-  int efTest(event_flag)
+  boolean efTest(event_flag)
 
-Returns ``TRUE`` if the event flag was set.
+Returns whether the event flag was set.
 
 efClear
 ^^^^^^^
 
 .. c:function::
-  int efClear(event_flag)
+  boolean efClear(event_flag)
+
+Clears the event flag and returns whether the event flag was set.
+Also and causes evaluation of the :token:`when` clauses for all
+state sets that are pending on this event flag.
 
-Clears the event flag and causes the execution of the
-:token:`when` statements for all state sets that are pending on this event
-flag.
 
 efTestAndClear
 ^^^^^^^^^^^^^^
 
 .. c:function::
-  int efTestAndClear(event_flag)
+  boolean efTestAndClear(event_flag)
 
-Clears the event flag and returns ``TRUE`` if the event
+Clears the event flag and returns whether the event
 flag was set. It is intended for use within a :token:`when` clause.
 
 macValueGet
@@ -1062,6 +1214,129 @@ Returns a pointer to a string that is the value for
 the specified macro name. If the macro does not exist, it returns
 ``NULL``.
 
+
+.. _safe mode:
+
+Safe Mode
+---------
+
+Since version 2.1 SNL code can be interpreted in safe mode. This must be
+enabled with the :option:`+s` option, because it changes the way variables
+are handled and is thus not fully backwards compatible. It should, however,
+be easy to adapt existing programs to safe mode by making communication
+between state sets explicit. New programs should no longer use the
+traditional unsafe mode.
+
+In the traditional (unsafe) mode, variables are *not* protected against
+access from concurrently running threads. Concurrent access to SNL
+variables was introduced in version 2.0, when implementation of the PV
+layer switched from the traditional single threaded CA mode ("preemptive
+callbacks disabled") to the multi-threaded mode ("preemptive callbacks
+enabled") in order to support more than one state set per program. This
+could result in data corruption for variables that are not read and written
+atomically, the details of which are architecture and compiler dependent
+(i.e. plain ``int`` is typically atomic, whereas double is problematic on
+some, string and arrays on almost all architectures/compilers). Even for
+plain ``int`` variables, read-modify-write cycles (like ``v++``) cannot be
+guaranteed to have any consistent result. Furthermore, conditions that have
+been met inside a :token:`when` clause cannot be relied upon to still hold
+inside the associated action block.
+
+Concurrent access to SNL variables happens when
+
+* multiple state sets access the same variable, or
+* variables are updated from the PV layer due to monitors
+  and asynchronous get operations.
+
+While it is possible to avoid the first case by careful coding (using e.g.
+event flags for synchronization) it is not possible to guard against the
+second case as these events can interrupt action statements at any time.
+
+Safe mode solves all these problems by changing the way variables,
+particularly global variables, are interpreted. In safe mode, all variables
+--except event flags-- are interpreted as if they were *local to the state
+set*. This means that setting a variable (even a global variable) in one
+state set does *not* automatically change its value as seen by other state
+sets. State sets are effectively isolated against each other, and all
+communication between them must be explicit. They are also isolated against
+updates by callbacks from the PV layer except at those points where they
+don't do anything i.e. when they wait for events in a :token:`when` clause.
+In safe mode, variable values get updated right before the conditions are
+evaluated, or when explicitly calling synchronization functions like
+:c:func:`pvGetComplete` or :c:func:`pvGet` (the latter only if called in
+synchronous mode); see below for details.
+
+Safe mode implies reentrant mode, see :ref:`reentrant option`.
+
+.. _anonymous pvs:
+
+Anonymous PVs
+^^^^^^^^^^^^^
+
+Explicit communication between state sets can use either event flags or
+variables. Event flags work exactly the same as in traditional mode.
+
+To use a variable for communication it must have been *assigned* with an
+:token:`assign` statement. You can then use :c:func:`pvPut`,
+:c:func:`pvGet`, :c:func:`pvMonitor`, and most of the other built-in PV
+functions, *regardless of whether the PV name is the empty string (``""``)
+or not*. If it is the empty string, the PV functions behave as if the
+assignment had been to an internal, anonymous 'pseudo PV' that behaves
+similar to an external 'real' PV. The variant ``assign var;`` of the
+:token:`assign` statement has been introduced as an abbreviation for
+``assign var to "";``.
+
+
+For instance, with the declaration ::
+
+  int var;
+  assign var;
+
+the action statement ::
+
+  pvPut(var)
+
+makes the value of ``var`` available to other state sets. They will,
+however, not see the new value until they issue either a (synchronous)
+:c:func:`pvGet`, or the variable is declared as monitored and state
+change conditions are evaluated.
+
+The action ::
+
+  pvGet(var, SYNC)
+
+updates ``var`` immediately with whatever has been written to it
+previously via :c:func:`pvPut` by some other state set. Whereas ::
+
+  pvGet(var, ASYNC)
+
+has no immediate effect on the variable ``var``. Instead, ``var``
+will be updated only if the code calls :c:func:`pvGetComplete`
+(and it returns ``TRUE``), or when state change conditions are
+evaluated the next time.
+
+.. note:: This behaviour is exactly the same as with external PVs.
+
+.. note:: Using ``SYNC`` or ``ASYNC`` with anonymous PVs is not very
+   useful since all operations complete immediately.
+
+Event flags can be :token:`sync`\ed to anonymous PVs and will behave
+as with external PVs.
+
+Event Flags in Safe Mode
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Apart from the :token:`sync` and :token:`syncq` features, an event flag
+behaves like an anonymous PV of boolean type with a monitor. We could
+have defined (pseudo code)::
+
+  efSet(e)          { e = TRUE; pvPut(e); }
+  efClear(e)        { e = FALSE; pvPut(e); }
+  efTest(e)         { pvGet(e,SYNC); return e; }
+  efTestAndClear(e) { pvGet(e,SYNC);
+    if(e) { efClear(e); return TRUE} else return FALSE; }
+
+
 C Compatibility Features
 ------------------------
 
@@ -1139,7 +1414,7 @@ Variable Modification for Reentrant Option
 
 If the reentrant option (:option:`+r`) is specified to SNC then all
 variables are made part of a structure. Suppose we have the
-following declarations in the SNL::
+following top-level declarations in the SNL program::
 
   int sw1;
   float v5;
@@ -1159,14 +1434,25 @@ has the following type::
 
   struct UserVar *pVar;
 
-Reference to variable ``sw1`` is made as::
+Reference to variable ``sw1`` is made as ::
 
   pVar->sw1
 
 This conversion is automatically performed by the SNC for all SNL
 statements, but you will have to handle escaped C code yourself.
 
+.. note:: :ref:`safe mode` (enabled with the :option:`+s` option) implies
+   reentrant mode. In safe mode, each state set has its own copy of the
+   ``UserVar`` struct. You can operate on its members in any way you like,
+   including taking the address of variables and passing them to C
+   functions.
+
+
 Syntax Summary
 --------------
 
+Here is a complete summary of all syntax rules. It is guaranteed to
+be up-to-date as it is automatically generated from the parser
+generator specification.
+
 .. include:: bnf.inc
diff --git a/src/dev/devSequencer.c b/src/dev/devSequencer.c
index abfd3b695e1ae7d249eff987a63cd49573050f0f..87f9beee391c4830b5a9535b0b968065c561e84d 100644
--- a/src/dev/devSequencer.c
+++ b/src/dev/devSequencer.c
@@ -165,7 +165,6 @@ typedef struct {
             (TARGET) = (SOURCE); \
             (UPDATEFLAG) = updated; \
         }
-#define NEW(type) (type*)calloc(1,sizeof(type))
 
 LOCAL seqShowScanPvt* seqShowScanPvtInit(struct link* link)
 {
@@ -175,7 +174,7 @@ LOCAL seqShowScanPvt* seqShowScanPvtInit(struct link* link)
     int              argN  = 0;
     int              i     = 0;
 
-    pvtPt = NEW(seqShowScanPvt);
+    pvtPt = new(seqShowScanPvt);
     if(!pvtPt) return NULL;
 
     pvtPt->updateFlag = 1;
diff --git a/src/seq/seqCom.h b/src/seq/seqCom.h
index e1c0fde42fa4110f0add4d7bdab20aa3d6bf5dd4..dab89d34a1deb3ece21bf96d5d3d0c4150ef45c9 100644
--- a/src/seq/seqCom.h
+++ b/src/seq/seqCom.h
@@ -90,8 +90,8 @@ typedef unsigned DELAY_ID;		/* identifier for a delay */
 typedef int boolean;
 
 /* Prototypes for functions generated by snc */
-typedef void ACTION_FUNC(SS_ID ssId, USER_VAR *var, int transNum, short *nextState);
-typedef boolean EVENT_FUNC(SS_ID ssId, USER_VAR *var, short *transNum, short *nextState);
+typedef void ACTION_FUNC(SS_ID ssId, USER_VAR *var, int transNum, int *nextState);
+typedef boolean EVENT_FUNC(SS_ID ssId, USER_VAR *var, int *transNum, int *nextState);
 typedef void DELAY_FUNC(SS_ID ssId, USER_VAR *var);
 typedef void ENTRY_FUNC(SS_ID ssId, USER_VAR *var);
 typedef void EXIT_FUNC(SS_ID ssId, USER_VAR *var);
@@ -151,7 +151,7 @@ struct seqProgram
 	unsigned	numSS;		/* number of state sets */
 	unsigned	varSize;	/* # bytes in user variable area */
 	const char	*params;	/* program paramters */
-	unsigned	numEvents;	/* number of event flags */
+	unsigned	numEvFlags;	/* number of event flags */
 	bitMask		options;	/* program option mask */
 	INIT_FUNC	*initFunc;	/* init function */
 	ENTRY_FUNC	*entryFunc;	/* entry function */
diff --git a/src/seq/seqPvt.h b/src/seq/seqPvt.h
index a7aec470ea379cccfec646c094a1b494a3eef2b8..f2b52ca7e971cc3742aa4460afbe92804e1a989e 100644
--- a/src/seq/seqPvt.h
+++ b/src/seq/seqPvt.h
@@ -32,42 +32,37 @@
 #define	INCLseqPvth
 
 #include "seq_queue.h"
+#include "seqCom.h"
 
-/* global variable for PV system context */
-#ifdef DECLARE_PV_SYS
-void *pvSys;
-#else
-extern void *pvSys;
-#endif
-
-#define valPtr(ch,ss)	((char*)basePtr(ch,ss)+(ch)->offset)
-#define basePtr(ch,ss)	(((ch)->sprog->options&OPT_SAFE)?(ss)->var:bufPtr(ch))
-#define bufPtr(ch)	(((ch)->sprog->options&OPT_REENT)?(ch)->sprog->var:0)
+#define valPtr(ch,ss)		((char*)basePtr(ch,ss)+(ch)->offset)
+#define basePtr(ch,ss)		(((ch)->sprog->options&OPT_SAFE)?(ss)->var:(ch)->sprog->var)
+#define bufPtr(ch)		((char*)(ch)->sprog->var+(ch)->offset)
 
-#define ssNum(ss)	((ss)->sprog->ss-(ss))
+#define ssNum(ss)		((ss)-(ss)->sprog->ss)
+#define chNum(ch)		((ch)-(ch)->sprog->chan)
 
 /* Generic iteration on lists */
 #define foreach(e,l)		for (e = l; e != 0; e = e->next)
 
 /* Generic min and max */
 #ifndef min
-#define min(x, y) (((x) < (y)) ? (x) : (y))
+#define min(x, y)		(((x) < (y)) ? (x) : (y))
 #endif
 
 #ifndef max
-#define max(x, y) (((x) < (y)) ? (y) : (x))
+#define max(x, y)		(((x) < (y)) ? (y) : (x))
 #endif
 
 /* Generic allocation */
 #define newArray(type,count)	(type *)calloc(count, sizeof(type))
 #define new(type)		newArray(type,1)
 
+typedef struct assigned_channel	ACHAN;
 typedef struct channel		CHAN;
 typedef seqState		STATE;
 typedef struct macro		MACRO;
 typedef struct state_set	SSCB;
 typedef struct program_instance	SPROG;
-typedef struct auxiliary_args	AUXARGS;
 typedef struct pvreq		PVREQ;
 typedef const struct pv_type	PVTYPE;
 
@@ -75,7 +70,7 @@ typedef const struct pv_type	PVTYPE;
 struct channel
 {
 	/* static channel data (assigned once on startup) */
-	ptrdiff_t	offset;		/* offset to value */
+	ptrdiff_t	offset;		/* offset to value (e.g. in sprog->var) */
 	const char	*varName;	/* variable name */
 	unsigned	count;		/* number of elements in array */
 	unsigned	eventNum;	/* event number */
@@ -83,30 +78,12 @@ struct channel
 	SPROG		*sprog;		/* state program that owns this struct*/
 
 	/* dynamic channel data (assigned at runtime) */
-	char		*dbAsName;	/* channel name from assign statement */
+	ACHAN		*ach;		/* channel "assigned" to a real pv */
 	EV_ID		efId;		/* event flag id if synced */
-	boolean		monFlag;	/* whether channel shall be monitored */
-
-	char		*dbName;	/* channel name after macro expansion */
-	void		*pvid;		/* PV (process variable) id */
-	boolean		assigned;	/* whether channel is assigned */
-	boolean		connected;	/* whether channel is connected */
-	boolean		*getComplete;	/* array of flags, one for each state set */
-	epicsEventId	putSemId;	/* semaphore id for async put */
-	short		status;		/* last db access status code */
-	epicsTimeStamp	timeStamp;	/* time stamp */
-	unsigned	dbCount;	/* actual count for db access */
-	short		severity;	/* last db access severity code */
-	const char	*message;	/* last db access error message */
-	boolean		gotFirstMonitor;
-	boolean		gotFirstConnect;
-	boolean		monitored;	/* whether channel is monitored */
-	void		*monid;		/* event id (supplied by PV lib) */
 	QUEUE		queue;		/* queue if queued */
-
+	boolean		monitored;	/* whether channel is monitored */
 	/* buffer access, only used in safe mode */
-	epicsMutexId	varLock;	/* mutex for un-assigned vars */
-	boolean		*dirty;		/* array of dirty flags, one for each state set */
+	epicsMutexId	varLock;	/* mutex for put to anonymous pvs */
 	boolean		wr_active;	/* buffer is currently being written */
 };
 
@@ -118,34 +95,50 @@ struct pv_type
 	size_t		size;
 };
 
+struct assigned_channel
+{
+	char		*dbName;	/* channel name after macro expansion */
+	void		*pvid;		/* PV (process variable) id */
+	unsigned	dbCount;	/* actual count for db access */
+	boolean		connected;	/* whether channel is connected */
+	void		*monid;		/* monitor id (supplied by PV lib) */
+	boolean		gotOneMonitor;	/* whether got at least one monitor */
+	/* data received from pv layer per request/monitor */
+	epicsTimeStamp	timeStamp;	/* time stamp */
+	pvStat		status;		/* last db access status code */
+	pvSevr		severity;	/* last db access severity code */
+	const char	*message;	/* last db access error message */
+};
+
 /* Structure to hold information about a State Set */
 struct state_set
 {
+	/* static state set data (assigned once on startup) */
 	const char	*ssName;	/* state set name (for debugging) */
 	epicsThreadId	threadId;	/* thread id */
-	unsigned	threadPriority;	/* thread priority */
-	unsigned	stackSize;	/* stack size */
-	epicsEventId	allFirstConnectAndMonitorSemId;
-	epicsEventId	syncSemId;	/* semaphore for event sync */
-	epicsEventId	getSemId;	/* semaphore id for async get */
-	epicsEventId	death1SemId;	/* semaphore id for death (#1) */
-	epicsEventId	death2SemId;	/* semaphore id for death (#2) */
-	epicsEventId	death3SemId;	/* semaphore id for death (#3) */
-	epicsEventId	death4SemId;	/* semaphore id for death (#4) */
 	unsigned	numStates;	/* number of states */
 	STATE		*states;	/* ptr to array of state blocks */
-	short		currentState;	/* current state index */
-	short		nextState;	/* next state index */
-	short		prevState;	/* previous state index */
-	short		transNum;	/* highest prio trans. # triggered */
-	const bitMask	*mask;		/* current event mask */
 	unsigned	maxNumDelays;	/* max. number of delays */
+	SPROG		*sprog;		/* ptr back to state program block */
+
+	/* dynamic state set data (assigned at runtime) */
+	int		currentState;	/* current state index */
+	int		nextState;	/* next state index */
+	int		prevState;	/* previous state index */
+	int		transNum;	/* highest prio trans. # triggered */
+	const bitMask	*mask;		/* current event mask */
 	unsigned	numDelays;	/* number of delays activated */
-	double		*delay;		/* queued delay value in secs (array) */
-	boolean		*delayExpired;	/* TRUE if delay expired (array) */
+	double		*delay;		/* queued delay values in secs (array) */
+	boolean		*delayExpired;	/* whether delay expired (array) */
 	double		timeEntered;	/* time that a state was entered */
-	char		*var;		/* variable value block (safe mode) */
-	SPROG		*sprog;	/* ptr back to state program block */
+	epicsEventId	syncSemId;	/* semaphore for event sync */
+	epicsEventId	dead;		/* event to signal state set exit done */
+	/* these are arrays, one for each channel */
+	epicsEventId	*getSemId;	/* semaphores for async get */
+	epicsEventId	*putSemId;	/* semaphores for async put */
+	/* safe mode */
+	boolean		*dirty;		/* array of flags, one for each channel */
+	USER_VAR	*var;		/* variable value block (safe mode) */
 };
 
 /* All information about a state program.
@@ -153,47 +146,47 @@ struct state_set
  */
 struct program_instance
 {
-	const char	*progName;	/* program name (for debugging) */
-	epicsThreadId	threadId;	/* thread id (main thread) */
-	unsigned	threadPriority;	/* thread priority */
-	unsigned	stackSize;	/* stack size */
-	epicsMutexId	programLock;	/* mutex for locking CA events */
+	/* static program data (assigned once on startup) */
+	const char	*progName;	/* program name (for messages) */
+	int		instance;	/* program instance number */
+	unsigned	threadPriority;	/* thread priority (all threads) */
+	unsigned	stackSize;	/* stack size (all threads) */
+	void		*pvSys;		/* pv system handle */
+	char		*pvSysName;	/* pv system name ("ca", "ktl", ...) */
+	int		debug;		/* pv system debug level */
 	CHAN		*chan;		/* table of channels */
-	unsigned	numChans;	/* number of db channels, incl. unass */
-	unsigned	assignCount;	/* number of db channels assigned */
-	unsigned	connectCount;	/* number of channels connected */
-	unsigned	firstConnectCount;
-	unsigned	numMonitoredChans;
-	unsigned	firstMonitorCount;
-	unsigned	allFirstConnectAndMonitor;
-	boolean		allDisconnected;
+	unsigned	numChans;	/* number of channels */
 	QUEUE		*queues;	/* array of syncQ queues */
 	unsigned	numQueues;	/* number of syncQ queues */
 	SSCB		*ss;		/* array of state set control blocks */
 	unsigned	numSS;		/* number of state sets */
-	char		*var;		/* user variable area (or CA buffer in safe mode) */
-	size_t		varSize;	/* # bytes in user variable area */
+	USER_VAR	*var;		/* user variable area (or CA buffer in safe mode) */
+	size_t		varSize;	/* size of user variable area */
 	MACRO		*macros;	/* ptr to macro table */
-	char		*params;	/* program paramters */
-	bitMask		*events;	/* event bits for event flags & db */
-	unsigned	numEvents;	/* number of events */
+	char		*params;	/* program parameters */
 	unsigned	options;	/* options (bit-encoded) */
 	INIT_FUNC	*initFunc;	/* init function */
 	ENTRY_FUNC	*entryFunc;	/* entry function */
 	EXIT_FUNC	*exitFunc;	/* exit function */
-	void		*pvReqPool;	/* freeList for pv requests */
-
-	int		instance;	/* program instance number */
-	SPROG		*next;		/* next element in program list */
-};
+	unsigned	numEvFlags;	/* number of event flags */
 
-/* Auxiliary thread arguments */
-struct auxiliary_args
-{
-	char		*pvSysName;	/* PV system ("ca", "ktl", ...) */
-	int		debug;		/* debug level */
+	/* dynamic program data (assigned at runtime) */
+	epicsMutexId	programLock;	/* mutex for locking dynamic program data */
+        /* the following five members must always be protected by programLock */
+	bitMask		*evFlags;	/* event bits for event flags & channels */
+	unsigned	assignCount;	/* number of channels assigned to ext. pv */
+	unsigned	connectCount;	/* number of channels connected */
+	unsigned	monitorCount;	/* number of channels monitored */
+	unsigned	firstMonitorCount; /* number of channels that received
+					   at least one monitor event */
+	void		*pvReqPool;	/* freeList for pv requests (has own lock) */
+	boolean		die;		/* flag set when seqStop is called */
+	epicsEventId	ready;		/* all channels connected & got 1st monitor */
+	epicsEventId	dead;		/* event to signal exit of main thread done */
+	SPROG		*next;		/* next element in program list (global lock) */
 };
 
+/* Request data for pvPut and pvGet */
 struct pvreq
 {
 	CHAN		*ch;		/* requested variable */
@@ -208,12 +201,11 @@ struct pvreq
 /* Internal procedures */
 
 /* seq_task.c */
-void sequencer (SPROG *sp);
-void ss_write_buffer(CHAN *ch, void *val);
+void sequencer (void *arg);
+void ss_write_buffer(SSCB *pwSS, CHAN *ch, void *val);
+void ss_read_buffer(SSCB *ss, CHAN *ch);
 void seqWakeup(SPROG *sp, unsigned eventNum);
-void seqFree(SPROG *sp);
-void *seqAuxThread(void *);
-epicsThreadId seqAuxThreadId;
+void seqCreatePvSys(SPROG *sp);
 /* seq_mac.c */
 void seqMacParse(SPROG *sp, const char *macStr);
 char *seqMacValGet(SPROG *sp, const char *name);
@@ -227,8 +219,9 @@ void seq_put_handler(void *var, pvType type, int count,
 void seq_mon_handler(void *var, pvType type, int count,
 	pvValue *value, void *arg, pvStat status);
 void seq_conn_handler(void *var,int connected);
-pvStat seq_connect(SPROG *sp);
-pvStat seq_disconnect(SPROG *sp);
+pvStat seq_connect(SPROG *sp, boolean wait);
+void seq_disconnect(SPROG *sp);
+pvStat seq_monitor(CHAN *ch, boolean on);
 /* seq_prog.c */
 typedef int seqTraversee(SPROG *prog, void *param);
 void seqTraverseProg(seqTraversee *func, void *param);
@@ -238,10 +231,13 @@ void seqDelProg(SPROG *sp);
 void seqAddProg(SPROG *sp);
 /* seqCommands.c */
 typedef int sequencerProgramTraversee(SPROG **sprog, seqProgram *pseq, void *param);
-void traverseSequencerPrograms(sequencerProgramTraversee *traversee, void *param);
-
+int traverseSequencerPrograms(sequencerProgramTraversee *traversee, void *param);
+/* seq_main.c */
+void seq_free(SPROG *sp);
 /* debug/query support */
 typedef int pr_fun(const char *format,...);
-static int nothing (const char *format,...) {return 0;}
+void print_channel_value(pr_fun *, CHAN *ch, void *val);
+
+static int nothing(const char *format,...) {return 0;}
 
 #endif	/*INCLseqPvth*/
diff --git a/src/seq/seq_ca.c b/src/seq/seq_ca.c
index 047f46aa4d6c49dd56f2f34d659498e3387d3970..f3e477e91569d7a28a0a4a1f3aefced6fa33dc7e 100644
--- a/src/seq/seq_ca.c
+++ b/src/seq/seq_ca.c
@@ -1,4 +1,4 @@
-/*	Process Variable (was CA) interface for sequencer.
+/*	Process Variable interface for sequencer.
  *
  *	Author:  Andy Kozubal
  *	Date:    July, 1991
@@ -33,76 +33,102 @@
 /* #define DEBUG errlogPrintf */
 #define DEBUG nothing
 
-
-#include "seq.h"
+/*
+ * Event type (extra argument passed to proc_db_events().
+ */
+enum event_type {
+	GET_COMPLETE,
+	PUT_COMPLETE,
+	MON_COMPLETE
+};
+
+static const char *event_type_name[] = {
+	"get",
+	"put",
+	"mon"
+};
 
 static void proc_db_events(pvValue *value, pvType type,
-	CHAN *ch, SSCB *ss, long complete_type);
+	CHAN *ch, SSCB *ss, enum event_type evtype, pvStat status);
 static void proc_db_events_queued(SPROG *sp, CHAN *ch, pvValue *value);
 
 /*
- * seq_connect() - Connect to all database channels.
+ * seq_connect() - Initiate connect & monitor requests to PVs.
+ * If wait is TRUE, wait for all connections to be established.
  */
-pvStat seq_connect(SPROG *sp)
+pvStat seq_connect(SPROG *sp, boolean wait)
 {
-	CHAN		*ch;
-        pvStat		status;
+	pvStat		status;
 	unsigned	nch;
+	int		delay = 10;
+	boolean		ready = FALSE;
 
-	for (nch = 0, ch = sp->chan; nch < sp->numChans; nch++, ch++)
+	for (nch = 0; nch < sp->numChans; nch++)
 	{
-		if (ch->dbName == NULL || ch->dbName[0] == 0)
-			continue; /* skip records without pv names */
-		sp->assignCount += 1; /* keep track of number of *assigned* channels */
-		if (ch->monFlag) sp->numMonitoredChans++;/*do it before pvVarCreate*/
+		CHAN *ch = sp->chan + nch;
+
+		if (ch->ach == NULL)
+			continue;		/* skip anonymous pvs */
+		sp->assignCount += 1;
+		if (ch->monitored)
+			sp->monitorCount++;	/* do it before pvVarCreate */
 	}
+
 	/*
-	 * For each channel: connect to db & issue monitor (if monFlag is TRUE).
+	 * For each channel: create pv object, then subscribe if monitored.
 	 */
-	for (nch = 0, ch = sp->chan; nch < sp->numChans; nch++, ch++)
+	for (nch = 0; nch < sp->numChans; nch++)
 	{
-		if (ch->dbName == NULL || ch->dbName[0] == 0)
+		CHAN *ch = sp->chan + nch;
+		ACHAN *ach = ch->ach;
+
+		if (ach == NULL)
 			continue; /* skip records without pv names */
 		DEBUG("seq_connect: connect %s to %s\n", ch->varName,
-			ch->dbName);
+			ach->dbName);
 		/* Connect to it */
 		status = pvVarCreate(
-			pvSys,			/* PV system context */
-			ch->dbName,		/* PV name */
-			seq_conn_handler,	/* connection handler routine */
-			ch,			/* private data is CHAN struc */
-			0,			/* debug level (inherited) */
-			&ch->pvid );		/* ptr to PV id */
+				sp->pvSys,		/* PV system context */
+				ch->ach->dbName,	/* PV name */
+				seq_conn_handler,	/* connection handler routine */
+				ch,			/* private data is CHAN struc */
+				0,			/* debug level (inherited) */
+				&ach->pvid);		/* ptr to PV id */
 		if (status != pvStatOK)
 		{
 			errlogPrintf("seq_connect: pvVarCreate() %s failure: "
-				"%s\n", ch->dbName, pvVarGetMess(ch->pvid));
-			return status;
-		}
-		ch->assigned = TRUE;
-		/* Clear monitor indicator */
-		ch->monitored = FALSE;
-
-		/*
-		 * Issue monitor request
-		 */
-		if (ch->monFlag)
-		{
-			seq_pvMonitor(sp->ss, nch);
+				"%s\n", ach->dbName, pvVarGetMess(ach->pvid));
+			continue;
 		}
 	}
-	sp->allDisconnected = FALSE;
-	pvSysFlush(pvSys);
+	pvSysFlush(sp->pvSys);
+
+	if (wait)
+	{
+		do {
+			/* Check whether we have been asked to exit */
+			if (sp->die)
+				return pvStatERROR;
+
+			epicsMutexMustLock(sp->programLock);
+			ready = sp->connectCount == sp->assignCount;
+			epicsMutexUnlock(sp->programLock);
+
+			if (!ready) {
+				epicsEventWaitStatus status = epicsEventWaitWithTimeout(
+					sp->ready, (double)delay);
+				if (status == epicsEventWaitError || sp->die)
+					return pvStatERROR;
+				if (delay < 60) delay += 10;
+				errlogPrintf("%s[%d]: assignCount=%d, connectCount=%d, monitorCount=%d\n",
+					sp->progName, sp->instance,
+					sp->assignCount, sp->connectCount, sp->monitorCount);
+			}
+		} while (!ready);
+	}
 	return pvStatOK;
 }
 
-/*
- * Event completion type (extra argument passed to proc_db_events().
- */
-#define	GET_COMPLETE 0
-#define	PUT_COMPLETE 1
-#define	MON_COMPLETE 2
-
 /*
  * seq_get_handler() - Sequencer callback handler.
  * Called when a "get" completes.
@@ -110,13 +136,14 @@ pvStat seq_connect(SPROG *sp)
 epicsShareFunc void seq_get_handler(
 	void *var, pvType type, int count, pvValue *value, void *arg, pvStat status)
 {
-	PVREQ *req = (PVREQ *)arg;
-	CHAN *ch = req->ch;
-	SPROG *sp = ch->sprog;
+	PVREQ	*rQ = (PVREQ *)arg;
+	CHAN	*ch = rQ->ch;
+	SPROG	*sp = ch->sprog;
 
+	assert(ch->ach != NULL);
 	freeListFree(sp->pvReqPool, arg);
 	/* Process event handling in each state set */
-	proc_db_events(value, type, ch, req->ss, GET_COMPLETE);
+	proc_db_events(value, type, ch, rQ->ss, GET_COMPLETE, status);
 }
 
 /*
@@ -126,13 +153,14 @@ epicsShareFunc void seq_get_handler(
 epicsShareFunc void seq_put_handler(
 	void *var, pvType type, int count, pvValue *value, void *arg, pvStat status)
 {
-	PVREQ *req = (PVREQ *)arg;
-	CHAN *ch = req->ch;
-	SPROG *sp = ch->sprog;
+	PVREQ	*rQ = (PVREQ *)arg;
+	CHAN	*ch = rQ->ch;
+	SPROG	*sp = ch->sprog;
 
+	assert(ch->ach != NULL);
 	freeListFree(sp->pvReqPool, arg);
 	/* Process event handling in each state set */
-	proc_db_events(value, type, ch, req->ss, PUT_COMPLETE);
+	proc_db_events(value, type, ch, rQ->ss, PUT_COMPLETE, status);
 }
 
 /*
@@ -141,25 +169,24 @@ epicsShareFunc void seq_put_handler(
 epicsShareFunc void seq_mon_handler(
 	void *var, pvType type, int count, pvValue *value, void *arg, pvStat status)
 {
-	CHAN *ch = (CHAN *)arg;
-	SPROG *sp = ch->sprog;
+	CHAN	*ch = (CHAN *)arg;
+	SPROG	*sp = ch->sprog;
+	ACHAN	*ach = ch->ach;
 
+	assert(ach != NULL);
 	/* Process event handling in each state set */
-	proc_db_events(value, type, ch, sp->ss, MON_COMPLETE);
-	if(!ch->gotFirstMonitor)
+	proc_db_events(value, type, ch, sp->ss, MON_COMPLETE, status);
+	if (!ach->gotOneMonitor)
 	{
-		ch->gotFirstMonitor = 1;
+		ach->gotOneMonitor = TRUE;
+		epicsMutexMustLock(sp->programLock);
 		sp->firstMonitorCount++;
-		if((sp->firstMonitorCount==sp->numMonitoredChans)
-			&& (sp->firstConnectCount==sp->assignCount))
+		if (sp->firstMonitorCount == sp->monitorCount
+			&& sp->connectCount == sp->assignCount)
 		{
-			SSCB *ss;
-			unsigned nss;
-			for(nss=0, ss=sp->ss; nss<sp->numSS; nss++,ss++)
-			{
-				epicsEventSignal(ss->allFirstConnectAndMonitorSemId);
-			}
+			epicsEventSignal(sp->ready);
 		}
+		epicsMutexUnlock(sp->programLock);
 	}
 }
 
@@ -169,15 +196,18 @@ static void proc_db_events(
 	pvType	type,
 	CHAN	*ch,
 	SSCB	*ss,		/* originator, for put and get */
-	long	complete_type)
+	enum event_type evtype,
+	pvStat	status)
 {
 	SPROG	*sp = ch->sprog;
+	ACHAN	*ach = ch->ach;
 
-	DEBUG("proc_db_events: var=%s, pv=%s, type=%s\n", ch->varName,
-		ch->dbName, complete_type==0?"get":complete_type==1?"put":"mon");
+	assert(ach != NULL);
+	DEBUG("proc_db_events: var=%s, pv=%s, type=%s, status=%d\n", ch->varName,
+		ach->dbName, event_type_name[evtype], status);
 
 	/* If monitor on var queued via syncQ, branch to alternative routine */
-	if (ch->queue && complete_type == MON_COMPLETE)
+	if (ch->queue && evtype == MON_COMPLETE)
 	{
 		proc_db_events_queued(sp, ch, value);
 		return;
@@ -187,53 +217,41 @@ static void proc_db_events(
 	   for put completion only) */
 	if (value != NULL)
 	{
-		void *val = pv_value_ptr(value, type);
+		void *val = pv_value_ptr(value,type);
 
 		/* Write value to CA buffer (lock-free) */
-		ss_write_buffer(ch, val);
-		if (pv_is_time_type(type))
-		{
-			ch->status = *pv_status_ptr(value, type);
-			ch->severity = *pv_severity_ptr(value, type);
-			ch->timeStamp = *pv_stamp_ptr(value, type);
-		}
+		ss_write_buffer(0, ch, val);
+
+		/* Copy status, severity and time stamp */
+		ach->status = *pv_status_ptr(value,type);
+		ach->severity = *pv_severity_ptr(value,type);
+		ach->timeStamp = *pv_stamp_ptr(value,type);
+
 		/* Copy error message (only when severity indicates error) */
-		if (ch->severity != pvSevrNONE)
+		if (ach->severity != pvSevrNONE)
 		{
-			const char *pmsg = pvVarGetMess(ch->pvid);
+			const char *pmsg = pvVarGetMess(ach->pvid);
 			if (!pmsg) pmsg = "unknown";
-			ch->message = pmsg;
+			ach->message = pmsg;
 		}
 	}
 
-	/* Indicate completed pvGet() or pvPut()  */
-	switch (complete_type)
-	{
-	    case GET_COMPLETE:
-		ch->getComplete[ssNum(ss)] = TRUE;
-		break;
-	    case PUT_COMPLETE:
-		break;
-	    default:
-		break;
-	}
-
 	/* Wake up each state set that uses this channel in an event */
 	seqWakeup(sp, ch->eventNum);
 
 	/* If there's an event flag associated with this channel, set it */
 	/* TODO: check if correct/documented to do this for non-monitor completions */
 	if (ch->efId > 0)
-		seq_efSet(ss, ch->efId);
+		seq_efSet(sp->ss, ch->efId);
 
-	/* Give semaphore for completed synchronous pvGet() and pvPut() */
-	switch (complete_type)
+	/* Signal completion */
+	switch (evtype)
 	{
 	    case GET_COMPLETE:
-		epicsEventSignal(ss->getSemId);
+		epicsEventSignal(ss->getSemId[chNum(ch)]);
 		break;
 	    case PUT_COMPLETE:
-		epicsEventSignal(ch->putSemId);
+		epicsEventSignal(ss->putSemId[chNum(ch)]);
 		break;
 	    default:
 		break;
@@ -246,7 +264,7 @@ static void proc_db_events_queued(SPROG *sp, CHAN *ch, pvValue *value)
 	boolean	full;
 
 	DEBUG("proc_db_events_queued: var=%s, pv=%s, queue=%p, used(max)=%d(%d)\n",
-		ch->varName, ch->dbName,
+		ch->varName, ch->ach->dbName,
 		ch->queue, seqQueueUsed(ch->queue), seqQueueNumElems(ch->queue));
 	/* Copy whole message into queue; no lock needed because only one writer */
 	full = seqQueuePut(ch->queue, value);
@@ -255,7 +273,7 @@ static void proc_db_events_queued(SPROG *sp, CHAN *ch, pvValue *value)
 		errlogSevPrintf(errlogMinor,
 		  "monitor event for variable %s (pv %s): "
 		  "last queue element overwritten (queue is full)\n",
-		  ch->varName, ch->dbName
+		  ch->varName, ch->ach->dbName
 		);
 	}
 	/* Set event flag; note: it doesn't matter which state set we pass. */
@@ -265,125 +283,132 @@ static void proc_db_events_queued(SPROG *sp, CHAN *ch, pvValue *value)
 }
 
 /* Disconnect all database channels */
-pvStat seq_disconnect(SPROG *sp)
+epicsShareFunc void seq_disconnect(SPROG *sp)
 {
-	CHAN	*ch;
 	unsigned nch;
-	SPROG	*mySP; /* will be NULL if this is not a sequencer thread */
 
-	/* Did we already disconnect? */
-	if (sp->allDisconnected)
-		return pvStatOK;
 	DEBUG("seq_disconnect: sp = %p\n", sp);
 
-	/* Attach to PV context of pvSys creator (auxiliary thread) */
-	mySP = seqFindProg(epicsThreadGetIdSelf());
-	if (mySP == NULL)
-	{
-#ifdef	DEBUG_DISCONNECT
-		errlogPrintf("seq_disconnect: pvSysAttach(pvSys)\n");
-#endif	/*DEBUG_DISCONNECT*/
-		/* not a sequencer thread */
-		pvSysAttach(pvSys);
-	}
-
-	ch = sp->chan;
-#ifdef	DEBUG_DISCONNECT
-	errlogPrintf("seq_disconnect: sp = %p, ch = %p\n", sp, ch);
-#endif	/*DEBUG_DISCONNECT*/
-
-	for (nch = 0; nch < sp->numChans; nch++, ch++)
+	epicsMutexMustLock(sp->programLock);
+	for (nch = 0; nch < sp->numChans; nch++)
 	{
+		CHAN	*ch = sp->chan + nch;
 		pvStat	status;
+		ACHAN	*ach = ch->ach;
 
-		if (!ch->assigned)
+		if (!ach)
 			continue;
 		DEBUG("seq_disconnect: disconnect %s from %s\n",
- 			ch->varName, ch->dbName);
+ 			ch->varName, ach->dbName);
 		/* Disconnect this PV */
-		status = pvVarDestroy(ch->pvid);
+		status = pvVarDestroy(ach->pvid);
 		if (status != pvStatOK)
 		    errlogPrintf("seq_disconnect: pvVarDestroy() %s failure: "
-				"%s\n", ch->dbName, pvVarGetMess(ch->pvid));
+				"%s\n", ach->dbName, pvVarGetMess(ach->pvid));
 
 		/* Clear monitor & connect indicators */
-		ch->monitored = FALSE;
-		ch->connected = FALSE;
+		ach->connected = FALSE;
+		sp->connectCount -= 1;
 	}
+	epicsMutexUnlock(sp->programLock);
 
-	sp->allDisconnected = TRUE;
+	pvSysFlush(sp->pvSys);
+}
 
-	pvSysFlush(pvSys);
+pvStat seq_monitor(CHAN *ch, boolean on)
+{
+	ACHAN	*ach = ch->ach;
+	pvStat	status;
 
-	return pvStatOK;
+	assert(ch);
+	assert(ach);
+	if (on == (ach->monid != NULL))			/* already done */
+		return pvStatOK;
+	if (on)
+		status = pvVarMonitorOn(
+				ach->pvid,		/* pvid */
+				ch->type->getType,	/* requested type */
+				(int)ch->count,		/* element count */
+				seq_mon_handler,	/* function to call */
+				ch,			/* user arg (channel struct) */
+				&ach->monid);		/* where to put event id */
+	else
+		status = pvVarMonitorOff(ach->pvid, ach->monid);
+	if (status != pvStatOK)
+		errlogPrintf("seq_monitor: pvVarMonitor%s(%s) failure: %s\n",
+			on?"On":"Off", ach->dbName, pvVarGetMess(ach->pvid));
+	else if (!on)
+		ach->monid = NULL;
+	return status;
 }
 
 /*
  * seq_conn_handler() - Sequencer connection handler.
  * Called each time a connection is established or broken.
  */
-void seq_conn_handler(void *var,int connected)
+void seq_conn_handler(void *var, int connected)
 {
-	CHAN	*ch;
-	SPROG	*sp;
-
-	/* Private data is db ptr (specified at pvVarCreate()) */
-	ch = (CHAN *)pvVarGetPrivate(var);
+	CHAN	*ch = (CHAN *)pvVarGetPrivate(var);
+	SPROG	*sp = ch->sprog;
+	ACHAN	*ach = ch->ach;
 
-	/* State program that owns this db entry */
-	sp = ch->sprog;
+	epicsMutexMustLock(sp->programLock);
 
-	/* If PV not connected */
+	assert(ach != NULL);
 	if (!connected)
 	{
-		DEBUG("%s disconnected from %s\n", ch->varName, ch->dbName);
-		if (ch->connected)
+		DEBUG("%s disconnected from %s\n", ch->varName, ach->dbName);
+		if (ach->connected)
 		{
-			ch->connected = FALSE;
+			ach->connected = FALSE;
 			sp->connectCount--;
-			ch->monitored = FALSE;
+
+			if (ch->monitored)
+			{
+				seq_monitor(ch, FALSE);
+			}
 		}
 		else
 		{
-			errlogPrintf("%s disconnected but already disconnected %s\n",
-				ch->varName, ch->dbName);
+			errlogPrintf("%s disconnect event but already disconnected %s\n",
+				ch->varName, ach->dbName);
 		}
 	}
-	else	/* PV connected */
+	else	/* connected */
 	{
-		DEBUG("%s connected to %s\n", ch->varName,ch->dbName);
-		if (!ch->connected)
+		DEBUG("%s connected to %s\n", ch->varName, ach->dbName);
+		if (!ach->connected)
 		{
-			ch->connected = TRUE;
+			int dbCount;
+			ach->connected = TRUE;
 			sp->connectCount++;
-			if (ch->monFlag)
-				ch->monitored = TRUE;
-			ch->dbCount = (unsigned)pvVarGetCount(var);
-			if (ch->dbCount > ch->count)
-				ch->dbCount = ch->count;
+			if (sp->firstMonitorCount == sp->monitorCount
+				&& sp->connectCount == sp->assignCount)
+			{
+				epicsEventSignal(sp->ready);
+			}
+			dbCount = pvVarGetCount(var);
+			assert(dbCount >= 0);
+			ach->dbCount = min(ch->count, (unsigned)dbCount);
+
+			if (ch->monitored)
+			{
+				seq_monitor(ch, TRUE);
+			}
 		}
 		else
 		{
-			printf("%s connected but already connected %s\n",
-				ch->varName,ch->dbName);
-		}
-		if(!ch->gotFirstConnect)
-		{
-			ch->gotFirstConnect = 1;
-			sp->firstConnectCount++;
-			if((sp->firstMonitorCount==sp->numMonitoredChans)
-				&& (sp->firstConnectCount==sp->assignCount))
-			{
-				SSCB *ss;
-				unsigned nss;
-				for(nss=0, ss=sp->ss; nss<sp->numSS; nss++,ss++)
-				{
-					epicsEventSignal(ss->allFirstConnectAndMonitorSemId);
-				}
-			}
+			errlogPrintf("%s connect event but already connected %s\n",
+				ch->varName, ach->dbName);
 		}
 	}
-
-	/* Wake up each state set that is waiting for event processing */
+	epicsMutexUnlock(sp->programLock);
+
+	/* Wake up each state set that is waiting for event processing.
+	   Why each one? Because pvConnectCount and pvMonitorCount should
+	   act like monitored anonymous channels. Any state set might be
+	   using these functions inside a when-condition and it is expected
+	   that such conditions get checked whenever these counts change.
+	   This is really a crude solution. */
 	seqWakeup(sp, 0);
 }
diff --git a/src/seq/seq_cmd.c b/src/seq/seq_cmd.c
index 67112591bfbf2060a06f1eb420a793494466d197..97dccaae53c760534fbb61d925d7390f80aadc4e 100644
--- a/src/seq/seq_cmd.c
+++ b/src/seq/seq_cmd.c
@@ -10,44 +10,56 @@
  *    University of Saskatchewan
  *    Saskatoon, Saskatchewan, CANADA
  *    cls.usask.ca
+ *
+ * Copyright, 2010, Helmholtz-Zentrum Berlin f. Materialien
+ *                  und Energie GmbH, Germany (HZB)
+ * (see file Copyright.HZB included in this distribution)
+ *
  */
 #include "seq.h"
 
 struct sequencerProgram {
     seqProgram *prog;
-    epicsMutexId lock;
     struct program_instance *instances;
     struct sequencerProgram *next;
 };
+
+/* These are the only global variables in the whole seq library. */
 static struct sequencerProgram *seqHead;
+static epicsMutexId seqLock;
 
 /*
  * This routine is called before multitasking has started, so there's
- * no race condition in creating the linked list or the instance lock.
+ * no race condition in creating the linked list or the lock.
  */
 epicsShareFunc void epicsShareAPI seqRegisterSequencerProgram(seqProgram *p)
 {
     struct sequencerProgram *sp;
 
+    seqLock = epicsMutexMustCreate();
     sp = (struct sequencerProgram *)mallocMustSucceed(sizeof *sp, "seqRegisterSequencerProgram");
     sp->prog = p;
     sp->next = seqHead;
-    sp->lock = epicsMutexMustCreate();
     sp->instances = NULL;
     seqHead = sp;
 }
 
-void traverseSequencerPrograms(sequencerProgramTraversee *traversee, void *param)
+int traverseSequencerPrograms(sequencerProgramTraversee *traversee, void *param)
 {
     struct sequencerProgram *sp;
+    int stop = FALSE;
 
+    epicsMutexMustLock(seqLock);
     foreach(sp, seqHead) {
-        int stop;
-        epicsMutexMustLock(sp->lock);
         stop = traversee(&sp->instances, sp->prog, param);
-        epicsMutexUnlock(sp->lock);
-        if (!stop) break;
+        if (stop) break;
+    }
+    /* call one last time to indicate that list was exhausted */
+    if (!stop) {
+        stop = traversee(NULL, NULL, param);
     }
+    epicsMutexUnlock(seqLock);
+    return stop;
 }
 
 /*
diff --git a/src/seq/seq_if.c b/src/seq/seq_if.c
index eb050700d34d78e9931a4346d26652ba519bf8bc..dbb62d350fd6f6be4c6ca79632e2d88d3689b14d 100644
--- a/src/seq/seq_if.c
+++ b/src/seq/seq_if.c
@@ -33,57 +33,90 @@
 /* #define DEBUG errlogPrintf */
 #define DEBUG nothing
 
-/* Flush outstanding PV requests */
-epicsShareFunc void epicsShareAPI seq_pvFlush(SS_ID ss)
-{
-	pvSysFlush(pvSys);
-}	
-
 /*
  * seq_pvGet() - Get DB value.
  * TODO: add optional timeout argument.
  */
 epicsShareFunc pvStat epicsShareAPI seq_pvGet(SS_ID ss, VAR_ID varId, enum compType compType)
 {
-	SPROG	*sp = ss->sprog;
-	CHAN	*ch = sp->chan + varId;
-	int	sync;	/* whether synchronous get */
-	pvStat	status;
-	PVREQ	*req;
+	SPROG		*sp = ss->sprog;
+	CHAN		*ch = sp->chan + varId;
+	int		status;
+	PVREQ		*req;
+	epicsEventId	getSem = ss->getSemId[varId];
+	ACHAN		*ach = ch->ach;
+	double		tmo = 10.0;
 
-	/* Determine whether performing asynchronous or synchronous i/o */
-	switch (compType)
-	{
-	case DEFAULT:
-		sync = !(sp->options & OPT_ASYNC);
-		break;
-	case ASYNC:
-		sync = FALSE;
-		break;
-	case SYNC:
-		sync = TRUE;
-		break;
-	default:
-		errlogSevPrintf(errlogFatal, "pvGet: user error (bad completion type)\n");
+	if ((sp->options & OPT_SAFE) && !ach)
+	{
+		ss_read_buffer(ss,ch);
+		return pvStatOK;
+	}
+	if (!ach)
+	{
+		errlogSevPrintf(errlogFatal,
+			"pvGet(%s): user error (variable not assigned)\n",
+			ch->varName
+		);
 		return pvStatERROR;
 	}
 
-	/* Flag this pvGet() as not completed */
-	ch->getComplete = FALSE;
+	if (compType == DEFAULT)
+	{
+		compType = (sp->options & OPT_ASYNC) ? ASYNC : SYNC;
+	}
 
 	/* Check for channel connected */
-	if (!ch->connected)
+	if (!ach->connected)
 	{
-		ch->status = pvStatDISCONN;
-		ch->severity = pvSevrINVALID;
-		ch->message = "disconnected";
-		return ch->status;
+		ach->status = pvStatDISCONN;
+		ach->severity = pvSevrINVALID;
+		ach->message = "disconnected";
+		return ach->status;
 	}
 
-	/* If synchronous pvGet then clear the pvGet pend semaphore */
-	if (sync)
+	if (compType == SYNC)
 	{
-		epicsEventTryWait(ss->getSemId);
+		double before, after;
+		pvTimeGetCurrentDouble(&before);
+		switch (epicsEventWaitWithTimeout(getSem, tmo))
+		{
+		case epicsEventWaitOK:
+			pvTimeGetCurrentDouble(&after);
+			tmo -= (after - before);
+			break;
+		case epicsEventWaitTimeout:
+			errlogSevPrintf(errlogFatal,
+				"pvGet(ss %s, var %s, pv %s): failed (timeout "
+				"waiting for other get requests to finish)\n",
+				ss->ssName, ch->varName, ach->dbName
+			);
+			return pvStatERROR;
+		case epicsEventWaitError:
+			errlogSevPrintf(errlogFatal,
+				"pvGet: epicsEventWaitWithTimeout() failure\n");
+			return pvStatERROR;
+		}
+	}
+	else if (compType == ASYNC)
+	{
+		switch (epicsEventTryWait(getSem))
+		{
+		case epicsEventWaitOK:
+			break;
+		case epicsEventWaitTimeout:
+			errlogSevPrintf(errlogFatal,
+				"pvGet(ss %s, var %s, pv %s): user error "
+				"(there is already a get pending for this variable/"
+				"state set combination)\n",
+				ss->ssName, ch->varName, ach->dbName
+			);
+			return pvStatERROR;
+		case epicsEventWaitError:
+			errlogSevPrintf(errlogFatal,
+				"pvGet: epicsEventTryWait() failure\n");
+			return pvStatERROR;
+		}
 	}
 
 	/* Allocate and initialize a pv request */
@@ -93,35 +126,43 @@ epicsShareFunc pvStat epicsShareAPI seq_pvGet(SS_ID ss, VAR_ID varId, enum compT
 
 	/* Perform the PV get operation with a callback routine specified */
 	status = pvVarGetCallback(
-			ch->pvid,		/* PV id */
+			ach->pvid,		/* PV id */
 			ch->type->getType,	/* request type */
 			(int)ch->count,	/* element count */
 			seq_get_handler,	/* callback handler */
 			req);			/* user arg */
 	if (status != pvStatOK)
 	{
+		ach->status = pvStatERROR;
+		ach->severity = pvSevrMAJOR;
+		ach->message = "get failure";
 		errlogPrintf("seq_pvGet: pvVarGetCallback() %s failure: %s\n",
-			ch->dbName, pvVarGetMess(ch->pvid));
-		ch->getComplete[ssNum(ss)] = TRUE;
+			ach->dbName, pvVarGetMess(ach->pvid));
 		return status;
 	}
 
-	/* Synchronous: wait for completion (10s timeout) */
-	if (sync)
+	/* Synchronous: wait for completion */
+	if (compType == SYNC)
 	{
-		epicsEventWaitStatus sem_status;
-
-		pvSysFlush(pvSys);
-		sem_status = epicsEventWaitWithTimeout(ss->getSemId, 10.0);
-		if (sem_status != epicsEventWaitOK)
+		pvSysFlush(sp->pvSys);
+		switch (epicsEventWaitWithTimeout(getSem, tmo))
 		{
-			ch->status = pvStatTIMEOUT;
-			ch->severity = pvSevrMAJOR;
-			ch->message = "get completion timeout";
-			return ch->status;
+		case epicsEventWaitOK:
+			break;
+		case epicsEventWaitTimeout:
+			ach->status = pvStatTIMEOUT;
+			ach->severity = pvSevrMAJOR;
+			ach->message = "get completion timeout";
+			return ach->status;
+		case epicsEventWaitError:
+			ach->status = pvStatERROR;
+			ach->severity = pvSevrMAJOR;
+			ach->message = "get completion failure";
+			return ach->status;
 		}
+		epicsEventSignal(getSem);
 	}
-        
+
 	return pvStatOK;
 }
 
@@ -130,9 +171,65 @@ epicsShareFunc pvStat epicsShareAPI seq_pvGet(SS_ID ss, VAR_ID varId, enum compT
  */
 epicsShareFunc boolean epicsShareAPI seq_pvGetComplete(SS_ID ss, VAR_ID varId)
 {
-	CHAN *ch = ss->sprog->chan + varId;
+	epicsEventId	getSem = ss->getSemId[varId];
 
-	return ch->getComplete[ssNum(ss)];
+	switch (epicsEventTryWait(getSem))
+	{
+	case epicsEventWaitOK:
+		epicsEventSignal(getSem);
+		return TRUE;
+	case epicsEventWaitTimeout:
+		return FALSE;
+	case epicsEventWaitError:
+		errlogPrintf("pvGetComplete: "
+		  "epicsEventTryWait(getSemId[%d]) failure\n", varId);
+	default:
+		return FALSE;
+	}
+}
+
+static void anonymous_put(SS_ID ss, CHAN *ch)
+{
+	char	*var = valPtr(ch,ss);	/* ptr to value */
+
+	/* Must lock because multiple writers */
+	epicsMutexMustLock(ch->varLock);
+	if (ch->queue)
+	{
+		QUEUE queue = ch->queue;
+		pvType type = ch->type->getType;
+		size_t size = ch->type->size;
+		char value[pv_size_n(type, ch->count)];
+		int full;
+		DEBUG("seq_pvPut: type=%d, size=%d, count=%d, value=%p, val_ptr=%p, buf_size=%d, q=%p\n",
+			type, size, ch->count, value, pv_value_ptr(value, type),
+			pv_size_n(type, ch->count), queue);
+		print_channel_value(printf, ch, var);
+		memcpy(pv_value_ptr(value, type), var, size * ch->count);
+		print_channel_value(printf, ch, pv_value_ptr(value, type));
+		/* Copy whole message into queue */
+		full = seqQueuePut(queue, value);
+		if (full)
+		{
+			errlogSevPrintf(errlogMinor,
+			  "pvPut on queued variable %s (anonymous): "
+			  "last queue element overwritten (queue is full)\n",
+			  ch->varName
+			);
+		}
+	}
+	/* check if monitored to mirror behaviour for named PVs */
+	else if (ch->monitored)
+	{
+		ss_write_buffer(ss, ch, var);
+	}
+	/* Must give varLock before calling seq_efSet, else (possible) deadlock! */
+	epicsMutexUnlock(ch->varLock);
+	/* Wake up each state set that uses this channel in an event */
+	seqWakeup(ss->sprog, ch->eventNum);
+	/* If there's an event flag associated with this channel, set it */
+	if (ch->efId)
+		seq_efSet(ss, ch->efId);
 }
 
 /*
@@ -142,65 +239,105 @@ epicsShareFunc pvStat epicsShareAPI seq_pvPut(SS_ID ss, VAR_ID varId, enum compT
 {
 	SPROG	*sp = ss->sprog;
 	CHAN	*ch = sp->chan + varId;
-	int	nonb;	/* whether to call pvVarPutNoBlock (=fire&forget) */
-	int	sync;	/* whether to wait for completion */
 	int	status;
-	unsigned count;
+	int	count;
 	char	*var = valPtr(ch,ss);	/* ptr to value */
 	PVREQ	*req;
+	ACHAN	*ach = ch->ach;
+	epicsEventId	putSem = ss->putSemId[varId];
+	double	tmo = 10.0;
 
-	DEBUG("seq_pvPut: pv name=%s, var=%p\n", ch->dbName, var);
+	DEBUG("seq_pvPut: pv name=%s, var=%p\n", ach ? ach->dbName : "<anonymous>", var);
 
-	/* Determine whether performing asynchronous or synchronous i/o
-	   ((+a) option was never honored for put, so DEFAULT
-	   means non-blocking and therefore implicitly asynchronous) */
-	switch (compType)
-	{
-	case DEFAULT:
-		nonb = TRUE;
-		sync = FALSE;
-		break;
-	case ASYNC:
-		nonb = FALSE;
-		sync = FALSE;
-		break;
-	case SYNC:
-		nonb = FALSE;
-		sync = TRUE;
-		break;
-	default:
-		errlogSevPrintf(errlogFatal, "pvPut: user error (bad completion type)\n");
+	/* First handle anonymous PV (safe mode only) */
+	if ((sp->options & OPT_SAFE) && !ach)
+	{
+		anonymous_put(ss, ch);
+		return pvStatOK;
+	}
+	if (!ach)
+	{
+		errlogSevPrintf(errlogFatal,
+			"pvPut(%s): user error (variable not assigned)\n",
+			ch->varName
+		);
 		return pvStatERROR;
 	}
 
-	if (!nonb)
+	/* Check for channel connected */
+	if (!ach->connected)
 	{
-		/* Must wait for active put to complete first */
-		epicsEventWait(ch->putSemId);
+		ach->status = pvStatDISCONN;
+		ach->severity = pvSevrINVALID;
+		ach->message = "disconnected";
+		return ach->status;
 	}
 
-	/* Check for channel connected */
-	/* TODO: is this needed? */
-	if (!ch->connected)
+	/* Determine whether to perform synchronous, asynchronous, or
+           plain put
+	   ((+a) option was never honored for put, so DEFAULT
+	   means non-blocking and therefore implicitly asynchronous) */
+	if (compType == SYNC)
+	{
+		double before, after;
+		pvTimeGetCurrentDouble(&before);
+		switch (epicsEventWaitWithTimeout(putSem, tmo))
+		{
+		case epicsEventWaitOK:
+			pvTimeGetCurrentDouble(&after);
+			tmo -= (after - before);
+			break;
+		case epicsEventWaitTimeout:
+			errlogSevPrintf(errlogFatal,
+				"pvPut(ss %s, var %s, pv %s): failed (timeout "
+				"waiting for other put requests to finish)\n",
+				ss->ssName, ch->varName, ach->dbName
+			);
+			return pvStatERROR;
+		case epicsEventWaitError:
+			errlogSevPrintf(errlogFatal,
+				"pvPut: epicsEventWaitWithTimeout() failure\n");
+			return pvStatERROR;
+		}
+	}
+	else if (compType == ASYNC)
 	{
-		ch->status = pvStatDISCONN;
-		ch->severity = pvSevrINVALID;
-		ch->message = "disconnected";
-		return ch->status;
+		switch (epicsEventTryWait(putSem))
+		{
+		case epicsEventWaitOK:
+			break;
+		case epicsEventWaitTimeout:
+			ach->status = pvStatERROR;
+			ach->severity = pvSevrMAJOR;
+			ach->message = "already one put pending";
+			status = ach->status;
+			errlogSevPrintf(errlogFatal,
+				"pvPut(ss %s, var %s, pv %s): user error "
+				"(there is already a put pending for this variable/"
+				"state set combination)\n",
+				ss->ssName, ch->varName, ach->dbName
+			);
+			return pvStatERROR;
+		case epicsEventWaitError:
+			errlogSevPrintf(errlogFatal,
+				"pvPut: epicsEventTryWait() failure\n");
+			return pvStatERROR;
+		}
 	}
 
 	/* Determine number of elements to put (don't try to put more
 	   than db count) */
-	count = min(ch->count, ch->dbCount);
+	assert(ach->dbCount <= INT_MAX);
+	count = (int)ach->dbCount;
 
 	/* Perform the PV put operation (either non-blocking or with a
 	   callback routine specified) */
-	if (nonb)
+	if (compType == DEFAULT)
 	{
 		status = pvVarPutNoBlock(
-				ch->pvid,		/* PV id */
+				ach->pvid,		/* PV id */
 				ch->type->putType,	/* data type */
-				(int)count,		/* element count */
+				count,			/* element count */
 				(pvValue *)var);	/* data value */
 	}
 	else
@@ -211,42 +348,50 @@ epicsShareFunc pvStat epicsShareAPI seq_pvPut(SS_ID ss, VAR_ID varId, enum compT
 		req->ch = ch;
 
 		status = pvVarPutCallback(
-				ch->pvid,		/* PV id */
+				ach->pvid,		/* PV id */
 				ch->type->putType,	/* data type */
-				(int)count,		/* element count */
+				count,			/* element count */
 				(pvValue *)var,	/* data value */
 				seq_put_handler,	/* callback handler */
 				req);			/* user arg */
 	}
 	if (status != pvStatOK)
 	{
-		errlogPrintf("seq_pvPut: pvVarPut%s() %s failure: %s\n",
-			nonb ? "NoBlock" : "Callback", ch->dbName,
-			pvVarGetMess(ch->pvid));
+		errlogPrintf("pvPut: pvVarPut%s() %s failure: %s\n",
+			(compType == DEFAULT) ? "NoBlock" : "Callback",
+			ach->dbName, pvVarGetMess(ach->pvid));
 		return status;
 	}
 
 	/* Synchronous: wait for completion (10s timeout) */
-	if (sync) /* => !nonb */
+	if (compType == SYNC)
 	{
-		epicsEventWaitStatus sem_status;
-
-		pvSysFlush(pvSys);
-		sem_status = epicsEventWaitWithTimeout(ch->putSemId, 10.0);
-		if (sem_status != epicsEventWaitOK)
+		pvSysFlush(sp->pvSys);
+		switch (epicsEventWaitWithTimeout(putSem, tmo))
 		{
-			ch->status = pvStatTIMEOUT;
-			ch->severity = pvSevrMAJOR;
-			ch->message = "put completion timeout";
-			return ch->status;
+		case epicsEventWaitOK:
+			break;
+		case epicsEventWaitTimeout:
+			ach->status = pvStatTIMEOUT;
+			ach->severity = pvSevrMAJOR;
+			ach->message = "put completion timeout";
+			status = ach->status;
+			break;
+		case epicsEventWaitError:
+			ach->status = pvStatERROR;
+			ach->severity = pvSevrMAJOR;
+			ach->message = "put completion failure";
+			status = ach->status;
+			break;
 		}
+		epicsEventSignal(putSem);
 	}
 
 	DEBUG("seq_pvPut: status=%d, mess=%s\n", status,
-		pvVarGetMess(ch->pvid));
+		pvVarGetMess(ach->pvid));
 	if (status != pvStatOK)
 	{
-		DEBUG("pvPut on \"%s\" failed (%d)\n", ch->dbName, status);
+		DEBUG("pvPut on \"%s\" failed (%d)\n", ach->dbName, status);
 		DEBUG("  putType=%d\n", ch->type->putType);
 		DEBUG("  size=%d, count=%d\n", ch->type->size, count);
 	}
@@ -264,22 +409,33 @@ epicsShareFunc boolean epicsShareAPI seq_pvPutComplete(
 	boolean		any,
 	boolean		*complete)
 {
-	SPROG	*sp = ss->sprog;
-	long	anyDone = FALSE, allDone = TRUE;
-	unsigned i;
+	boolean		anyDone = FALSE, allDone = TRUE;
+	unsigned	n;
 
-	for (i=0; i<length; i++)
+	for (n = 0; n < length; n++)
 	{
-		CHAN *ch = sp->chan + varId + i;
+		epicsEventId	putSem = ss->putSemId[varId+n];
+		boolean		done = FALSE;
 
-		long done = epicsEventTryWait(ch->putSemId);
+		switch (epicsEventTryWait(putSem))
+		{
+		case epicsEventWaitOK:
+			epicsEventSignal(putSem);
+			done = TRUE;
+			break;
+		case epicsEventWaitError:
+			errlogPrintf("pvPutComplete: "
+			  "epicsEventTryWait(putSemId[%d]) failure\n", varId);
+		case epicsEventWaitTimeout:
+			break;
+		}
 
 		anyDone = anyDone || done;
 		allDone = allDone && done;
 
-		if (complete != NULL)
+		if (complete)
 		{
-			complete[i] = done;
+			complete[n] = done;
 		}
 		else if (any && done)
 		{
@@ -299,104 +455,94 @@ epicsShareFunc boolean epicsShareAPI seq_pvPutComplete(
  */
 epicsShareFunc pvStat epicsShareAPI seq_pvAssign(SS_ID ss, VAR_ID varId, const char *pvName)
 {
-	SPROG	*sp = ss->sprog;
-	CHAN	*ch = sp->chan + varId;
-	pvStat	status;
-        unsigned nchar;
+	SPROG		*sp = ss->sprog;
+	CHAN		*ch = sp->chan + varId;
+	pvStat		status = pvStatOK;
+	unsigned	nchar;
+	ACHAN		*ach = ch->ach;
+
+	if (!pvName) pvName = "";
 
 	DEBUG("Assign %s to \"%s\"\n", ch->varName, pvName);
 
-	if (ch->assigned)
-	{	/* Disconnect this PV */
-		status = pvVarDestroy(ch->pvid);
+	epicsMutexMustLock(sp->programLock);
+
+	if (ach)	/* Disconnect this PV */
+	{
+		status = pvVarDestroy(ach->pvid);
 		if (status != pvStatOK)
 		{
 			errlogPrintf("seq_pvAssign: pvVarDestroy() %s failure: "
-				"%s\n", ch->dbName, pvVarGetMess(ch->pvid));
+				"%s\n", ach->dbName, pvVarGetMess(ach->pvid));
 		}
-		free(ch->dbName);
-		ch->assigned = FALSE;
-		sp->assignCount -= 1;
+		free(ach->dbName);
 	}
 
-	if (ch->connected)
-	{
-		ch->connected = FALSE;
-		sp->connectCount -= 1;
-	}
-	ch->monitored = FALSE;
 	nchar = strlen(pvName);
-	ch->dbName = (char *)calloc(1, nchar + 1);
-	strcpy(ch->dbName, pvName);
 
-	/* Connect */
-	if (nchar > 0)
+	if (nchar == 0)
+	{
+		free(ach);
+		ch->ach = NULL;
+		sp->assignCount -= 1;
+		if (ach->connected)
+		{
+			ach->connected = FALSE;
+			sp->connectCount -= 1;
+		}
+	}
+	else		/* Connect */
 	{
-		ch->assigned = TRUE;
+		if (!ach)
+		{
+			ch->ach = ach = new(ACHAN);
+		}
+		ach->dbName = epicsStrDup(pvName);
+
 		sp->assignCount += 1;
 		status = pvVarCreate(
-			pvSys,			/* PV system context */
-			ch->dbName,		/* DB channel name */
+			sp->pvSys,		/* PV system context */
+			ach->dbName,		/* DB channel name */
 			seq_conn_handler,	/* connection handler routine */
 			ch,			/* user ptr is CHAN structure */
 			0,			/* debug level (inherited) */
-			&ch->pvid);		/* ptr to pvid */
+			&ach->pvid);		/* ptr to pvid */
 		if (status != pvStatOK)
 		{
 			errlogPrintf("seq_pvAssign: pvVarCreate() %s failure: "
-				"%s\n", ch->dbName, pvVarGetMess(ch->pvid));
-			return status;
-		}
-
-		if (ch->monFlag)
-		{
-			status = seq_pvMonitor(ss, varId);
-			if (status != pvStatOK)
-				return status;
+				"%s\n", ach->dbName, pvVarGetMess(ach->pvid));
 		}
 	}
 
-	pvSysFlush(pvSys);
-	
-	return pvStatOK;
+	epicsMutexUnlock(sp->programLock);
+
+	return status;
 }
 
 /*
- * seq_pvMonitor() - Initiate a monitor on a channel.
+ * seq_pvMonitor() - Initiate monitoring on a channel
  */
 epicsShareFunc pvStat epicsShareAPI seq_pvMonitor(SS_ID ss, VAR_ID varId)
 {
 	SPROG	*sp = ss->sprog;
 	CHAN	*ch = sp->chan + varId;
-	int	status;
-
-	DEBUG("seq_pvMonitor \"%s\"\n", ch->dbName);
+	ACHAN	*ach = ch->ach;
 
-/*	if (ch->monitored || !ch->assigned)	*/
-/*	WFL, 96/08/07, don't check monitored because it can get set TRUE */
-/*	in the connection handler before this routine is executed; this */
-/*	fix pending a proper fix */
-	if (!ch->assigned)
+	if (!ach && (sp->options & OPT_SAFE))
+	{
+		ch->monitored = TRUE;
 		return pvStatOK;
-
-	status = pvVarMonitorOn(
-		ch->pvid,		/* pvid */
-		ch->type->getType,	/* requested type */
-		(int)ch->count,	/* element count */
-		seq_mon_handler,	/* function to call */
-		ch,			/* user arg (db struct) */
-		&ch->monid);		/* where to put event id */
-
-	if (status != pvStatOK)
+	}
+	if (!ach)
 	{
-		errlogPrintf("seq_pvMonitor: pvVarMonitorOn() %s failure: %s\n",
-			ch->dbName, pvVarGetMess(ch->pvid));
-		return status;
+		errlogSevPrintf(errlogFatal,
+			"pvMonitor(%s): user error (variable not assigned)\n",
+			ch->varName
+		);
+		return pvStatERROR;
 	}
-	pvSysFlush(pvSys);
-
 	ch->monitored = TRUE;
-	return pvStatOK;
+	return seq_monitor(ch, TRUE);
 }
 
 /*
@@ -404,23 +550,25 @@ epicsShareFunc pvStat epicsShareAPI seq_pvMonitor(SS_ID ss, VAR_ID varId)
  */
 epicsShareFunc pvStat epicsShareAPI seq_pvStopMonitor(SS_ID ss, VAR_ID varId)
 {
-	CHAN	*ch = ss->sprog->chan + varId;
-	int	status;
-
-	if (!ch->monitored)
-		return -1;
+	SPROG	*sp = ss->sprog;
+	CHAN	*ch = sp->chan + varId;
+	ACHAN	*ach = ch->ach;
 
-	status = pvVarMonitorOff(ch->pvid,ch->monid);
-	if (status != pvStatOK)
+	if (!ach && (sp->options & OPT_SAFE))
 	{
-		errlogPrintf("seq_pvStopMonitor: pvVarMonitorOff() %s failure: "
-			"%s\n", ch->dbName, pvVarGetMess(ch->pvid));
-		return status;
+		ch->monitored = FALSE;
+		return pvStatOK;
+	}
+	if (!ach)
+	{
+		errlogSevPrintf(errlogFatal,
+			"pvStopMonitor(%s): user error (variable not assigned)\n",
+			ch->varName
+		);
+		return pvStatERROR;
 	}
-
 	ch->monitored = FALSE;
-
-	return status;
+	return seq_monitor(ch, FALSE);
 }
 
 /*
@@ -429,7 +577,7 @@ epicsShareFunc pvStat epicsShareAPI seq_pvStopMonitor(SS_ID ss, VAR_ID varId)
  */
 epicsShareFunc void epicsShareAPI seq_pvSync(SS_ID ss, VAR_ID varId, EV_ID ev_flag)
 {
-	assert(ev_flag >= 0 && ev_flag <= ss->sprog->numEvents);
+	assert(ev_flag >= 0 && ev_flag <= ss->sprog->numEvFlags);
 	ss->sprog->chan[varId].efId = ev_flag;
 }
 
@@ -438,9 +586,7 @@ epicsShareFunc void epicsShareAPI seq_pvSync(SS_ID ss, VAR_ID varId, EV_ID ev_fl
  */
 epicsShareFunc unsigned epicsShareAPI seq_pvChannelCount(SS_ID ss)
 {
-	SPROG	*sp = ss->sprog;
-
-	return sp->numChans;
+	return ss->sprog->numChans;
 }
 
 /*
@@ -448,9 +594,7 @@ epicsShareFunc unsigned epicsShareAPI seq_pvChannelCount(SS_ID ss)
  */
 epicsShareFunc unsigned epicsShareAPI seq_pvConnectCount(SS_ID ss)
 {
-	SPROG	*sp = ss->sprog;
-
-	return sp->connectCount;
+	return ss->sprog->connectCount;
 }
 
 /*
@@ -458,9 +602,13 @@ epicsShareFunc unsigned epicsShareAPI seq_pvConnectCount(SS_ID ss)
  */
 epicsShareFunc unsigned epicsShareAPI seq_pvAssignCount(SS_ID ss)
 {
-	SPROG	*sp = ss->sprog;
+	return ss->sprog->assignCount;
+}
 
-	return sp->assignCount;
+/* Flush outstanding PV requests */
+epicsShareFunc void epicsShareAPI seq_pvFlush(SS_ID ss)
+{
+	pvSysFlush(ss->sprog->pvSys);
 }
 
 /*
@@ -469,8 +617,7 @@ epicsShareFunc unsigned epicsShareAPI seq_pvAssignCount(SS_ID ss)
 epicsShareFunc boolean epicsShareAPI seq_pvConnected(SS_ID ss, VAR_ID varId)
 {
 	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->connected;
+	return ch->ach && ch->ach->connected;
 }
 
 /*
@@ -478,9 +625,7 @@ epicsShareFunc boolean epicsShareAPI seq_pvConnected(SS_ID ss, VAR_ID varId)
  */
 epicsShareFunc boolean epicsShareAPI seq_pvAssigned(SS_ID ss, VAR_ID varId)
 {
-	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->assigned;
+	return ss->sprog->chan[varId].ach != NULL;
 }
 
 /*
@@ -490,8 +635,7 @@ epicsShareFunc boolean epicsShareAPI seq_pvAssigned(SS_ID ss, VAR_ID varId)
 epicsShareFunc unsigned epicsShareAPI seq_pvCount(SS_ID ss, VAR_ID varId)
 {
 	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->dbCount;
+	return ch->ach ? ch->ach->dbCount : ch->count;
 }
 
 /*
@@ -500,8 +644,7 @@ epicsShareFunc unsigned epicsShareAPI seq_pvCount(SS_ID ss, VAR_ID varId)
 epicsShareFunc char *epicsShareAPI seq_pvName(SS_ID ss, VAR_ID varId)
 {
 	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->dbName;
+	return ch->ach ? ch->ach->dbName : NULL;
 }
 
 /*
@@ -510,8 +653,7 @@ epicsShareFunc char *epicsShareAPI seq_pvName(SS_ID ss, VAR_ID varId)
 epicsShareFunc pvStat epicsShareAPI seq_pvStatus(SS_ID ss, VAR_ID varId)
 {
 	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->status;
+	return ch->ach ? ch->ach->status : pvStatOK;
 }
 
 /*
@@ -520,8 +662,7 @@ epicsShareFunc pvStat epicsShareAPI seq_pvStatus(SS_ID ss, VAR_ID varId)
 epicsShareFunc pvSevr epicsShareAPI seq_pvSeverity(SS_ID ss, VAR_ID varId)
 {
 	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->severity;
+	return ch->ach ? ch->ach->severity : pvSevrOK;
 }
 
 /*
@@ -530,8 +671,7 @@ epicsShareFunc pvSevr epicsShareAPI seq_pvSeverity(SS_ID ss, VAR_ID varId)
 epicsShareFunc const char *epicsShareAPI seq_pvMessage(SS_ID ss, VAR_ID varId)
 {
 	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->message;
+	return ch->ach ? ch->ach->message : "";
 }
 
 /*
@@ -548,9 +688,16 @@ epicsShareFunc VAR_ID epicsShareAPI seq_pvIndex(SS_ID ss, VAR_ID varId)
 epicsShareFunc epicsTimeStamp epicsShareAPI seq_pvTimeStamp(SS_ID ss, VAR_ID varId)
 {
 	CHAN *ch = ss->sprog->chan + varId;
-
-	return ch->timeStamp;
+	if (ch->ach)
+		return ch->ach->timeStamp;
+	else
+	{
+		epicsTimeStamp ts;
+		epicsTimeGetCurrent(&ts);
+		return ts;
+	}
 }
+
 /*
  * seq_efSet() - Set an event flag, then wake up each state
  * set that might be waiting on that event flag.
@@ -559,13 +706,14 @@ epicsShareFunc void epicsShareAPI seq_efSet(SS_ID ss, EV_ID ev_flag)
 {
 	SPROG	*sp = ss->sprog;
 
-	epicsMutexMustLock(sp->programLock);
-
-	DEBUG("seq_efSet: sp=%p, ss=%p, ev_flag=%ld\n", sp, ss,
+	DEBUG("seq_efSet: sp=%p, ss=%p, ev_flag=%d\n", sp, ss,
 		ev_flag);
+	assert(ev_flag > 0 && ev_flag <= ss->sprog->numEvFlags);
+
+	epicsMutexMustLock(sp->programLock);
 
 	/* Set this bit */
-	bitSet(sp->events, ev_flag);
+	bitSet(sp->evFlags, ev_flag);
 
 	/* Wake up state sets that are waiting for this event flag */
 	seqWakeup(sp, ev_flag);
@@ -582,12 +730,12 @@ epicsShareFunc boolean epicsShareAPI seq_efTest(SS_ID ss, EV_ID ev_flag)
 	SPROG	*sp = ss->sprog;
 	int	isSet;
 
+	assert(ev_flag > 0 && ev_flag <= ss->sprog->numEvFlags);
 	epicsMutexMustLock(sp->programLock);
 
-	isSet = bitTest(sp->events, ev_flag);
+	isSet = bitTest(sp->evFlags, ev_flag);
 
-	DEBUG("seq_efTest: ev_flag=%ld, event=%#lx, isSet=%d\n",
-		ev_flag, sp->events[0], isSet);
+	DEBUG("seq_efTest: ev_flag=%d, isSet=%d\n", ev_flag, isSet);
 
 	epicsMutexUnlock(sp->programLock);
 
@@ -602,12 +750,11 @@ epicsShareFunc boolean epicsShareAPI seq_efClear(SS_ID ss, EV_ID ev_flag)
 	SPROG	*sp = ss->sprog;
 	int	isSet;
 
-	isSet = bitTest(sp->events, ev_flag);
-
+	assert(ev_flag > 0 && ev_flag <= ss->sprog->numEvFlags);
 	epicsMutexMustLock(sp->programLock);
 
-	/* Clear this bit */
-	bitClear(sp->events, ev_flag);
+	isSet = bitTest(sp->evFlags, ev_flag);
+	bitClear(sp->evFlags, ev_flag);
 
 	/* Wake up state sets that are waiting for this event flag */
 	seqWakeup(sp, ev_flag);
@@ -625,10 +772,11 @@ epicsShareFunc boolean epicsShareAPI seq_efTestAndClear(SS_ID ss, EV_ID ev_flag)
 	SPROG	*sp = ss->sprog;
 	int	isSet;
 
+	assert(ev_flag > 0 && ev_flag <= ss->sprog->numEvFlags);
 	epicsMutexMustLock(sp->programLock);
 
-	isSet = bitTest(sp->events, ev_flag);
-	bitClear(sp->events, ev_flag);
+	isSet = bitTest(sp->evFlags, ev_flag);
+	bitClear(sp->evFlags, ev_flag);
 
 	epicsMutexUnlock(sp->programLock);
 
@@ -642,16 +790,18 @@ epicsShareFunc boolean epicsShareAPI seq_pvGetQ(SS_ID ss, VAR_ID varId)
 {
 	SPROG	*sp = ss->sprog;
 	CHAN	*ch = sp->chan + varId;
-	char	*var = valPtr(ch,ss);
+	void	*var = valPtr(ch,ss);
 	EV_ID	ev_flag = ch->efId;
 	boolean	isSet;
+	ACHAN	*ach = ch->ach;
 
 	epicsMutexMustLock(sp->programLock);
 
 	/* Determine event flag number and whether set */
-	isSet = bitTest(sp->events, ev_flag);
+	isSet = bitTest(sp->evFlags, ev_flag);
 
-	DEBUG("seq_pvGetQ: pv name=%s, isSet=%d\n", ch->dbName, isSet);
+	DEBUG("seq_pvGetQ: pv name=%s, isSet=%d\n",
+		ach ? ach->dbName : "<anomymous>", isSet);
 
 	/* If set, queue should be non-empty */
 	if (isSet)
@@ -670,15 +820,18 @@ epicsShareFunc boolean epicsShareAPI seq_pvGetQ(SS_ID ss, VAR_ID varId)
 		}
 		else
 		{
-			assert(pv_is_time_type(type));
-			/* Copy status, severity and time stamp */
-			ch->status = *pv_status_ptr(value,type);
-			ch->severity = *pv_severity_ptr(value,type);
-			ch->timeStamp = *pv_stamp_ptr(value,type);
-			memcpy(var, pv_value_ptr(value,type), ch->type->size * ch->count);
-			/* If queue is now empty, clear the event flag */
-			if (seqQueueIsEmpty(queue))
-				bitClear(sp->events, ev_flag);
+			if (ach)
+			{
+				assert(pv_is_time_type(type));
+				/* Copy status, severity and time stamp */
+				ach->status = *pv_status_ptr(value,type);
+				ach->severity = *pv_severity_ptr(value,type);
+				ach->timeStamp = *pv_stamp_ptr(value,type);
+				memcpy(var, pv_value_ptr(value,type), ch->type->size * ch->count);
+				/* If queue is now empty, clear the event flag */
+				if (seqQueueIsEmpty(queue))
+					bitClear(sp->evFlags, ev_flag);
+			}
 		}
 	}
 	epicsMutexUnlock(sp->programLock);
@@ -706,13 +859,13 @@ epicsShareFunc void epicsShareAPI seq_pvFlushQ(SS_ID ss, VAR_ID varId)
 	EV_ID	ev_flag = ch->efId;
 	QUEUE	queue = ch->queue;
 
-	DEBUG("seq_pvFlushQ: pv name=%s, count=%d\n",
-		ch->dbName, seqQueueUsed(queue));
+	DEBUG("seq_pvFlushQ: pv name=%s, count=%d\n", ch->ach ? ch->ach->dbName : "<anomymous>",
+		seqQueueUsed(queue));
 	seqQueueFlush(queue);
 
 	epicsMutexMustLock(sp->programLock);
 	/* Clear event flag */
-	bitClear(sp->events, ev_flag);
+	bitClear(sp->evFlags, ev_flag);
 	epicsMutexUnlock(sp->programLock);
 }
 
@@ -759,6 +912,7 @@ epicsShareFunc boolean epicsShareAPI seq_optGet(SS_ID ss, const char *opt)
 {
 	SPROG	*sp = ss->sprog;
 
+	assert(opt);
 	switch (opt[0])
 	{
 	case 'a': return ( (sp->options & OPT_ASYNC) != 0);
diff --git a/src/seq/seq_mac.c b/src/seq/seq_mac.c
index 1d235b206c6eaa445fdbeeaeb4fb66eb3df78dfa..4adf99dcffc6c4b43b0b2c65b4e838b545b859d9 100644
--- a/src/seq/seq_mac.c
+++ b/src/seq/seq_mac.c
@@ -27,7 +27,7 @@ struct macro
 
 static unsigned seqMacParseName(const char *str);
 static unsigned seqMacParseValue(const char *str);
-static const char *skipBlanks(const char *str);
+static const char *skipBlanks(const char *pchr);
 static MACRO *seqMacTblGet(SPROG *sp, char *name);
 
 /* 
@@ -230,11 +230,11 @@ static unsigned seqMacParseValue(const char *str)
 }
 
 /* skipBlanks() - skip blank characters */
-static const char *skipBlanks(const char *str)
+static const char *skipBlanks(const char *pchr)
 {
-	while (*str == ' ')
-		str++;
-	return str;
+	while (*pchr == ' ')
+		pchr++;
+	return	pchr;
 }
 
 /*
diff --git a/src/seq/seq_main.c b/src/seq/seq_main.c
index e61aee77e412553f16e0610a0afa3dd6a29f5e99..5eea3a0defc4d894ae5699c9b60f4a3bbddfba0a 100644
--- a/src/seq/seq_main.c
+++ b/src/seq/seq_main.c
@@ -13,20 +13,13 @@
 /* #define DEBUG printf */
 #define DEBUG nothing
 
-/* function prototypes for local routines */
-static void seqInitTables(SPROG *, seqProgram *);
-static void init_sprog(seqProgram *, SPROG *);
-static void init_sscb(seqProgram *, SPROG *);
-static void init_chan(seqProgram *, SPROG *);
+static void init_sprog(SPROG *sp, seqProgram *seqProg);
+static void init_sscb(SPROG *sp, SSCB *ss, seqSS *seqSS);
+static void init_chan(SPROG *sp, CHAN *ch, seqChan *seqChan);
 static PVTYPE *find_type(const char *userType);
 
-/*	Globals */
-
-/*	Auxiliary sequencer thread id; used to share PV context. */
-epicsThreadId seqAuxThreadId = (epicsThreadId) 0;
-
 /*
- * seq: User-callable routine to run a state program.
+ * seq: Run a state program.
  * Usage:  seq(<sp>, <macros string>, <stack size>)
  *	sp is the ptr to the state program structure.
  *	Example:  seq(&myprog, "logfile=mylog", 0)
@@ -40,10 +33,9 @@ epicsShareFunc void epicsShareAPI seq(
 {
 	epicsThreadId	tid;
 	SPROG		*sp;
-	char		*value;
+	char		*str;
 	const char	*threadName;
-	unsigned	smallStack;
-	AUXARGS		auxArgs;
+	unsigned int	smallStack;
 
 	/* Print version & date of sequencer */
 	printf(SEQ_VERSION "\n");
@@ -51,6 +43,7 @@ epicsShareFunc void epicsShareAPI seq(
 	/* Exit if no parameters specified */
 	if (seqProg == 0)
 	{
+		errlogPrintf("Bad first argument seqProg (is NULL)\n");
 		return;
 	}
 
@@ -61,11 +54,11 @@ epicsShareFunc void epicsShareAPI seq(
 		errlogPrintf(" - Possible mismatch between SNC & SEQ "
 			"versions\n");
 		errlogPrintf(" - Re-compile your program?\n");
-		epicsThreadSleep( 1.0 );	/* let error messages get printed */
+		epicsThreadSleep(1.0);	/* let error messages get printed */
 		return;
 	}
 
-	sp = (SPROG *)calloc(1, sizeof (SPROG));
+	sp = new(SPROG);
 
 	/* Parse the macro definitions from the "program" statement */
 	seqMacParse(sp, seqProg->params);
@@ -73,17 +66,16 @@ epicsShareFunc void epicsShareAPI seq(
 	/* Parse the macro definitions from the command line */
 	seqMacParse(sp, macroDef);
 
-	/* Initialize the sequencer tables */
-	seqInitTables(sp, seqProg);
-
+	/* Initialize program struct */
+	init_sprog(sp, seqProg);
 
 	/* Specify stack size */
 	if (stackSize == 0)
 		stackSize = epicsThreadGetStackSize(THREAD_STACK_SIZE);
-	value = seqMacValGet(sp, "stack");
-	if (value != NULL && strlen(value) > 0)
+	str = seqMacValGet(sp, "stack");
+	if (str != NULL && strlen(str) > 0)
 	{
-		sscanf(value, "%ud", &stackSize);
+		sscanf(str, "%ud", &stackSize);
 	}
 	smallStack = epicsThreadGetStackSize(epicsThreadStackSmall);
 	if (stackSize < smallStack)
@@ -91,89 +83,55 @@ epicsShareFunc void epicsShareAPI seq(
 	sp->stackSize = stackSize;
 
 	/* Specify thread name */
-	value = seqMacValGet(sp, "name");
-	if (value != NULL && strlen(value) > 0)
-		threadName = value;
+	str = seqMacValGet(sp, "name");
+	if (str != NULL && strlen(str) > 0)
+		threadName = str;
 	else
 		threadName = sp->progName;
 
 	/* Specify PV system name (defaults to CA) */
-	value = seqMacValGet(sp, "pvsys");
-	if (value != NULL && strlen(value) > 0)
-		auxArgs.pvSysName = value;
+	str = seqMacValGet(sp, "pvsys");
+	if (str != NULL && strlen(str) > 0)
+		sp->pvSysName = str;
 	else
-		auxArgs.pvSysName = "ca";
+		sp->pvSysName = "ca";
 
 	/* Determine debug level (currently only used for PV-level debugging) */
-	value = seqMacValGet(sp, "debug");
-	if (value != NULL && strlen(value) > 0)
-		auxArgs.debug = atol(value);
+	str = seqMacValGet(sp, "debug");
+	if (str != NULL && strlen(str) > 0)
+		sp->debug = atol(str);
 	else
-		auxArgs.debug = 0;
+		sp->debug = 0;
 
-	/* Spawn the sequencer auxiliary thread */
-	if (seqAuxThreadId == (epicsThreadId) 0)
-	{
-		unsigned auxStack = epicsThreadGetStackSize(epicsThreadStackMedium);
-		epicsThreadCreate("seqAux", THREAD_PRIORITY+1, auxStack,
-				(EPICSTHREADFUNC)seqAuxThread, &auxArgs);
-		while (seqAuxThreadId == (epicsThreadId) 0)
-			/* wait for thread to init. message system */
-			epicsThreadSleep(0.1);
-
-		if (seqAuxThreadId == (epicsThreadId) -1)
-		{
-			epicsThreadSleep( 1.0 );	/* let error messages get printed */
-			return;
-		}
-		DEBUG("thread seqAux spawned, tid=%p\n", seqAuxThreadId);
-	}
-
-	/* Spawn the initial sequencer thread */
-	DEBUG("Spawning thread %s, stackSize=%d\n", threadName,
-		sp->stackSize);
 	/* Specify thread priority */
 	sp->threadPriority = THREAD_PRIORITY;
-	value = seqMacValGet(sp, "priority");
-	if (value != NULL && strlen(value) > 0)
+	str = seqMacValGet(sp, "priority");
+	if (str != NULL && strlen(str) > 0)
 	{
-		sscanf(value, "%ud", &(sp->threadPriority));
+		sscanf(str, "%ud", &(sp->threadPriority));
 	}
 	if (sp->threadPriority > THREAD_PRIORITY)
 		sp->threadPriority = THREAD_PRIORITY;
 
-	tid = epicsThreadCreate(threadName, sp->threadPriority, sp->stackSize,
-		(EPICSTHREADFUNC)sequencer, sp);
+	tid = epicsThreadCreate(threadName, sp->threadPriority,
+		sp->stackSize, sequencer, sp);
 
-	printf("Spawning state program \"%s\", thread %p: \"%s\"\n",
+	printf("Spawning sequencer program \"%s\", thread %p: \"%s\"\n",
 		sp->progName, tid, threadName);
 }
 
-/* seqInitTables - initialize sequencer tables */
-static void seqInitTables(SPROG *sp, seqProgram *seqProg)
-{
-	/* Initialize state program block */
-	init_sprog(seqProg, sp);
-
-	/* Initialize state set control blocks */
-	init_sscb(seqProg, sp);
-
-	/* Initialize database channel blocks */
-	init_chan(seqProg, sp);
-}
-
 /*
  * Copy data from seqCom.h structures into this thread's dynamic structures
  * as defined in seq.h.
  */
-static void init_sprog(seqProgram *seqProg, SPROG *sp)
+static void init_sprog(SPROG *sp, seqProgram *seqProg)
 {
-	unsigned nWords;
+	unsigned nss, nch;
 
 	/* Copy information for state program */
 	sp->numSS = seqProg->numSS;
 	sp->numChans = seqProg->numChans;
-	sp->numEvents = seqProg->numEvents;
+	sp->numEvFlags = seqProg->numEvFlags;
 	sp->options = seqProg->options;
 	sp->progName = seqProg->progName;
 	sp->initFunc = seqProg->initFunc;
@@ -184,167 +142,168 @@ static void init_sprog(seqProgram *seqProg, SPROG *sp)
 
 	/* Allocate user variable area if reentrant option (+r) is set */
 	if (sp->options & OPT_REENT)
-	{
-		sp->var = (char *)calloc(sp->varSize, 1);
-	}
+		sp->var = (USER_VAR *)calloc(1, sp->varSize);
+	else
+		sp->var = NULL;
 
-	DEBUG("init_sprog: num SS=%ld, num Chans=%ld, num Events=%ld, "
-		"Prog Name=%s, var Size=%ld\n", sp->numSS, sp->numChans,
-		sp->numEvents, sp->progName, sp->varSize);
+	DEBUG("init_sprog: numSS=%d, numChans=%d, numEvFlags=%u, "
+		"progName=%s, varSize=%u\n", sp->numSS, sp->numChans,
+		sp->numEvFlags, sp->progName, sp->varSize);
 
-	/* Create a semaphore for resource locking on PV events */
+	/* Create semaphores */
 	sp->programLock = epicsMutexMustCreate();
-	sp->connectCount = 0;
-	sp->assignCount = 0;
-	sp->allDisconnected = TRUE;
+	sp->ready = epicsEventMustCreate(epicsEventEmpty);
+	sp->dead = epicsEventMustCreate(epicsEventEmpty);
 
-	/* Allocate an array for event flag bits */
-	nWords = (sp->numEvents + NBITS - 1) / NBITS;
-	if (nWords == 0)
-		nWords = 1;
-	sp->events = (bitMask *)calloc(nWords, sizeof(bitMask));
-
-	/* Allocate and initialize syncQ queues */
-	sp->numQueues = seqProg->numQueues;
-	sp->queues = NULL;
+	/* Allocate an array for event flag bits. Note this does
+	   *not* reserve space for all event numbers (i.e. including
+	   channels), only for event flags. */
+	sp->evFlags = newArray(bitMask, NWORDS(sp->numEvFlags));
 
 	/* Allocate and initialize syncQ queues */
 	if (sp->numQueues > 0)
 	{
 		sp->queues = newArray(QUEUE, sp->numQueues);
 	}
-	/* initial pool for pv requests is 1kB on 32-bit systems */
+	/* Initial pool for pv requests is 1kB on 32-bit systems */
 	freeListInitPvt(&sp->pvReqPool, 128, sizeof(PVREQ));
-}
-
-/*
- * Initialize the state set control blocks
- */
-static void init_sscb(seqProgram *seqProg, SPROG *sp)
-{
-	SSCB		*ss;
-	unsigned	nss;
-	seqSS		*seqSS;
-
 
-	/* Allocate space for the SSCB structures */
-	sp->ss = ss = (SSCB *)calloc(seqProg->numSS, sizeof(SSCB));
+	/* Allocate array of state set structs and initialize it */
+	sp->ss = newArray(SSCB, sp->numSS);
+	for (nss = 0; nss < sp->numSS; nss++)
+	{
+		init_sscb(sp, sp->ss + nss, seqProg->ss + nss);
+	}
 
-	/* Copy information for each state set and state */
-	seqSS = seqProg->ss;
-	for (nss = 0; nss < seqProg->numSS; nss++, ss++, seqSS++)
+	/* Allocate array of channel structs and initialize it */
+	sp->chan = newArray(CHAN, sp->numChans);
+	for (nch = 0; nch < sp->numChans; nch++)
 	{
-		/* Fill in SSCB */
-		ss->ssName = seqSS->ssName;
-		ss->numStates = seqSS->numStates;
-		ss->maxNumDelays = seqSS->numDelays;
-
-		ss->delay = (double *)calloc(ss->maxNumDelays, sizeof(double));
-		ss->delayExpired = (boolean *)calloc(ss->maxNumDelays, sizeof(boolean));
-		ss->currentState = 0; /* initial state */
-		ss->nextState = 0;
-		ss->prevState = 0;
-		ss->threadId = 0;
-		/* Initialize to start time rather than zero time! */
-		pvTimeGetCurrentDouble(&ss->timeEntered);
-		ss->sprog = sp;
-
-		DEBUG("init_sscb: SS Name=%s, num States=%ld, ss=%p\n",
-			ss->ssName, ss->numStates, ss);
-		ss->allFirstConnectAndMonitorSemId = epicsEventMustCreate(epicsEventEmpty);
-		/* Create a binary semaphore for synchronizing events in a SS */
-		ss->syncSemId = epicsEventMustCreate(epicsEventEmpty);
-
-		/* Create binary semaphores for synchronous pvGet() and
-		   pvPut() */
-		ss->getSemId = epicsEventMustCreate(epicsEventFull);
-
-		/* Create binary semaphores for thread death */
-		ss->death1SemId = epicsEventMustCreate(epicsEventEmpty);
-		ss->death2SemId = epicsEventMustCreate(epicsEventEmpty);
-		ss->death3SemId = epicsEventMustCreate(epicsEventEmpty);
-		ss->death4SemId = epicsEventMustCreate(epicsEventEmpty);
-
-		/* No need to copy the state structs, they can be shared
-		   because nothing gets mutated. */
-		ss->states = seqSS->states;
-
-		/* Allocate user variable area if safe mode option (+s) is set */
-		if (sp->options & OPT_SAFE)
-		{
-			sp->var = (char *)calloc(sp->varSize, 1);
-		}
+		init_chan(sp, sp->chan + nch, seqProg->chan + nch);
 	}
+}
 
-	DEBUG("init_sscb: numSS=%ld\n", sp->numSS);
+/*
+ * Initialize a state set control block
+ */
+static void init_sscb(SPROG *sp, SSCB *ss, seqSS *seqSS)
+{
+	unsigned nch;
+
+	/* Fill in SSCB */
+	ss->ssName = seqSS->ssName;
+	ss->numStates = seqSS->numStates;
+	ss->maxNumDelays = seqSS->numDelays;
+
+	ss->delay = newArray(double, ss->maxNumDelays);
+	ss->delayExpired = newArray(boolean, ss->maxNumDelays);
+	ss->currentState = 0; /* initial state */
+	ss->nextState = 0;
+	ss->prevState = 0;
+	ss->threadId = 0;
+	/* Initialize to start time rather than zero time! */
+	pvTimeGetCurrentDouble(&ss->timeEntered);
+	ss->sprog = sp;
+
+	ss->syncSemId = epicsEventMustCreate(epicsEventEmpty);
+
+	ss->getSemId = newArray(epicsEventId, sp->numChans);
+	for (nch = 0; nch < sp->numChans; nch++)
+		ss->getSemId[nch] = epicsEventMustCreate(epicsEventFull);
+
+	ss->putSemId = newArray(epicsEventId, sp->numChans);
+	for (nch = 0; nch < sp->numChans; nch++)
+		ss->putSemId[nch] = epicsEventMustCreate(epicsEventFull);
+	ss->dead = epicsEventMustCreate(epicsEventEmpty);
+
+	ss->dirty = newArray(boolean, sp->numChans);
+
+	/* No need to copy the state structs, they can be shared
+	   because nothing gets mutated. */
+	ss->states = seqSS->states;
+
+	/* Allocate user variable area if safe mode option (+s) is set */
+	if (sp->options & OPT_SAFE)
+		ss->var = (USER_VAR *)calloc(1, sp->varSize);
+	else
+		ss->var = NULL;
 }
 
 /*
  * init_chan--Build the database channel structures.
  * Note:  Actual PV name is not filled in here. */
-static void init_chan(seqProgram *seqProg, SPROG *sp)
+static void init_chan(SPROG *sp, CHAN *ch, seqChan *seqChan)
 {
-	unsigned	nchan;
-	CHAN		*ch;
-	seqChan		*seqChan;
-
-	/* Allocate space for the CHAN structures */
-	sp->chan = (CHAN *)calloc(sp->numChans, sizeof(CHAN));
-	ch = sp->chan;
-
-	seqChan = seqProg->chan;
-	for (nchan = 0; nchan < sp->numChans; nchan++, ch++, seqChan++)
+	DEBUG("init_chan: ch=%p\n", ch);
+	ch->sprog = sp;
+	ch->varName = seqChan->varName;
+	ch->offset = seqChan->offset;
+	ch->count = seqChan->count;
+	if (ch->count == 0) ch->count = 1;
+	ch->efId = seqChan->efId;
+	ch->monitored = seqChan->monitored;
+	ch->eventNum = seqChan->eventNum;
+
+	if (seqChan->dbAsName != NULL)
 	{
-		DEBUG("init_chan: ch=%p\n", ch);
-		ch->sprog = sp;
-		ch->varName = seqChan->varName;
-		ch->offset = seqChan->offset;
-		ch->count = seqChan->count;
-		ch->efId = seqChan->efId;
-		ch->monFlag = seqChan->monitored;
-		ch->eventNum = seqChan->eventNum;
-		ch->assigned = 0;
-
-		if (seqChan->dbAsName != NULL)
+		char name_buffer[100];
+
+		seqMacEval(sp, seqChan->dbAsName, name_buffer, sizeof(name_buffer));
+		if (name_buffer[0])
 		{
-			char name_buffer[100];
-
-			seqMacEval(sp, seqChan->dbAsName, name_buffer, sizeof(name_buffer));
-			if (name_buffer[0])
-			{
-				ch->dbAsName = epicsStrDup(name_buffer);
-			}
-			DEBUG("  assigned name=%s, expanded name=%s\n",
-				seqChan->dbAsName, ch->dbAsName);
+			ACHAN	*ach = new(ACHAN);
+			ach->dbName = epicsStrDup(name_buffer);
+			ch->ach = ach;
 		}
-		else
-			DEBUG("  pv name=<anonymous>\n");
-
-		/* Latest error message (dynamically allocated) */
-		ch->message = NULL;
+		DEBUG("  assigned name=%s, expanded name=%s\n",
+			seqChan->dbAsName, ch->ach->dbName);
+	}
+	else
+		DEBUG("  pv name=<anonymous>\n");
 
-		/* Fill in get/put db types, element size */
-		ch->type = find_type(seqChan->varType);
+	/* Fill in get/put db types, element size */
+	ch->type = find_type(seqChan->varType);
 
-		DEBUG(" Assigned Name=%s, VarName=%s, VarType=%s, count=%ld\n"
-			"   size=%u, efId=%ld, monFlag=%u, eventNum=%ld\n",
-			ch->dbAsName, ch->varName,
-			ch->type->typeStr, ch->count,
-			ch->type->size,
-			ch->efId, ch->monFlag, ch->eventNum);
+	DEBUG("  varname=%s, count=%u\n"
+		"  efId=%u, monitored=%u, eventNum=%u\n",
+		ch->varName, ch->count,
+		ch->efId, ch->monitored, ch->eventNum);
+	DEBUG("  type=%p: typeStr=%s, putType=%d, getType=%d, size=%d\n",
+		ch->type, ch->type->typeStr,
+		ch->type->putType, ch->type->getType, ch->type->size);
 
-		ch->varLock = epicsMutexMustCreate();
-		ch->dirty = (boolean *)calloc(sp->numSS,sizeof(boolean));
-		ch->getComplete = (boolean *)calloc(sp->numSS,sizeof(boolean));
-		ch->putSemId = epicsEventMustCreate(epicsEventFull);
+	if (seqChan->queued)
+	{
+		/* We want to store the whole pv message in the queue,
+		   so that we can extract status etc when we remove
+		   the message. */
+		size_t size = pv_size_n(ch->type->getType, ch->count);
+
+		if (sp->queues[seqChan->queueIndex] == NULL)
+			sp->queues[seqChan->queueIndex] =
+				seqQueueCreate(seqChan->queueSize, size);
+		else
+		{
+			assert(seqQueueNumElems(sp->queues[seqChan->queueIndex])
+				== seqChan->queueSize);
+			assert(seqQueueElemSize(sp->queues[seqChan->queueIndex])
+				== size);
+		}
+		ch->queue = sp->queues[seqChan->queueIndex];
+		DEBUG("  queued=%d, queueSize=%d, queueIndex=%d, queue=%p\n",
+			seqChan->queued, seqChan->queueSize,
+			seqChan->queueIndex, ch->queue);
+		DEBUG("  queue->numElems=%d, queue->elemSize=%d\n",
+			seqQueueNumElems(ch->queue), seqQueueElemSize(ch->queue));
 	}
+	ch->varLock = epicsMutexMustCreate();
 }
 
 /*
- * find_type -- returns types for DB put/get and element size
- * based on user variable type.
+ * find_type() -- returns types for DB put/get, element size based on user variable type.
  * Mapping is determined by the following pv_type_map[] array.
- * pvTypeTIME_* types for gets/monitors return status and time stamp.
+ * pvTypeTIME_* types for gets/monitors return status, severity, and time stamp
+ * in addition to the value.
  */
 static PVTYPE pv_type_map[] =
 {
@@ -376,3 +335,60 @@ static PVTYPE *find_type(const char *userType)
 	}
 	return pt;
 }
+
+/* Free all allocated memory in a program structure */
+void seq_free(SPROG *sp)
+{
+	unsigned nss, nch;
+
+	/* Delete state sets */
+	for (nss = 0; nss < sp->numSS; nss++)
+	{
+		SSCB *ss = sp->ss + nss;
+
+		epicsEventDestroy(ss->syncSemId);
+		for (nch = 0; nch < sp->numChans; nch++)
+		{
+			epicsEventDestroy(ss->getSemId[nch]);
+			epicsEventDestroy(ss->putSemId[nch]);
+		}
+		free(ss->getSemId);
+		free(ss->putSemId);
+
+		epicsEventDestroy(ss->dead);
+
+                if (ss->delay) free(ss->delay);
+                if (ss->delayExpired) free(ss->delayExpired);
+                if (ss->dirty) free(ss->dirty);
+		if (ss->var) free(ss->var);
+	}
+
+	/* Delete program-wide semaphores */
+	epicsMutexDestroy(sp->programLock);
+	if (sp->ready) epicsEventDestroy(sp->ready);
+
+	seqMacFree(sp);
+
+	for (nch = 0; nch < sp->numChans; nch++)
+	{
+		CHAN *ch = sp->chan + nch;
+
+		if (ch->ach)
+		{
+			if (ch->ach->dbName != NULL)
+				free(ch->ach->dbName);
+			free(ch->ach);
+		}
+	}
+	free(sp->chan);
+	free(sp->ss);
+	if (sp->queues) {
+		unsigned nq;
+		for (nq = 0; nq < sp->numQueues; nq++)
+			seqQueueDestroy(sp->queues[nq]);
+		free(sp->queues);
+	}
+	if (sp->evFlags) free(sp->evFlags);
+	if (sp->var) free(sp->var);
+	free(sp);
+}
diff --git a/src/seq/seq_prog.c b/src/seq/seq_prog.c
index 21580ddf2101c5e8444f05118f85e08659928c20..dc78075c73705c052bf1ed540ee01be6b4fd46d9 100644
--- a/src/seq/seq_prog.c
+++ b/src/seq/seq_prog.c
@@ -33,13 +33,13 @@ struct findStateSetArgs {
 static int findStateSet(SPROG *sp, void *param)
 {
     struct findStateSetArgs *pargs = (struct findStateSetArgs *)param;
-    unsigned n;
+    unsigned nss;
 
-    for (n = 0; n < sp->numSS; n++) {
-        SSCB *ss = sp->ss + n;
+    for (nss = 0; nss < sp->numSS; nss++) {
+        SSCB *ss = sp->ss + nss;
 
         DEBUG("findStateSet trying %s[%d] ss[%d].threadId=%p\n",
-            sp->progName, sp->instance, n, ss->threadId);
+            sp->progName, sp->instance, nss, ss->threadId);
         if (ss->threadId == pargs->threadId) {
             pargs->ss = ss;
             return TRUE;
@@ -100,7 +100,7 @@ static int traverseInstances(SPROG **ppInstances, seqProgram *pseq, void *param)
 {
     struct traverseInstancesArgs *pargs = (struct traverseInstancesArgs *)param;
     SPROG *sp;
-
+    if (!ppInstances) return FALSE;
     foreach(sp, *ppInstances) {
         if (pargs->func(sp, pargs->param))
             return TRUE;    /* terminate traversal */
@@ -113,7 +113,7 @@ static int traverseInstances(SPROG **ppInstances, seqProgram *pseq, void *param)
  * call the specified routine or function.  Passes one parameter of
  * pointer size.
  */
-void seqTraverseProg(seqTraversee *func, void *param)
+void seqTraverseProg(seqTraversee * func, void *param)
 {
     struct traverseInstancesArgs args;
     args.func = func;
@@ -125,11 +125,31 @@ static int addProg(SPROG **ppInstances, seqProgram *pseq, void *param)
 {
     SPROG *sp = (SPROG *)param;
 
-    assert(ppInstances);
+    if (!ppInstances || !pseq) {
+        if (!sp->pvSys) {
+            seqCreatePvSys(sp);
+        }
+        return FALSE;
+    }
+    assert(sp->pvSysName);
+    /* search for an instance with the same pvSysName */
+    if (!sp->pvSys) {
+        SPROG *curSP;
+        foreach(curSP, *ppInstances) {
+            if (strcmp(sp->pvSysName, curSP->pvSysName) == 0) {
+                DEBUG("Found a program instance (%p[%d]) with pvSys=%s.\n",
+                    curSP, curSP->instance, curSP->pvSysName);
+                sp->pvSys = curSP->pvSys;
+            }
+            break;
+        }
+    }
+    /* same program name */
     if (strcmp(sp->progName, pseq->progName) == 0) {
         SPROG *curSP, *lastSP = NULL;
         int instance = -1;
 
+        /* determine maximum instance number for this program */
         foreach(curSP, *ppInstances) {
             lastSP = curSP;
             /* check precondition */
@@ -137,13 +157,15 @@ static int addProg(SPROG **ppInstances, seqProgram *pseq, void *param)
             instance = max(curSP->instance, instance);
         }
         sp->instance = instance + 1;
-        if (lastSP != NULL) {
+        if (lastSP) {
             lastSP->next = sp;
         } else {
             *ppInstances = sp;
         }
-        DEBUG("Added program %p, instance %d to instance list.\n", sp, sp->instance);
-        return TRUE;
+        DEBUG("Added program %p, instance %d to instance list.\n",
+            sp, sp->instance);
+        if (sp->pvSys)
+            return TRUE;
     }
     return FALSE;
 }
@@ -161,7 +183,8 @@ static int delProg(SPROG **ppInstances, seqProgram *pseq, void *param)
 {
     SPROG *sp = (SPROG *)param;
 
-    assert(ppInstances);
+    if (!ppInstances || !pseq)
+        return FALSE;
     if (strcmp(sp->progName, pseq->progName) == 0) {
         SPROG *curSP;
 
diff --git a/src/seq/seq_qry.c b/src/seq/seq_qry.c
index 84e6ba0622d9c993a78340063a3a89e6d35383fb..c9bf102e9669c3e2b1e2d803cbb7aafff7f8819b 100644
--- a/src/seq/seq_qry.c
+++ b/src/seq/seq_qry.c
@@ -11,11 +11,16 @@
 ***************************************************************************/
 #include "seq.h"
 
-static int wait_rtn(void);
-static void printValue(void *val, unsigned count, int type);
-static SPROG *seqQryFind(epicsThreadId tid);
+static int userInput(void);
+static void printValue(pr_fun *pr, void *val, unsigned count, int type);
+static SSCB *seqQryFind(epicsThreadId tid);
 static void seqShowAll(void);
 
+void print_channel_value(pr_fun *pr, CHAN *ch, void *val)
+{
+	printValue(pr, val, ch->count, ch->type->putType);
+}
+
 /*
  * seqShow() - Query the sequencer for state information.
  * If a non-zero thread id is specified then print the information about
@@ -23,42 +28,41 @@ static void seqShowAll(void);
  */
 epicsShareFunc void epicsShareAPI seqShow(epicsThreadId tid)
 {
+	SSCB	*ss = seqQryFind(tid);
 	SPROG	*sp;
-	SSCB	*ss;
 	STATE	*st;
 	unsigned nss;
 	double	timeNow, timeElapsed;
 
-	sp = seqQryFind(tid);
-	if (sp == NULL)
-		return;
+	if (ss == NULL) return;
+	sp = ss->sprog;
 
 	/* Print info about state program */
 	printf("State Program: \"%s\"\n", sp->progName);
-	printf("  initial thread id = %p\n", sp->threadId);
 	printf("  thread priority = %d\n", sp->threadPriority);
-	printf("  number of state sets = %u\n", sp->numSS);
-	printf("  number of syncQ queues = %u\n", sp->numQueues);
+	printf("  number of state sets = %d\n", sp->numSS);
+	printf("  number of syncQ queues = %d\n", sp->numQueues);
 	if (sp->numQueues > 0)
 		printf("  queue array address = %p\n",sp->queues);
-	printf("  number of channels = %u\n", sp->numChans);
-	printf("  number of channels assigned = %u\n", sp->assignCount);
-	printf("  number of channels connected = %u\n", sp->connectCount);
+	printf("  number of channels = %d\n", sp->numChans);
+	printf("  number of channels assigned = %d\n", sp->assignCount);
+	printf("  number of channels connected = %d\n", sp->connectCount);
+	printf("  number of channels monitored = %d\n", sp->monitorCount);
 	printf("  options: async=%d, debug=%d, newef=%d, reent=%d, conn=%d, "
 		"main=%d\n",
-	 ((sp->options & OPT_ASYNC) != 0), ((sp->options & OPT_DEBUG) != 0),
-	 ((sp->options & OPT_NEWEF) != 0), ((sp->options & OPT_REENT) != 0),
-	 ((sp->options & OPT_CONN)  != 0), ((sp->options & OPT_MAIN)  != 0));
-	if ((sp->options & OPT_REENT) != 0)
-		printf("  user variables: address = %p, length = %u "
-			"= 0x%x bytes\n",
-			sp->var, sp->varSize, sp->varSize);
+		(sp->options & OPT_ASYNC) != 0, (sp->options & OPT_DEBUG) != 0,
+		(sp->options & OPT_NEWEF) != 0, (sp->options & OPT_REENT) != 0,
+		(sp->options & OPT_CONN)  != 0, (sp->options & OPT_MAIN)  != 0);
+	if (sp->options & OPT_REENT)
+		printf("  user variables: address = %p, length = %u\n",
+			sp->var, sp->varSize);
 	printf("\n");
 
 	/* Print state set info */
-	for (nss = 0, ss = sp->ss; nss < sp->numSS; nss++, ss++)
+	for (nss = 0; nss < sp->numSS; nss++)
 	{
 		unsigned n;
+		SSCB	*ss = sp->ss + nss;
 
 		printf("  State Set: \"%s\"\n", ss->ssName);
 
@@ -69,7 +73,7 @@ epicsShareFunc void epicsShareAPI seqShow(epicsThreadId tid)
 			printf("  thread name = %s;", threadName);
 		}
 
-		printf("  thread id = %p\n", ss->threadId);
+		printf("  Thread id = %p\n", ss->threadId);
 
 		st = ss->states;
 		printf("  First state = \"%s\"\n", st->stateName);
@@ -85,34 +89,51 @@ epicsShareFunc void epicsShareAPI seqShow(epicsThreadId tid)
 		timeElapsed = timeNow - ss->timeEntered;
 		printf("  Elapsed time since state was entered = %.1f "
 			"seconds\n", timeElapsed);
+
+		printf("  Get in progress = [");
+		for (n = 0; n < sp->numChans; n++)
+			printf("%d",!seq_pvGetComplete(ss, n));
+		printf("]\n");
+
+		printf("  Put in progress = [");
+		for (n = 0; n < sp->numChans; n++)
+			printf("%d",!seq_pvPutComplete(ss, n, 0, 0, 0));
+		printf("]\n");
+
 		printf("  Queued time delays:\n");
 		for (n = 0; n < ss->numDelays; n++)
 		{
-			printf("\tdelay[%2d]=%f", n, ss->delay[n]);
+			printf("\tdelay[%d]=%f", n, ss->delay[n]);
 			if (ss->delayExpired[n])
 				printf(" - expired");
 			printf("\n");
 		}
+
+		if (sp->options & OPT_SAFE)
+			printf("  User variables: address = %p, length = %u\n",
+				sp->var, sp->varSize);
 		printf("\n");
 	}
 }
+
 /*
  * seqChanShow() - Show channel information for a state program.
  */
 epicsShareFunc void epicsShareAPI seqChanShow(epicsThreadId tid, const char *str)
 {
+	SSCB	*ss = seqQryFind(tid);
 	SPROG	*sp;
-	CHAN	*ch;
-	int	nch;
-        int	n;
+	int	nch = 0;
+	int	dn = 1;
 	char	tsBfr[50], connQual;
 	int	match, showAll;
 
-	sp = seqQryFind(tid);
-	if(!sp) return;
+	if (ss == NULL) return;
+	sp = ss->sprog;
 
 	printf("State Program: \"%s\"\n", sp->progName);
-	printf("Number of channels=%u\n", sp->numChans);
+	printf("Number of channels=%d\n", sp->numChans);
+	printf("View: State set %s\n", ss->ssName);
 
 	if (str != NULL)
 	{
@@ -126,22 +147,24 @@ epicsShareFunc void epicsShareAPI seqChanShow(epicsThreadId tid, const char *str
 	else
 		connQual = 0;
 
-	ch = sp->chan;
-	for (nch = 0; (unsigned)nch < sp->numChans; )
+	while (dn && (unsigned)nch < sp->numChans)
 	{
+		CHAN *ch = sp->chan + nch;
+		ACHAN *ach = ch->ach;
+
 		if (str != NULL)
 		{
 			/* Check for channel connect qualifier */
 			if (connQual == '+')
-				showAll = ch->connected;
+				showAll = ach && ach->connected;
 			else if (connQual == '-')
-				showAll = ch->assigned && (!ch->connected);
+				showAll = ach && (!ach->connected);
 			else
 				showAll = TRUE;
 
 			/* Check for pattern match if specified */
 			match = (str[0] == 0) ||
-					(strstr(ch->dbName, str) != NULL);
+					(ach && strstr(ach->dbName, str) != NULL);
 			if (!(match && showAll))
 			{
 				ch += 1;
@@ -149,58 +172,48 @@ epicsShareFunc void epicsShareAPI seqChanShow(epicsThreadId tid, const char *str
 				continue; /* skip this channel */
 			}
 		}
-		printf("\n#%d of %u:\n", nch+1, sp->numChans);
-		printf("Channel name: \"%s\"\n", ch->dbName);
-		printf("  Unexpanded (assigned) name: \"%s\"\n", ch->dbAsName);
+		printf("\n#%d of %d:\n", nch+1, sp->numChans);
 		printf("  Variable name: \"%s\"\n", ch->varName);
-		printf("    offset = %d\n", ch->offset);
 		printf("    type = %s\n", ch->type->typeStr);
 		printf("    count = %u\n", ch->count);
-		printValue(bufPtr(ch)+ch->offset, ch->count, ch->type->putType);
-
-		printf("  Monitor flag = %d\n", ch->monFlag);
-		if (ch->monitored)
-			printf("    Monitored\n");
-		else
-			printf("    Not monitored\n");
+		printf("  Value =");
+		printValue(printf, valPtr(ch,ss), ch->count, ch->type->putType);
 
-		if (ch->assigned)
-			printf("  Assigned\n");
+		if (ach)
+			printf("  Assigned to \"%s\"\n", ach->dbName);
 		else
 			printf("  Not assigned\n");
 
-		if(ch->connected)
+		if(ach && ach->connected)
 			printf("  Connected\n");
 		else
 			printf("  Not connected\n");
 
-		if(ch->getComplete)
-			printf("  Last get completed\n");
+		if (ch->monitored)
+			printf("  Monitored\n");
 		else
-			printf("  Get not completed or no get issued\n");
+			printf("  Not monitored\n");
 
-		if(epicsEventTryWait(ch->putSemId))
-			printf("  Last put completed\n");
+		if (ch->efId)
+			printf("  Sync'ed to event flag %u\n", ch->efId);
 		else
-			printf("  Put not completed or no put issued\n");
-
-		printf("  Status = %d\n", ch->status);
-		printf("  Severity = %d\n", ch->severity);
-		printf("  Message = %s\n", ch->message != NULL ?
-			ch->message : "");
-
-		/* Print time stamp in text format: "yyyy/mm/dd hh:mm:ss.sss" */
-		epicsTimeToStrftime(tsBfr, sizeof(tsBfr),
-			"%Y/%m/%d %H:%M:%S.%03f", &ch->timeStamp);
-		printf("  Time stamp = %s\n", tsBfr);
-
-		n = wait_rtn();
-		if (n == 0)
-			return;
-		nch += n;
-		if (nch < 0)
-			nch = 0;
-		ch = sp->chan + nch;
+			printf("  Not sync'ed\n");
+
+		if (ach)
+		{
+			printf("  Status = %d\n", ach->status);
+			printf("  Severity = %d\n", ach->severity);
+			printf("  Message = %s\n", ach->message != NULL ?
+				ach->message : "");
+			/* Print time stamp in text format: "yyyy/mm/dd hh:mm:ss.sss" */
+			epicsTimeToStrftime(tsBfr, sizeof(tsBfr),
+				"%Y/%m/%d %H:%M:%S.%03f", &ach->timeStamp);
+			printf("  Time stamp = %s\n", tsBfr);
+		}
+
+		dn = userInput();
+		nch = max(0, nch + dn);
+		assert(nch >= 0);
 	}
 }
 /*
@@ -217,44 +230,49 @@ struct seqStats
 
 static int seqcarCollect(SPROG *sp, void *param)
 {
-	struct seqStats *pstats = (struct seqStats *) param;
-	CHAN	*ch = sp->chan;
-	unsigned nch;
-	int	level = pstats->level;
-	int 	printedProgName = 0;
+	struct seqStats	*pstats = (struct seqStats *) param;
+	unsigned	nch;
+	int		level = pstats->level;
+	int 		printedProgName = 0;
+
 	pstats->nProgs++;
 	for (nch = 0; nch < sp->numChans; nch++)
 	{
-		if (ch->assigned) pstats->nChans++;
-		if (ch->connected) pstats->nConn++;
+		CHAN *ch = sp->chan + nch;
+		ACHAN *ach = ch->ach;
+
+		if (ach) pstats->nChans++;
+		if (ach && ach->connected) pstats->nConn++;
 		if (level > 1 ||
-		    (level == 1 && !ch->connected))
-		    {
+			(level == 1 && ach && !ach->connected))
+		{
 			if (!printedProgName)
 			{
 				printf("  Program \"%s\"\n", sp->progName);
 				printedProgName = 1;
 			}
-			printf("    Variable \"%s\" %sconnected to PV \"%s\"\n",
-				ch->varName,
-				ch->connected ? "" : "not ",
-				ch->dbName);
+			if (ach)
+				printf("    Variable \"%s\" %sconnected to PV \"%s\"\n",
+					ch->varName,
+					ach->connected ? "" : "not ",
+					ach->dbName);
+			else
+				printf("    Variable \"%s\" not assigned to PV\n",
+					ch->varName);
 		}
-		ch++;
 	}
 	return FALSE;	/* continue traversal */
 }
 
 epicsShareFunc void epicsShareAPI seqcar(int level)
 {
-	struct seqStats stats = {0, 0, 0, 0};
-	int diss;
+	struct seqStats	stats = {0, 0, 0, 0};
+
 	stats.level = level;
 	seqTraverseProg(seqcarCollect, (void *) &stats);
-	diss = stats.nChans - stats.nConn;
 	printf("Total programs=%d, channels=%d, connected=%d, disconnected=%d\n",
-		stats.nProgs, stats.nChans, stats.nConn, diss);
-	return;
+		stats.nProgs, stats.nChans, stats.nConn,
+		stats.nChans - stats.nConn);
 }
 
 #if 0
@@ -272,16 +290,15 @@ epicsShareFunc void epicsShareAPI seqcaStats(int *pchans, int *pdiscon)
  */
 epicsShareFunc void epicsShareAPI seqQueueShow(epicsThreadId tid)
 {
+	SSCB	*ss = seqQryFind(tid);
 	SPROG	*sp;
 	int	n = 0;
 	int	dn = 1;
 
-	sp = seqQryFind(tid);
-	if(!sp) return;
-
+	if (ss == NULL) return;
+	sp = ss->sprog;
 	printf("State Program: \"%s\"\n", sp->progName);
 	printf("Number of queues = %d\n", sp->numQueues);
-
 	while (dn && (unsigned)n < sp->numQueues)
 	{
 		QUEUE	queue = sp->queues[n];
@@ -290,69 +307,37 @@ epicsShareFunc void epicsShareAPI seqQueueShow(epicsThreadId tid)
 			seqQueueNumElems(queue),
 			seqQueueUsed(queue),
 			seqQueueElemSize(queue));
-#if 0
-		for (entry = (QENTRY *) ellFirst(queue), i = 1;
-		     entry != NULL;
-		     entry = (QENTRY *) ellNext(&entry->node), i++)
-		{
-			CHAN	*ch = entry->ch;
-			pvValue	*access = &entry->value;
-			void	*val = pv_value_ptr(access, ch->type->getType);
-			char	tsBfr[50];
-
-			printf("\nEntry #%d: channel name: \"%s\"\n",
-							    i, ch->dbName);
-			printf("  Variable name: \"%s\"\n", ch->varName);
-			printValue(val, 1, ch->type->putType);
-							/* was ch->count */
-			printf("  Status = %d\n",
-					access->timeStringVal.status);
-			printf("  Severity = %d\n",
-					access->timeStringVal.severity);
-
-			/* Print time stamp in text format:
-			   "yyyy/mm/dd hh:mm:ss.sss" */
-			epicsTimeToStrftime(tsBfr, sizeof(tsBfr), "%Y/%m/%d "
-				"%H:%M:%S.%03f", &access->timeStringVal.stamp);
-			printf("  Time stamp = %s\n", tsBfr);
-		}
-#endif
-
-		dn = wait_rtn();
-		if (n == 0)
-			return;
+		dn = userInput();
 		n = max(0, n + dn);
 		assert(n >= 0);
 	}
 }
 
-/* Read from console until a RETURN is detected */
-static int wait_rtn(void)
+/* Read from console until a RETURN is detected.
+   The return value <n> value means:
+   n == 0: quit
+   n > 0 : move forward n items
+   n < 0 : move backward n items
+*/
+static int userInput(void)
 {
-	char	bfr[10];
-	int	i, n;
+	char	buffer[10];
+	int	n;
 
 	printf("Next? (+/- skip count)\n");
-	for (i = 0;  i < 10; i++)
-	{
-		int c = getchar();
-		if (c == EOF)
-			break;
-		if ((bfr[i] = (char)c) == '\n')
-			break;
-	}
-	bfr[i] = 0;
-	if (bfr[0] == 'q')
+	if (fgets(buffer, 10, stdin) == NULL)
+		return 0;
+	if (buffer[0] == 'q')
 		return 0; /* quit */
 
-	n = atoi(bfr);
+	n = atoi(buffer);
 	if (n == 0)
 		n = 1;
 	return n;
 }
 
 /* Print the current internal value of a database channel */
-static void printValue(void *val, unsigned count, int type)
+static void printValue(pr_fun *pr, void *val, unsigned count, int type)
 {
 	char	*c = (char *)val;
 	short	*s = (short *)val;
@@ -367,86 +352,78 @@ static void printValue(void *val, unsigned count, int type)
 		switch (type)
 		{
 		case pvTypeSTRING:
-			printf(" \"%.*s\"", MAX_STRING_SIZE, *t++);
+			pr(" \"%.*s\"", MAX_STRING_SIZE, *t++);
 			break;
 		case pvTypeCHAR:
-			printf(" %d", *c++);
+			pr(" %d", *c++);
 			break;
 		case pvTypeSHORT:
-			printf(" %d", *s++);
+			pr(" %d", *s++);
 			break;
 		case pvTypeLONG:
-			printf(" %ld", *l++);
+			pr(" %ld", *l++);
 			break;
 		case pvTypeFLOAT:
-			printf(" %g", *f++);
+			pr(" %g", *f++);
 			break;
 		case pvTypeDOUBLE:
-			printf(" %g", *d++);
+			pr(" %g", *d++);
 			break;
 		}
 	}
-	printf("\n");
+	pr("\n");
 }
 
 /* Find a state program associated with a given thread id */
-static SPROG *seqQryFind(epicsThreadId tid)
+static SSCB *seqQryFind(epicsThreadId tid)
 {
-	SPROG	*sp;
+	SSCB *ss;
 
-	if (tid == 0)
+	ss = seqFindStateSet(tid);
+	if (ss == NULL)
 	{
+		if (tid)
+			printf("No program instance is running thread %p.\n", tid);
 		seqShowAll();
-		return NULL;
-	}
-
-	/* Find a state program that has this thread id */
-	sp = seqFindProg(tid);
-	if (sp == NULL)
-	{
-		printf("No state program exists for thread id %ld\n", (long)tid);
-		return NULL;
 	}
-
-	return sp;
+	return ss;
 }
 
-static int	seqProgCount;
-
 /* This routine is called by seqTraverseProg() for seqShowAll() */
 static int seqShowSP(SPROG *sp, void *parg)
 {
-	SSCB	*ss;
-	unsigned nss;
-	const char *progName;
-	char	threadName[THREAD_NAME_SIZE];
-
-	if (seqProgCount++ == 0)
-		printf("Program Name     Thread ID  Thread Name      SS Name\n\n");
-
+	unsigned	nss;
+	const char	*progName;
+	char		threadName[THREAD_NAME_SIZE];
+	int		*pprogCount = (int *)parg;
+
+	if ((*pprogCount)++ == 0)
+		printf("Program Name     Thread ID  Thread Name      SS Name\n");
+		printf("------------     ---------  -----------      -------\n");
 	progName = sp->progName;
-	for (nss = 0, ss = sp->ss; nss < sp->numSS; nss++, ss++)
+	for (nss = 0; nss < sp->numSS; nss++)
 	{
+		SSCB *ss = sp->ss + nss;
+
 		if (ss->threadId == 0)
 			strcpy(threadName,"(no thread)");
 		else
 			epicsThreadGetName(ss->threadId, threadName,
 				      sizeof(threadName));
-		printf("%-16s %-10p %-16s %-16s\n", progName,
+		printf("%-16s %-8p  %-16s %-16s\n", progName,
 			ss->threadId, threadName, ss->ssName );
 		progName = "";
 	}
-	printf("\n");
 	return FALSE;	/* continue traversal */
 }
 
 /* Print a brief summary of all state programs */
 static void seqShowAll(void)
 {
+	int progCount = 0;
 
-	seqProgCount = 0;
-	seqTraverseProg(seqShowSP, 0);
-	if (seqProgCount == 0)
+	seqTraverseProg(seqShowSP, &progCount);
+	if (progCount == 0)
 		printf("No active state programs\n");
 }
 
diff --git a/src/seq/seq_queue.h b/src/seq/seq_queue.h
index 1744ef023bcae9979faec4d9373a934336293989..3cf32a5e00bf1aad0dccd72be5c1157ac0c3b1fc 100644
--- a/src/seq/seq_queue.h
+++ b/src/seq/seq_queue.h
@@ -31,7 +31,7 @@ QUEUE seqQueueCreate(unsigned numElems, unsigned elemSize);
 /* A common precondition of the following operations is
    that their QUEUE argument is valid; they do not check
    this.
-   
+
    A QUEUE is valid if it has been received as a
    non-NULL result of calling seqQueueCreate, and if
    seqQueueDelete has not been called for it.
diff --git a/src/seq/seq_task.c b/src/seq/seq_task.c
index 8fbebd2891ff0e6b442d5433f8bb4453d038cc39..ef0d9eef9721cb0ed3fd0aec940741b9ab73437d 100644
--- a/src/seq/seq_task.c
+++ b/src/seq/seq_task.c
@@ -11,49 +11,45 @@
 
 	Thread creation and control for sequencer state sets.
 ***************************************************************************/
-
-#define DECLARE_PV_SYS
 #include "seq.h"
 
 /* #define DEBUG errlogPrintf */
 #define DEBUG nothing
 
-#define varPtr(sp,ss)	(((sp)->options & OPT_SAFE) ? (ss)->var : (sp)->var)
-
-/* Function declarations */
-static boolean seq_waitConnect(SPROG *sp, SSCB *ss);
-static void ss_entry(SSCB *ss);
-static void ss_thread_init(SPROG *, SSCB *);
-static void ss_thread_uninit(SPROG *, SSCB *,int);
+static void ss_entry(void *arg);
 static void seq_clearDelay(SSCB *,STATE *);
-static int seq_getTimeout(SSCB *, double *);
+static boolean seq_getTimeout(SSCB *, double *);
 
 /*
  * sequencer() - Sequencer main thread entry point.
  */
-void sequencer (SPROG *sp)	/* ptr to original (global) state program table */
+void sequencer (void *arg)	/* ptr to original (global) state program table */
 {
-	SSCB		*ss = sp->ss;
+	SPROG		*sp = (SPROG *)arg;
 	unsigned	nss;
-	epicsThreadId	tid;
 	size_t		threadLen;
 	char		threadName[THREAD_NAME_SIZE+10];
 
-	/* Retrieve info about this thread */
-	sp->threadId = epicsThreadGetIdSelf();
-	epicsThreadGetName(sp->threadId, threadName, sizeof(threadName));
-	ss->threadId = sp->threadId;
+	/* Get this thread's id */
+	sp->ss->threadId = epicsThreadGetIdSelf();
 
-	/* Add the program to the state program list */
+	/* Add the program to the state program list
+	   and if necessary create pvSys */
 	seqAddProg(sp);
 
+	if (!sp->pvSys)
+	{
+		sp->die = TRUE;
+		goto exit;
+	}
+
 	/* Note that the program init, entry, and exit functions
 	   get the global var buffer sp->var passed,
 	   not the state set local one, even in safe mode. */
 	/* TODO: document this */
 
 	/* Call sequencer init function to initialize variables. */
-	sp->initFunc((USER_VAR *)sp->var);
+	sp->initFunc(sp->var);
 
 	/* Initialize state set variables. In safe mode, copy variable
 	   block to state set buffers.
@@ -66,28 +62,27 @@ void sequencer (SPROG *sp)	/* ptr to original (global) state program table */
 			memcpy(ss->var, sp->var, sp->varSize);
 	}
 
-	/* Attach to PV context of pvSys creator (auxiliary thread) */
-	pvSysAttach(pvSys);
-
-	/* Initiate connect & monitor requests to database channels */
-	seq_connect(sp);
+	/* Attach to PV system */
+	pvSysAttach(sp->pvSys);
 
-	/* If "+c" option, wait for all channels to connect (a failure
-	 * return means that we have been asked to exit) */
-	if (sp->options & OPT_CONN)
-	{
-		if (!seq_waitConnect(sp, ss)) return;
-	}
+	/* Initiate connect & monitor requests to database channels, waiting
+	   for all connections to be established if the option is set. */
+	if (seq_connect(sp, ((sp->options & OPT_CONN) != 0)) != pvStatOK)
+		goto exit;
 
 	/* Call program entry function if defined. */
-	if (sp->entryFunc) sp->entryFunc(ss, (USER_VAR *)sp->var);
+	if (sp->entryFunc) sp->entryFunc(sp->ss, sp->var);
 
-	/* Create each additional state-set task (additional state-set thread
+	/* Create each additional state set task (additional state set thread
 	   names are derived from the first ss) */
+	epicsThreadGetName(sp->ss->threadId, threadName, sizeof(threadName));
 	threadLen = strlen(threadName);
-	for (nss = 1, ss = sp->ss + 1; nss < sp->numSS; nss++, ss++)
+	for (nss = 1; nss < sp->numSS; nss++)
 	{
-		/* Form thread name from program name + state-set number */
+		SSCB		*ss = sp->ss + nss;
+		epicsThreadId	tid;
+
+		/* Form thread name from program name + state set number */
 		sprintf(threadName+threadLen, "_%d", nss);
 
 		/* Spawn the task */
@@ -95,97 +90,157 @@ void sequencer (SPROG *sp)	/* ptr to original (global) state program table */
 			threadName,			/* thread name */
 			sp->threadPriority,		/* priority */
 			sp->stackSize,			/* stack size */
-			(EPICSTHREADFUNC)ss_entry,	/* entry point */
+			ss_entry,			/* entry point */
 			ss);				/* parameter */
 
 		DEBUG("Spawning additional state set thread %p: \"%s\"\n", tid, threadName);
 	}
 
-	/* First state-set jumps directly to entry point */
+	/* First state set jumps directly to entry point */
 	ss_entry(sp->ss);
 
 	/* Call program exit function if defined */
-	if (sp->exitFunc) sp->exitFunc(ss, (USER_VAR *)sp->var);
+	if (sp->exitFunc) sp->exitFunc(sp->ss, sp->var);
+
+	DEBUG("   Wait for other state sets to exit\n");
+	for (nss = 1; nss < sp->numSS; nss++)
+	{
+		SSCB *ss = sp->ss + nss;
+		epicsEventMustWait(ss->dead);
+	}
+
+exit:
+	DEBUG("   Disconnect all channels\n");
+	seq_disconnect(sp);
+	DEBUG("   Remove program instance from list\n");
+	seqDelProg(sp);
+
+	printf("Instance %d of sequencer program \"%s\" terminated\n",
+		sp->instance, sp->progName);
+
+	/* Free all allocated memory */
+	seq_free(sp);
+}
+
+/*
+ * ss_read_buffer_static() - static version of ss_read_buffer.
+ * This is to enable inlining in the for loop in ss_entry.
+ */
+static void ss_read_buffer_static(SSCB *ss, CHAN *ch)
+{
+	SPROG	*sp = ss->sprog;
+	char	*val = valPtr(ch,ss);
+	char	*buf = bufPtr(ch);
+	int	nch = ch - sp->chan;
+	size_t	var_size = ch->type->size * ch->count;
+
+	if (!ss->dirty[nch])
+		return;
+	do {
+		ss->dirty[nch] = FALSE;
+		DEBUG("ss %s: before read %s", ss->ssName, ch->varName);
+		print_channel_value(DEBUG,ch,val);
+		memcpy(val, buf, var_size);
+		DEBUG("ss %s: after read %s", ss->ssName, ch->varName);
+		print_channel_value(DEBUG,ch,val);
+	} while ((ch->wr_active || ss->dirty[nch])
+		&& (epicsThreadSleep(0.0),TRUE));
+		/* Note: the sleep(0) here acts as a yield. */
 }
 
 /*
  * ss_read_buffer() - Only used in safe mode.
  * Lock-free reading of variable buffer.
- * See also ss_write_buffer().
  */
-static void ss_read_buffer(SSCB *ss)
+void ss_read_buffer(SSCB *ss, CHAN *ch)
 {
-	SPROG		*sp = ss->sprog;
-	ptrdiff_t	ss_num = ss - sp->ss;
-	unsigned	var;
+	return ss_read_buffer_static(ss, ch);
+}
+
+/*
+ * ss_read_all_buffer() - Only used in safe mode.
+ * Lock-free reading of variable buffer.
+ */
+static void ss_read_all_buffer(SPROG *sp, SSCB *ss)
+{
+	unsigned nch;
 
-	for (var = 0; var < sp->numChans; var++)
+	for (nch = 0; nch < sp->numChans; nch++)
 	{
-		CHAN	*ch = sp->chan + var;
-		char	*val = (char*)ss->var + ch->offset;
-		char	*buf = (char*)sp->var + ch->offset;
-		boolean *dirty = ch->dirty;
-		size_t	var_size = ch->type->size * ch->dbCount;
-
-		if (!dirty[ss_num])
-			continue;
-		do {
-			dirty[ss_num] = FALSE;
-			memcpy(val, buf, var_size);
-		} while ((ch->wr_active || dirty[ss_num])
-			&& (epicsThreadSleep(0),TRUE));
+		CHAN *ch = sp->chan + nch;
+		/* Call static version so it gets inlined */
+		ss_read_buffer_static(ss, ch);
 	}
 }
 
 /*
  * ss_write_buffer() - Only used in safe mode.
  * Lock-free writing of variable buffer.
- * See also ss_read_buffer().
  */
-void ss_write_buffer(CHAN *ch, void *val)
+void ss_write_buffer(SSCB *pwSS, CHAN *ch, void *val)
 {
 	SPROG	*sp = ch->sprog;
-	char	*buf = (char*)sp->var + ch->offset;
-	boolean *dirty = ch->dirty;
-	size_t	var_size = ch->type->size * ch->dbCount;
-	unsigned ss_num;
+	char	*buf = bufPtr(ch);
+	size_t	var_size = ch->type->size * ch->count;
+	unsigned nss;
+	int	nch = ch - sp->chan;
 
+#define ss_name pwSS ? pwSS->ssName : ""
 	ch->wr_active = TRUE;
+	DEBUG("ss %s: before write %s", ss_name, ch->varName);
+	print_channel_value(DEBUG,ch,buf);
 	memcpy(buf, val, var_size);
-	for (ss_num = 0; ss_num < sp->numSS; ss_num++)
+	DEBUG("ss %s: after write %s", ss_name, ch->varName);
+	print_channel_value(DEBUG,ch,buf);
+	for (nss = 0; nss < sp->numSS; nss++)
 	{
-		dirty[ss_num] = TRUE;
+		SSCB *ss = sp->ss + nss;
+		if (ss != pwSS)
+			ss->dirty[nch] = TRUE;
 	}
 	ch->wr_active = FALSE;
+#undef ss_name
 }
 
 /*
- * ss_entry() - Thread entry point for all state-sets.
- * Provides the main loop for state-set processing.
+ * ss_entry() - Thread entry point for all state sets.
+ * Provides the main loop for state set processing.
  */
-static void ss_entry(SSCB *ss)
+static void ss_entry(void *arg)
 {
+	SSCB		*ss = (SSCB *)arg;
 	SPROG		*sp = ss->sprog;
-	unsigned	nWords = (sp->numEvents + NBITS - 1) / NBITS;
-	USER_VAR	*var = (USER_VAR *)varPtr(sp,ss);
+	USER_VAR	*var;
 
-	/* Initialize this state-set thread */
-	ss_thread_init(sp, ss);
+	if (sp->options & OPT_SAFE)
+		var = ss->var;
+	else
+		var = sp->var;
 
-	/* Attach to PV context of pvSys creator (auxiliary thread); was
-	   already done for the first state set */
+	/* Attach to PV system; was already done for the first state set */
 	if (ss != sp->ss)
 	{
 		ss->threadId = epicsThreadGetIdSelf();
-		pvSysAttach(pvSys);
+		pvSysAttach(sp->pvSys);
 	}
 
+	/* Register this thread with the EPICS watchdog (no callback func) */
+	taskwdInsert(ss->threadId, 0, 0);
+
+	/* In safe mode, update local var buffer with global one before
+	   entering the event loop. Must do this using
+	   ss_read_all_buffer since CA and other state sets could
+	   already post events resp. pvPut. */
+	if (sp->options & OPT_SAFE)
+		ss_read_all_buffer(sp, ss);
 
 	/* Initial state is the first one */
 	ss->currentState = 0;
 	ss->nextState = -1;
 	ss->prevState = -1;
 
+	DEBUG("ss %s: entering main loop\n", ss->ssName);
+
 	/*
 	 * ============= Main loop ==============
 	 */
@@ -208,6 +263,9 @@ static void ss_entry(SSCB *ss)
 			st->entryFunc(ss, var);
 		}
 
+		/* Flush any outstanding DB requests */
+		pvSysFlush(sp->pvSys);
+
 		seq_clearDelay(ss, st); /* Clear delay list */
 		st->delayFunc(ss, var); /* Set up new delay list */
 
@@ -224,27 +282,19 @@ static void ss_entry(SSCB *ss)
 
 			/* Wake up on PV event, event flag, or expired delay */
 			if (seq_getTimeout(ss, &delay) && delay > 0.0)
-			{
 				epicsEventWaitWithTimeout(ss->syncSemId, delay);
-			}
 			else
-			{
 				epicsEventWait(ss->syncSemId);
-			}
 
 			/* Check whether we have been asked to exit */
-			if (epicsEventTryWait(ss->death1SemId) == epicsEventWaitOK)
-			{
+			if (sp->die)
 				goto exit;
-			}
 
 			/* Copy dirty variable values from CA buffer
 			 * to user (safe mode only).
 			 */
 			if (sp->options & OPT_SAFE)
-			{
-				ss_read_buffer(ss);
-			}
+				ss_read_all_buffer(sp, ss);
 
 			/* Check state change conditions */
 			ev_trig = st->eventFunc(ss, var,
@@ -254,9 +304,9 @@ static void ss_entry(SSCB *ss)
 			if (ev_trig && !(sp->options & OPT_NEWEF))
 			{
 				unsigned i;
-				for (i = 0; i < nWords; i++)
+				for (i = 0; i < NWORDS(sp->numEvFlags); i++)
 				{
-					sp->events[i] &= ~ss->mask[i];
+					sp->evFlags[i] &= ~ss->mask[i];
 				}
 			}
 		} while (!ev_trig);
@@ -271,9 +321,6 @@ static void ss_entry(SSCB *ss)
 			st->exitFunc(ss, var);
 		}
 
-		/* Flush any outstanding DB requests */
-		pvSysFlush(pvSys);
-
 		/* Change to next state */
 		ss->prevState = ss->currentState;
 		ss->currentState = ss->nextState;
@@ -281,87 +328,10 @@ static void ss_entry(SSCB *ss)
 
 	/* Thread exit has been requested */
 exit:
-
-	/* Uninitialize this state-set thread (phase 1) */
-	ss_thread_uninit(sp, ss, 1);
-
-	/* Pass control back (so all state-set threads can complete phase 1
-	 * before embarking on phase 2) */
-	epicsEventSignal(ss->death2SemId);
-
-	/* Wait for request to perform uninitialization (phase 2) */
-	epicsEventMustWait(ss->death3SemId);
-	ss_thread_uninit(sp, ss, 2);
-
-	/* Pass control back and die (i.e. exit) */
-	epicsEventSignal(ss->death4SemId);
-}
-
-/* Initialize a state-set thread */
-static void ss_thread_init(SPROG *sp, SSCB *ss)
-{
-	/* Get this thread's id */
-	ss->threadId = epicsThreadGetIdSelf();
-
-	/* Attach to PV context of pvSys creator (auxiliary thread); was
-	   already done for the first state-set */
-	if (sp->threadId != ss->threadId)
-		pvSysAttach(pvSys);
-
-	/* Register this thread with the EPICS watchdog (no callback func) */
-	taskwdInsert(ss->threadId, 0, (void *)0);
-}
-
-/* Uninitialize a state-set thread */
-static void ss_thread_uninit(SPROG *sp, SSCB *ss, int phase)
-{
-	/* Phase 1: if this is the first state-set, call user exit routine
-	   and disconnect all channels */
-	if (phase == 1 && ss->threadId == sp->threadId)
-	{
-	    USER_VAR *var = (USER_VAR *)varPtr(sp,ss);
-	    DEBUG("   Call exit function\n");
-	    sp->exitFunc(ss, var);
-
-	    DEBUG("   Disconnect all channels\n");
-	    seq_disconnect(sp);
-	}
-
-	/* Phase 2: unregister the thread with the EPICS watchdog */
-	else if (phase == 2)
-	{
-	    DEBUG("   taskwdRemove(%p)\n", ss->threadId);
-	    taskwdRemove(ss->threadId);
-	}
-}
-
-/* Wait for all channels to connect */
-static boolean seq_waitConnect(SPROG *sp, SSCB *ss)
-{
-	epicsStatus	status;
-	double		delay;
-
-	if (sp->numChans == 0)
-		return TRUE;
-	delay = 10.0; /* 10, 20, 30, 40, 40,... sec */
-	while (1)
-	{
-		status = epicsEventWaitWithTimeout(
-			ss->allFirstConnectAndMonitorSemId, delay);
-		if(status==epicsEventWaitOK) break;
-		if (delay < 40.0)
-		{
-			delay += 10.0;
-			errlogPrintf("numMonitoredChans %u firstMonitorCount %u",
-				sp->numMonitoredChans,sp->firstMonitorCount);
-			errlogPrintf(" assignCount %u firstConnectCount %u\n",
-				sp->assignCount,sp->firstConnectCount);
-		}
-		/* Check whether we have been asked to exit */
-		if (epicsEventTryWait(ss->death1SemId) == epicsEventWaitOK)
-			return FALSE;
-	}
-	return TRUE;
+	taskwdRemove(ss->threadId);
+	/* Declare ourselves dead */
+	if (ss != sp->ss)
+		epicsEventSignal(ss->dead);
 }
 
 /*
@@ -394,7 +364,7 @@ static void seq_clearDelay(SSCB *ss, STATE *st)
  * Return whether to time out when waiting for events.
  * If yes, set *pdelay to the timout (in seconds).
  */
-static int seq_getTimeout(SSCB *ss, double *pdelay)
+static boolean seq_getTimeout(SSCB *ss, double *pdelay)
 {
 	unsigned ndelay;
 	boolean	do_timeout = FALSE;
@@ -442,13 +412,11 @@ static int seq_getTimeout(SSCB *ss, double *pdelay)
 }
 
 /*
- * Delete all state-set threads and do general clean-up.
+ * Delete all state set threads and do general clean-up.
  */
 void epicsShareAPI seqStop(epicsThreadId tid)
 {
-	SPROG		*sp;
-	SSCB		*ss;
-	unsigned	nss;
+	SPROG	*sp;
 
 	/* Check that this is indeed a state program thread */
 	sp = seqFindProg(tid);
@@ -457,169 +425,24 @@ void epicsShareAPI seqStop(epicsThreadId tid)
 
 	DEBUG("Stop %s: sp=%p, tid=%p\n", sp->progName, sp,tid);
 
-	/* Ask all state-set threads to exit (phase 1) */
-	DEBUG("   Asking state-set threads to exit (phase 1):\n");
-	for (nss = 0, ss = sp->ss; nss < sp->numSS; nss++, ss++)
-	{
-		/* Just possibly hasn't started yet, so check... */
-		if (ss->threadId == 0)
-			continue;
+	/* Ask all state set threads to exit */
+	DEBUG("   Asking state set threads to exit\n");
+	sp->die = TRUE;
 
-		/* Ask the thread to exit */
-		DEBUG("      tid=%p\n", ss->threadId);
-		epicsEventSignal(ss->death1SemId);
-	}
+	/* Take care that we die even if waiting for initial connect */
+	epicsEventSignal(sp->ready);
 
-	/* Wake up all state-sets */
-	DEBUG("   Waking up all state-sets\n");
+	DEBUG("   Waking up all state sets\n");
 	seqWakeup (sp, 0);
-
-	/* Wait for them all to complete phase 1 of their deaths */
-	DEBUG("   Waiting for state-set threads phase 1 death:\n");
-	for (nss = 0, ss = sp->ss; nss < sp->numSS; nss++, ss++)
-	{
-		if (ss->threadId == 0)
-			continue;
-
-		if (epicsEventWaitWithTimeout(ss->death2SemId,10.0) != epicsEventWaitOK)
-		{
-			errlogPrintf("Timeout waiting for thread %p "
-				     "(\"%s\") death phase 1 (ignored)\n",
-				     ss->threadId, ss->ssName);
-		}
-		else
-		{
-			DEBUG("      tid=%p\n", ss->threadId);
-		}
-	}
-
-	/* Ask all state-set threads to exit (phase 2) */
-	DEBUG("   Asking state-set threads to exit (phase 2):\n");
-	for (nss = 0, ss = sp->ss; nss < sp->numSS; nss++, ss++)
-	{
-		if (ss->threadId == 0)
-			continue;
-
-		DEBUG("      tid=%p\n", ss->threadId);
-		epicsEventSignal(ss->death3SemId);
-	}
-
-	/* Wait for them all to complete phase 2 of their deaths */
-	DEBUG("   Waiting for state-set threads phase 2 death:\n");
-	for (nss = 0, ss = sp->ss; nss < sp->numSS; nss++, ss++)
-	{
-		if (ss->threadId == 0)
-			continue;
-
-		if (epicsEventWaitWithTimeout(ss->death4SemId,10.0) != epicsEventWaitOK)
-		{
-			errlogPrintf("Timeout waiting for thread %p "
-				     "(\"%s\") death phase 2 (ignored)\n",
-				     ss->threadId, ss->ssName);
-		}
-		else
-		{
-			DEBUG("      tid=%p\n", ss->threadId);
-		}
-	}
-
-	/* Remove the state program from the state program list */
-	seqDelProg(sp);
-
-	/* Delete state-set semaphores */
-	for (nss = 0, ss = sp->ss; nss < sp->numSS; nss++, ss++)
-	{
-		if (ss->allFirstConnectAndMonitorSemId != NULL)
-			epicsEventDestroy(ss->allFirstConnectAndMonitorSemId);
-		if (ss->syncSemId != NULL)
-			epicsEventDestroy(ss->syncSemId);
-		if (ss->getSemId != NULL)
-			epicsEventDestroy(ss->getSemId);
-		if (ss->death1SemId != NULL)
-			epicsEventDestroy(ss->death1SemId);
-		if (ss->death2SemId != NULL)
-			epicsEventDestroy(ss->death2SemId);
-		if (ss->death3SemId != NULL)
-			epicsEventDestroy(ss->death3SemId);
-		if (ss->death4SemId != NULL)
-			epicsEventDestroy(ss->death4SemId);
-	}
-
-	/* Delete program-wide semaphores */
-	epicsMutexDestroy(sp->programLock);
-
-	/* Free all allocated memory */
-	seqFree(sp);
-
-	DEBUG("   Done\n");
-}
-
-/* seqFree()--free all allocated memory */
-void seqFree(SPROG *sp)
-{
-	SSCB		*ss;
-	CHAN		*ch;
-	unsigned	nch;
-
-	seqMacFree(sp);
-	for (nch = 0; nch < sp->numChans; nch++)
-	{
-		ch = sp->chan + nch;
-
-		if (ch->dbName != NULL)
-			free(ch->dbName);
-		if (ch->putSemId != NULL)
-			epicsEventDestroy(ch->putSemId);
-	}
-
-	/* Free channel structures */
-	free(sp->chan);
-
-	ss = sp->ss;
-
-	/* Free event words */
-	free(sp->events);
-
-	/* Free SSCBs */
-	free(sp->ss);
-
-	/* Free SPROG */
-	free(sp);
 }
 
-/* 
- * Sequencer auxiliary thread -- loops on pvSysPend().
- */
-void *seqAuxThread(void *tArgs)
+void seqCreatePvSys(SPROG *sp)
 {
-	AUXARGS		*args = (AUXARGS *)tArgs;
-	char		*pvSysName = args->pvSysName;
-	long		debug = args->debug;
-	int		status;
-
-	/* Register this thread with the EPICS watchdog */
-	taskwdInsert(epicsThreadGetIdSelf(), 0, 0);
-
-	/* All state program threads will use a common PV context (subtract
-	   1 from debug level for PV debugging) */
-	status = pvSysCreate(pvSysName, debug>0?debug-1:0, &pvSys);
+	int debug = sp->debug;
+	pvStat status = pvSysCreate(sp->pvSysName,
+		max(0, debug-1), &sp->pvSys);
 	if (status != pvStatOK)
-	{
-		errlogPrintf("seqAuxThread: pvSysCreate() %s failure: %s\n",
-			pvSysName, pvSysGetMess(pvSys));
-		seqAuxThreadId = (epicsThreadId) -1;
-		return NULL;
-	}
-	seqAuxThreadId = epicsThreadGetIdSelf(); /* AFTER pvSysCreate() */
-
-	/* This loop allows for check for connect/disconnect on PVs */
-	for (;;)
-	{
-		pvSysPend(pvSys, 10.0, TRUE); /* returns every 10 sec. */
-	}
-
-	/* Return no result (never exit in any case) */
-	return NULL;
+		errlogPrintf("pvSysCreate(\"%s\") failure\n", sp->pvSysName);
 }
 
 /*
diff --git a/src/snc/gen_ss_code.c b/src/snc/gen_ss_code.c
index c24518df7cb153764d7df1d85dfcfc905b90e67e..b4b634ed921d36ebc818902f4a593b1c77d06841 100644
--- a/src/snc/gen_ss_code.c
+++ b/src/snc/gen_ss_code.c
@@ -248,12 +248,12 @@ void gen_ss_code(Program *program)
 			gen_state_func(ssp->value, sp->value,
 				sp->state_whens, gen_event_body,
 				"Event", "E", "boolean",
-				", short *pTransNum, short *pNextState");
+				", int *pTransNum, int *pNextState");
 			/* Generate action processing function */
 			gen_state_func(ssp->value, sp->value,
 				sp->state_whens, gen_action_body,
 				"Action", "A", "void",
-                                ", int transNum, short *pNextState");
+                                ", int transNum, int *pNextState");
 		}
 	}
 
diff --git a/test/Makefile b/test/Makefile
index a2df00a614d173a2c77ebdee0056a9f6fd25178c..bef3ca92b0ba2168c0801108e90f5aff775e2170 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -7,5 +7,6 @@ DIRS += simple
 DIRS += johng
 DIRS += validate
 DIRS += demo
+DIRS += race
 
 include $(TOP)/configure/RULES_DIRS
diff --git a/test/validate/local.st b/test/validate/local.st
index 89ce434685c343ae0de1ccad54ac3d66e074adfd..6e905848c2d0fd5f144fe1c07d6cbefe767a2931 100644
--- a/test/validate/local.st
+++ b/test/validate/local.st
@@ -14,15 +14,15 @@ int i = 1;
 /* only state-set */
 ss start {
     state first {
-	int i = 0;
-	when () {
-	    printf( "i = %d\n", i );
-	} state last
+        int i = 0;
+        when () {
+            printf( "i = %d\n", i );
+        } state last
     }
 
     state last {
-	when ( delay( 2.0 ) ) {
-	} state first
+        when ( delay( 2.0 ) ) {
+        } state first
     }
 }
 
diff --git a/test/validate/pvPutAsync.db b/test/validate/pvPutAsync.db
index 7cbab6d985792c815dc13b2fb328c37a234bd802..001af22c69830e81d1e0dd513d59e1fbea987738 100644
--- a/test/validate/pvPutAsync.db
+++ b/test/validate/pvPutAsync.db
@@ -15,9 +15,6 @@ record(seq,"pvPutAsyncSeq") {
     field(DLY1,"2")
     field(DOL1,"1")
     field(LNK1,"pvPutAsyncRes CA")
-    field(DLY2,"2")
-    field(DOL2,"2")
-    field(LNK2,"pvPutAsyncRes CA")
 }
 record(longin,"pvPutAsyncRes") {
 }
diff --git a/test/validate/pvPutAsync.st b/test/validate/pvPutAsync.st
index 6f1ccaeaf43fce81831e62de40dec0d91a3984c2..3873b5adf86ca198d2538e16cc1a536f40de06a9 100644
--- a/test/validate/pvPutAsync.st
+++ b/test/validate/pvPutAsync.st
@@ -5,25 +5,73 @@ program pvPutAsync
 int x;
 assign x to "pvPutAsync1";
 
-ss test {
-    state loop {
-        int i = 0;
+ss test1 {
+    state put_async {
+        int i;
+        entry {
+            i = 0;
+        }
         when (i == 4) {
-        } state stop
-        when (delay(0.2)) {
+        } state put_sync
+        when (delay(0.5)) {
             int status;
             x = 1;
             status = pvPut(x,ASYNC);
+            /* first ok, next three failure */
             if (status)
-                printf("pvPut failed: %d\n",status);
+                printf("test1: pvPut/ASYNC %d failed: %s\n", i, pvMessage(x));
             else
-                printf("pvPut ok\n");
+                printf("test1: pvPut/ASYNC %d issued\n", i);
             ++i;
-        } state loop
+        } state put_async
     }
-    state stop {
-        when (delay(10)) {
-            exit(0);
-        } state stop
+    state put_sync {
+        when (pvPutComplete(x)) {
+            int i = 0;
+            printf("test1: pvPut/ASYNC complete\n");
+            for (i=0; i<4; i++) {
+                int status = pvPut(x,SYNC);
+                /* should always succeed */
+                if (status) {
+                    printf("test1: pvPut/SYNC failed: %s\n", pvMessage(x));
+                } else {
+                    printf("test1: pvPut/SYNC ok\n");
+                }
+            }
+        } state wait
+    }
+    state wait {
+        when (delay(1)) {
+        } state put_async
+    }
+}
+
+#if 0
+ss test2 {
+    int x;
+    assign x to "pvPutAsync2";
+    state wait {
+        when (delay(0.5)) {
+        } state put_async
+    }
+    state put_async {
+        when (pvPutComplete(x)) {
+            int status;
+            printf("test2: pvPut/ASYNC complete\n");
+            x = 1;
+            status = pvPut(x,ASYNC);
+            /* should always succeed */
+            if (status)
+                printf("test2: pvPut/ASYNC 1 failed, %s\n", pvMessage(x));
+            else
+                printf("test2: pvPut/ASYNC 1 issued\n");
+            /* should always fail */
+            status = pvPut(x,ASYNC);
+            if (status)
+                printf("test2: pvPut/ASYNC 2 failed, %s\n", pvMessage(x));
+            else
+                printf("test2: pvPut/ASYNC 2 issued\n");
+        } state wait
     }
 }
+#endif
diff --git a/test/validate/syncq.db b/test/validate/syncq.db
new file mode 100644
index 0000000000000000000000000000000000000000..8419695f6f5ec6f55315f8950d533783280821f5
--- /dev/null
+++ b/test/validate/syncq.db
@@ -0,0 +1,4 @@
+record(waveform,"syncq") {
+	field(FTVL,"STRING")
+    field(NELM,"2")
+}
diff --git a/test/validate/syncq.st b/test/validate/syncq.st
index b5fb092c8911b83f401bac00ebdb50415a4ad7ee..07d7d8694e4abc2e6015bd70ddb1d9e6bec0b2ab 100644
--- a/test/validate/syncq.st
+++ b/test/validate/syncq.st
@@ -3,11 +3,14 @@ program syncq_test
 option +s;
 
 string s[2];
-assign s;
+assign s to "syncq";
 monitor s;
 evflag ef_s;
 syncq s to ef_s 5;
 
+int n = 0;
+assign n;
+
 ss get {
     state get {
         when (pvGetQ(s)) {
@@ -22,12 +25,21 @@ ss get {
 
 ss put {
     state put {
-        int n = 0;
         when (delay(1)) {
             sprintf(s[0], "%d", n);
             sprintf(s[1], "%d", -n);
             pvPut(s);
             n++;
+            pvPut(n);
         } state put
     }
 }
+
+ss flush {
+    monitor n;
+    state flush {
+        when (n%20==0) {
+            pvFlushQ(s);
+        } state flush
+    }
+}