Difference between revisions of "V4 Design: Record Processing"

From EPICSWIKI
Line 1: Line 1:
= EPICS V4: Record Processing =
= EPICS V4: Record Processing =
August 18 2005  
August 23 2005  


----
----
Line 7: Line 7:
== Overview ==
== Overview ==
  </center>
  </center>
This document described the semantics for V4 record processing.
This includes the following:
* Semantics for Database and Channel Access Links.
* Semantics for record processing
* Record Locking
* Posting database monitors
* Access to array and structure fields.


This version is based on feedback from the EPICS core developer's
This version is based on feedback from the EPICS core developer's
Line 12: Line 20:
It is also a companion to the V4 Design: dbdInterfaces wiki.
It is also a companion to the V4 Design: dbdInterfaces wiki.


The V3 record processing semantics do not work well for data
The main motive for the V4 semantics related to record and link processing is
that the V3 record processing semantics do not work well for data
acquisition applications. An example of data acquisition is:
acquisition applications. An example of data acquisition is:
*  Move sample to new position.
*  Move sample to new position.
Line 46: Line 55:


<center>
<center>
== V4 Link Semantics ==
== Goals for V4 Record Processing ==
  </center>
  </center>


Lets first state some goals for linking to other records:
V4 record processing will have the following features not available in V3:


NOTE: Instead of "forward link" the name "process link" is used.
*All links to other records will have the following features
** Can request that the linked record be processed.
** Can wait until the linked record completes processing
* Database links will no longer be processed via recursive calls to dbProcess
* Lock sets will no longer exist
** Each record instance will have a mutex which must be held while accessing any field of the record.
* Monitors will be implemented by database access without requiring calls to post monitors.
* Channel Access and Database Access can access arrays and structures


The features described next apply to both database and channel access links.
=== V4 Link Semantics ===
 
NOTES:
*Instead of "forward link" the name "process link" is used.
*Unless stated otherwise, the features described in this section apply to both database and channel access links.
 
Links are processed as follows:


* Input Link from another record
* Input Link from another record
** get current value
** Optionally request that record be processed.
** wait until next record process and get value
** Optionally wait for record to complete processing.
** request process, wait, get value
** Get value.
** When a record has multiple input links the links can be processed in parallel or sequentially
* Output Link to another record
* Output Link to another record
** put value
** Put value
** put value and request process
** Optionally request that the record be processed.
** put value, request process, wait
*** For database links, if the record is already active or already queued for processing the link request fails.
* process link to another record
*** What about channel access links? I think they should also fail.
** Optionally wait until record completes processing.
** When a record has multiple output links the links can be processed in parallel or sequentially
* Process Link
** request processing
** request processing
** request processing and wait
** Optionally wait until record completes processing.
* all links to other records
** When a record has multiple output links the links can be processed in parallel or sequentially
** Allow simultaneous requests, i.e. process and wait in parallel
 
** process one link at a time, i.e. process and wait serially.
The following rules are for links which request processing.
 
* If record is already being processed the request is ignored.
** Should the record containing the link have a warning message?
* Can a record declare that links to particular fields not cause processing?
** If yes than this should be declared by a record instance
** If yes than record containing link should have warning message
* Can a record declare that it can not be processed via process links?
** If yes than this should be declared by a record instance
** If yes than record containing link should have warning message


NOTE: wait does NOT mean to block. It means to save state, return,
NOTE: wait does NOT mean to block. It means to save state, return,
and resume at the saved state when called again.
and resume at the saved state when called again.
=== Processing V4 Database Links ===
In V3 record linked via database links are processed via recursive calls to
dbProcess. In V4 this will no longer be done. For V4 the following is done
when a link requests processing:
# If the record is active or already queued the request is ignored.
** Should the requester receive an error?
# If the record does not allow link requests to cause processing the request fails.
# If neither 1) or 2) are true than the record is queued for processing.
Question. Should these same rules apply to Channel Access Links?
=== Record Locking ===
V3 implemented lock sets in order to prevent different threads
from simultaneously accessing linked records.
Instead of lock sets V4:
* Implements a per record instance lock.
* Defines a rule that allows two records to be locked without deadlocks
=== Posting Monitors ===
In V3 monitors are posted when code (record support, device support, database access, etc) calls db_post_event. In V4 database access will itself handle
the posting of monitors.
All changes to database fields are done via an interface implemented by database
access. This makes it possible for database access to handle monitors.
=== Database Field Types ===
The early releases of V4 will support Channel Access and Database Access to the
following types of fields:
* primitive types
* string
* one dimensional array of primitive or string
* struct with fields of following types:
** primitive
** string
** array of primitive or string
Later releases may provide access to more complicated fields.


----
----
<center>
<center>
== V4 Record Processing ==
</center>
This section describes the semantics implemented by dbProcess.
dbProcess will keep internal state for the following:
# If the record is idle or being processed. Code can ask dbProcess if the record is idle but can access this variable or change it's value.
# If iocRecord or record access is currently in control. This state is not accessable from outside dbProcess.
# processState - A state which is passed to iocRecord or record support and can be changed by them via the value they return to dbProcess.
== V4 Processing States ==
== V4 Processing States ==
</center>


Instead of the V3 PROC field, V4 will have a field processState.
processState is passed to process (iocRecord or record support) and is also
returned by process.
This field has the states:
This field has the states:
; processIdle
; processStart
: record is not being processed
: processing is starting
; processInputActive
; processInputActive
: waiting for input links to complete
: waiting for input links to complete
Line 92: Line 184:


NOTES:
NOTES:
* Each state may have substates. For example if a record has multiple input links that are to be processed sequentially, it can keep state describing which link is being processed.   
* Record support can implement substates. For example if a record has multiple input links that are to be processed sequentially, it can keep state describing which link is being processed.   
* Individual record types might use only a subset of the above states.
* Individual record types might use only a subset of the above states.
* V3 link instances had attributes like CA, CP, CPP. For V4 these should be something like:
* V3 link instances had attributes like CA, CP, CPP. For V4 these should be something like:
Line 100: Line 192:




----
<center>
== dbProcess ==
== dbProcess ==
</center>


The functionality described in this section will be divided between
The functionality described in this section will be divided between
Line 126: Line 215:
For each state the following actions are performed:
For each state the following actions are performed:
* dbProcessIdle
* dbProcessIdle
** prepare record for processing and call iocRecord with processState = processIdle
** prepare record for processing and call iocRecord with processState = processStart
** If iocRecord returns processInputActive set dbProcessState = dbProcessIocRecord and return
** If iocRecord returns processInputActive set dbProcessState = dbProcessIocRecord and return
** If iocRecord returns processActive set processState = idle and goto state dbProcessRecordSupport
** If iocRecord returns processActive set processState = processStart and goto state dbProcessRecordSupport
** If iocRecord returns processDone goto state dbProcessDone
** If iocRecord returns processDone goto state dbProcessDone
* dbProcessIocRecord
* dbProcessIocRecord
** If processState = processInputActive call iocRecord
** If processState = processInputActive call iocRecord
*** If iocRecord returns processInputActive return
*** If iocRecord returns processInputActive return
*** If iocRecord returns processActive set processState = processIdle and goto state dbProcessRecordSupport
*** If iocRecord returns processActive set processState = processStart and goto state dbProcessRecordSupport
*** If iocRecord returns processDone goto state dbProcessDone
*** If iocRecord returns processDone goto state dbProcessDone
** If processState = processDone or processOutputActive call iocRecord
** If processState = processDone or processOutputActive call iocRecord
Line 139: Line 228:
*** If iocRecord returns processDone goto state dbProcessDone
*** If iocRecord returns processDone goto state dbProcessDone
* dbProcessRecordSupport
* dbProcessRecordSupport
** If processState = processIdle, processInputActive, or processActive call record support
** Call record support
*** If record support returns processInputActive return
*** If record support returns processInputActive return
*** If record support returns processActive handle monitors and return
*** If record support returns processActive handle monitors and return
*** If record support returns processDone goto dbProcessIocRecord
** If processState = processDone or processOutputActive call record support
*** If record support returns processOutputActive return
*** If record support returns processDone goto dbProcessIocRecord
*** If record support returns processDone goto dbProcessIocRecord
* dbProcessDone
* dbProcessDone
** Perform final processing - See below for details.
** Perform final processing - See below for details.
** set dbProcessState = dbProcessIdle and processState = processIdle
** set dbProcessState = dbProcessIdle and processState = processStart
** return
** return
NOTES:
* Record support can switch between the states processInputActive, processActive, and processOutputActive as often as it wants.
** This supports the semantics of both the motor and sequence records.
** iocRecord can NOT switch between these states.
* The semantics support the following model
** iocRecord processes all its input links and then
** Record support switches between input, active, and output as needed and then
** iocRecord processes all its output links and then
** dbProcess does final processing and sets the record idle.


The remaining subsections provide more details about each processState. Keep in mind
The remaining subsections provide more details about each processState. Keep in mind
Line 155: Line 252:
some by record support, and some by link support.
some by record support, and some by link support.


=== processState processIdle ===
=== processState processStart ===


If dbProcess is called with dbProcessIdle and processIdle then it:
If dbProcess is called with dbProcessIdle and processStart then it:
* Sets all dynamic monitor bits to 0
* calls iocRecord with processState processStart
* Other stuff?????
* calls iocRecord with processState processIdle


When iocRecord is called with processState processIdle it:
When iocRecord is called with processState processStart it:
* initializes severity. See Alarm Processing for details.
* initializes severity. See Alarm Processing for details.
* initializes status. See Alarm Processing for details.
* initializes status. See Alarm Processing for details.
Line 168: Line 263:
* returns with processState one of the following:
* returns with processState one of the following:
** processInputActive - dbProcess will again call iocRecord.
** processInputActive - dbProcess will again call iocRecord.
** processActive - dbProcess will set processState to processIdle and call record support.
** processActive - dbProcess will set processState to processStart and call record support.
** processDone - dbProcess will complete processing. record support will NOT be called and iocRecord will not be called to handle output links
** processDone - dbProcess will complete processing. record support will NOT be called and iocRecord will not be called to handle output links


When record support is called with processState processIdle it:
When record support is called with processState processStart it:
* starts processing it's input links. Details are given below.
* starts processing it's input links. Details are given below.
* returns with processState one of the following:
* returns with processState one of the following:
Line 195: Line 290:
are being processed. When iocRecord detects that all it's input
are being processed. When iocRecord detects that all it's input
links have completed, it sets processState = processActive and returns.
links have completed, it sets processState = processActive and returns.
dbProcess then sets processState = processIdle and calls record support so
dbProcess then sets processState = processStart and calls record support so
that it starts processing it's input links.
that it starts processing it's input links.


When record support is called it returns one of:
When record support is called it returns one of:
* processInputActive - dbProcess will again call record support
* processActive - dbProcess will again call record support
* processActive - dbProcess will again call record support
* processOutputActive - dbProcess will again call record support
* processOutputActive - dbProcess will again call record support
Line 214: Line 310:
For example the record can be periodically scanned.
For example the record can be periodically scanned.


When record support detects that it is done, it sets the processState to
When record support is called it returns one of:
either processOutputActive or processDone.
* processInputActive - dbProcess will again call record support
 
* processActive - dbProcess will post monitors and again call record support
While the processState is processActive dbProcess will post dynamic monitors.
* processOutputActive - dbProcess will again call record support
* processDone - dbProcess will call iocRecord to process it's output links


=== processState processOutputActive ===  
=== processState processOutputActive ===  
Line 224: Line 321:
completed. Links can be processed in parallel, sequencially, or a
completed. Links can be processed in parallel, sequencially, or a
combination of these.
combination of these.
Record support handles the record specific output links and then iocRecord
 
Record support handles the record specific output links and iocRecord
handles the output and process links in iocRecord.
handles the output and process links in iocRecord.
When record support detects that all it's output links are done,
it returns with processState processDone. dbProcess then calls iocRecord which
starts processing it's output and process links.
Until it is done it sets processState to processOutputActive.
When it is done it sets the processState to processDone.




Line 236: Line 329:


When both record support and iocRecord have reported that they are done,
When both record support and iocRecord have reported that they are done,
dbProcess does some final steps and then sets the processState to processIdle.
dbProcess does some final steps and then sets the processState to processStart.


The final steps are:
The final steps are:
Line 242: Line 335:
* If the request to process the record included a callback, the callback is called.
* If the request to process the record included a callback, the callback is called.
* If other record completion callback requests are present, call them.
* If other record completion callback requests are present, call them.
* Dynamic monitors are posted
* Monitors???
* ???
* ???
* The processState is set to processIdle and dbProcessState to dbProcessIdle.
* The record is set idle.




Line 253: Line 346:


V3 implemented lock sets, i.e. when a record was being processed it and all records linked via database links were locked.
V3 implemented lock sets, i.e. when a record was being processed it and all records linked via database links were locked.
For V4 we should try to get rid of lock sets.


Lock sets solved two Mutual Exclusion problems:
Lock sets solved two Mutual Exclusion problems:
Line 262: Line 353:
* No puts could be done to an record in a lock set while record processing is active.
* No puts could be done to an record in a lock set while record processing is active.
** For synchronous records this guarantees that no fields are modified except by record processing itself.
** For synchronous records this guarantees that no fields are modified except by record processing itself.
** For asynchronous records this guarantee is no longer valid.
** For asynchronous records this guarantee is not valid.




For V4 dbProcess will not be called recursively.
V4 does not call dbProcess recursively. Instead a process request is queued.
Instead a request will be made to process the record.
The request fails if either of the following is true:
 
* The record is active.
* If the link is a channel access link than channel access is used.
* The record is already queued.
* If the link is a database link to a passive record then the following can be done
** A request is made for one of the standard callback threads to process the record,
** The priority will be taken from the priority field in iocRecord.


Note that if all linked records are passive and the same priority then
Note that if all linked records are passive and the same priority then
Line 286: Line 374:
** The record completes.
** The record completes.


This leaves the question of what should be done about mutual exclusion?
This leaves the question of what should be done about mutual exclusion.
At least the following two locks per record instance are needed:


; recordLock
Each record instance will have an associated lock maintained by dbAccess.
: This is locked when dbProcess is called and unlocked before dbProcess returns. It is entirely private to dbProcess. This prevents simultaneous calls to dbProcess.
When dbProcess is called it takes the lock.
; fieldLock
Before it returns it unlocks.
: This must be taken before accessing any field.
Thus iocRecord and record support do not need to lock or unlock.


Should fieldLock be private to database access?
Other code that needs access to fields of a record instance must call
* yes - This simplifies code that accesses the database BUT it means that database access must lock/unlock every access of every field
dbLock before accessing any fields and dbUnlock after all accesses are complete.
* no - Any code can access multiple fields with just one lock/unlock.


Perhaps we should start with a private lock?
This leaves the problem of how to access fields from two different record
instances simultaneously. For example code that implements database
access needs such access. Code that needs such access must call dbLockLink before accessing the linked record and dbUnlockLink after access.


A problem remains: Array fields. As described below, array fields may only
dbLockLink is implemented as follows:
be accessable in segments. It might be a channel access client that
* Each Record instances has a unique ordinal number
is accessing the array in segments and will block between segments while
* Assume Record X has a link to record Y
waiting for the data to be transfered over the network.
* If the ordinal number of X is less than that of Y
** Just call dbLock for record Y
* If the ordinal numnber of X is less than that of Y
** call dbUnlock for X
** call dbLock for Y
** call dbLock for X


How to prevent two clients from trying to access the same array simultaneously?
dbUnlockLink just calls dbUnlock for the linked record.
* Does it matter?
* If application wants to allow only single access let the apllication solve the problem
** runControl records are one solution
** Other solutions can be envisioned
* Conclusion is that this is not a database responsibility


----
----
<center>
<center>
== get/put database fields ==
== Channel Access/ Database Access field types ==
  </center>
  </center>


Line 320: Line 408:


This section discusses how IOC database fields are accessed by channel access
This section discusses how IOC database fields are accessed by channel access
or via database links. The following types of data are supported.
or via database links.
 
For the initial implementation of V4 only the following types of data
will be accessable.
(Later releases of V4 will support more complicated types.)


* primitive types
* primitive types
* string
* string
* array of primitive or string
* enum
* one dimensional array of primitive or string
* struct with fields of following types:
* struct with fields of following types:
** primitive
** primitive
** string
** string
** array of primitive or string
** enum
** one dimensional array of primitive or string
 


Other fields will only be available as one of the above types.
Other fields will only be available as one of the above types.
Line 351: Line 446:
     }
     }


Then if a client has attaches to a process variable:
Then if a client attaches to process variable:
     record.field(calcInpLink[1].link)
     record.field(calcInpLink[1].link)
Then the data will be presented as a string describing the link.
The data will be presented as a string describing the link.
An example of the string value is:
An example of a string value is:
     acro9440(0,5)
     acro9440(0,5)


Line 367: Line 462:
A string is just a UTF_8 encoded character string
A string is just a UTF_8 encoded character string


=== DbfMenu ===
=== DbfMenu and DbfEnum ===


The index value is handled just like DbfInt16.
Handled as an enum
Since the choices for a menu can't change, only the changes to the index
can occur.


=== DbfEnum ===
The index value is handled just like DbfInt16.
The choices are handled as an array of strings. See array below for details.


=== DbfLink and DbfDevice ===
=== DbfLink and DbfDevice ===
Line 422: Line 510:
* Access by clients that have knowledge of the structure -
* Access by clients that have knowledge of the structure -
** In this case the data is transfered as a sequence of field values.
** In this case the data is transfered as a sequence of field values.
** Can we do this?
** Do this via dataAccess property catalog.
** Perhaps this looks like a synchronized set of requests to the fields?
* Access by clients that do not know about the structure -
* Access by clients that do not know about the structure -
**The structure value is presented as a string that has the structAssignmentList syntax defined in "V4 DB Record Instance Syntax"
**The structure value is presented as a string that has the structAssignmentList syntax defined in "V4 DB Record Instance Syntax"
** Is this needed? Can we just require that client know property catalog?




Line 434: Line 522:
* DbfString
* DbfString


Note that arrays of structs are not accessable.
Note that arrays of structs are not accessable in the early V4 releases.
 
Arrays still need lots more discussion!!!


Database Access is implemented so that record/link/device support can transfer an array in segments. Examples:
Database Access is implemented so that record/link/device support can transfer an array in segments. Examples:
Line 448: Line 538:
An individual element of an array of type DbfStruct can be accessed
An individual element of an array of type DbfStruct can be accessed
directly if it satisfies the criteria for accessing a DbfStruct.
directly if it satisfies the criteria for accessing a DbfStruct.
How to prevent two clients from trying to access the same array simultaneously?
* Does it matter?
* If application wants to allow only single access let the apllication solve the problem
** runControl records are one solution
** Other solutions can be envisioned
* Conclusion is that this is not a database responsibility


=== MDArray ===
=== MDArray ===


TBD
Not implemented if the early V4 releases.


=== Associated Data ===
=== Associated Data ===
Line 458: Line 556:
and data.
and data.


Perhaps these can be handled as four synchronized requests to the same record?
These will be handled automatically as described in "Posting Modifications"
 
How is this implemented?
 
* What does dbProcess do?
* How does a client make the request


----
----
Line 476: Line 569:
any help from the code that modifies database fields.
any help from the code that modifies database fields.


The following is the initial attempt to describe how monitors are handled.
Monitors are handled as follows:


The following definitions are used:
Each field of a record instance has an associated "modified" bit.
* monitor - A client has asked to be notified when a field value changes.
* post - Create storage, copy the current value of the field into the storage, and pass the storage to the monitor subsystem.
* static database field - A field that changes only because something external to the record modifies the field.
* dynamic database field - A field that changes because of record processing.


Static and dynamic fields have different semantics for monitors.
dbProcess handles all changes made by iocRecord or record support:
* Before it calls iocRecord or record support all modified bits are false.
* Each field modification causes the associated modify but tom be set true.
* When record support or iocRecord return dbProcess:
** posts all fields that have changed
** sets the associated modify bit false


=== static fields ===
dbLock/dbUnlock implements the same algorithm.


* primitive types - This includes DbfBool, DbfOctet, DbfInt16, ..., DbfFloat64. When a static primitive field is modified, a monitor is immediately posted.
dbLockLink/dbUnlockLink implements the same algorithm for the linked record.
* DbfMenu - Whenever the index is modified, a monitor is immediately posted. Since the choices for a menu can't change, only the changes to the index can occur.
* DbfEnum - Whenever the index is modified, a monitor is immediately posted.  What about when a  choice changes?
** Should only the choice that changes be posted? If so what is the syntax?
** Should the complete new set of choices be posted?
** Should the user just be notified that one or more choices changed and the user must issue a new read request?
* DbfLink and DbfDevice
**Both of these have the following:
*** <tt>choiceName</tt> A string that selects the link or device support.
*** <tt>dataStruct</tt> Each support has an associated struct for configuration.
** What should be made available?
*** Perhaps just notification that a change has occcured. Client must issue new read request
*string - Perhaps
**When a string field is modified, a monitor is immediately posted.
** Is this correct? For long strings it could cause excessive amounts of memory to be consumed. Probably not a big problem.
* array - Memory becomes a big issue.
**Perhaps the following semantics could be used.
*** The array is posted when the value changes.
*** When the value changes, the client is notified and must issue a read request to retrieve the data.
** Should both or only one be supported?  If both who decides which semantics to use?
*** client?
*** database developer?
*** ???
* struct - Can only have fields that are primitive, string, array
** Structure with only primitive or string fields should be posted immediately
** What about structures containing arrays?


=== dynamic fields ===
The following is the initial attempt to describe how monitors are handled.


For each dynamic database field, dbAccess keeps a modify bit
The following definitions are used:
that is set to 0 when the record starts processing and is set to 1
* monitor - A client has asked to be notified when a field value changes.
whenever the field is modified.
* post - Create storage, copy the current value of the field into the storage, and pass the storage to the monitor subsystem.


Whenever record support returns with processState processActive or iocRecord
returns with processState processDone, then the following is done:


* The monitor subsystem is notified about the beginning of synchronized monitors.
The problem is how to implement posting.
* Each field with the modify bit set to 1 is posted.
Separate discussions is required for the following: 1) primitive fields,
* The modify bit is set back to 0.
2) string fields, 3) structured fields, and 4) array fields.
* Since status, severity, and timeStamp are dynamic fields in iocRecord, they follow the same logic.
* The monitor subsystem is notified about the end of synchronized monitors.


Separate discussions are required for the following: 1) primitive fields,
The following is just some thoughts.
2) string fields, 3) structured fields, and  4) array fields.


* primitive fields - No problems.
* primitive fields - No problems.

Revision as of 16:03, 23 August 2005

EPICS V4: Record Processing

August 23 2005


Overview

This document described the semantics for V4 record processing. This includes the following:

  • Semantics for Database and Channel Access Links.
  • Semantics for record processing
  • Record Locking
  • Posting database monitors
  • Access to array and structure fields.

This version is based on feedback from the EPICS core developer's meeting at ANL/APS on July 11th through July 14th. It is also a companion to the V4 Design: dbdInterfaces wiki.

The main motive for the V4 semantics related to record and link processing is that the V3 record processing semantics do not work well for data acquisition applications. An example of data acquisition is:

  • Move sample to new position.
    • Several motors may be involved
    • While moving post monitors to show current position, etc.
  • Sample data
  • Wait until CA client has fetched the data before next move.

SynAPPS provides the Scan, motor, etc records which help with data processing applications. These records process as follows:

  • Record does not complete processing until all inputs, outputs, etc done. Not completing means that they do not call recGblFwdLink until all proccessing is complete. Note that recGblFwdLink is what causes the V3 ioc to complete record processing.
  • While waiting for asynchronous events these records have PACT=0 but keep state that shows it is still active
  • Can be scanned and issue monitors while internal state is active

Leaving PACT 0 but not calling recGblFwdLink was not anticipated when the V3 record processing semantics were created. For V4 it must be posssible for a record to be processed while it is waiting for asynchronous processing to complete.

In V3, if an input link is asynchronous, Process Passive does not wait for asynchronous processing to complete before fetching data. This also complicated data acquisition applications.

V4 record processing is designed to make data acquisition easier. Both input and output links can wait for record processing. This must be done, however, without record support blocking. If blocking makes it easier to implement record support, then the support can spawn a separate thread that does block. Record support can communicate with the separate thread to decide when to complete record processing.


Goals for V4 Record Processing

V4 record processing will have the following features not available in V3:

  • All links to other records will have the following features
    • Can request that the linked record be processed.
    • Can wait until the linked record completes processing
  • Database links will no longer be processed via recursive calls to dbProcess
  • Lock sets will no longer exist
    • Each record instance will have a mutex which must be held while accessing any field of the record.
  • Monitors will be implemented by database access without requiring calls to post monitors.
  • Channel Access and Database Access can access arrays and structures

V4 Link Semantics

NOTES:

  • Instead of "forward link" the name "process link" is used.
  • Unless stated otherwise, the features described in this section apply to both database and channel access links.

Links are processed as follows:

  • Input Link from another record
    • Optionally request that record be processed.
    • Optionally wait for record to complete processing.
    • Get value.
    • When a record has multiple input links the links can be processed in parallel or sequentially
  • Output Link to another record
    • Put value
    • Optionally request that the record be processed.
      • For database links, if the record is already active or already queued for processing the link request fails.
      • What about channel access links? I think they should also fail.
    • Optionally wait until record completes processing.
    • When a record has multiple output links the links can be processed in parallel or sequentially
  • Process Link
    • request processing
    • Optionally wait until record completes processing.
    • When a record has multiple output links the links can be processed in parallel or sequentially

The following rules are for links which request processing.

  • If record is already being processed the request is ignored.
    • Should the record containing the link have a warning message?
  • Can a record declare that links to particular fields not cause processing?
    • If yes than this should be declared by a record instance
    • If yes than record containing link should have warning message
  • Can a record declare that it can not be processed via process links?
    • If yes than this should be declared by a record instance
    • If yes than record containing link should have warning message

NOTE: wait does NOT mean to block. It means to save state, return, and resume at the saved state when called again.

Processing V4 Database Links

In V3 record linked via database links are processed via recursive calls to dbProcess. In V4 this will no longer be done. For V4 the following is done when a link requests processing:

  1. If the record is active or already queued the request is ignored.
    • Should the requester receive an error?
  1. If the record does not allow link requests to cause processing the request fails.
  2. If neither 1) or 2) are true than the record is queued for processing.

Question. Should these same rules apply to Channel Access Links?

Record Locking

V3 implemented lock sets in order to prevent different threads from simultaneously accessing linked records.

Instead of lock sets V4:

  • Implements a per record instance lock.
  • Defines a rule that allows two records to be locked without deadlocks

Posting Monitors

In V3 monitors are posted when code (record support, device support, database access, etc) calls db_post_event. In V4 database access will itself handle the posting of monitors.

All changes to database fields are done via an interface implemented by database access. This makes it possible for database access to handle monitors.

Database Field Types

The early releases of V4 will support Channel Access and Database Access to the following types of fields:

  • primitive types
  • string
  • one dimensional array of primitive or string
  • struct with fields of following types:
    • primitive
    • string
    • array of primitive or string

Later releases may provide access to more complicated fields.



V4 Record Processing

This section describes the semantics implemented by dbProcess.

dbProcess will keep internal state for the following:

  1. If the record is idle or being processed. Code can ask dbProcess if the record is idle but can access this variable or change it's value.
  2. If iocRecord or record access is currently in control. This state is not accessable from outside dbProcess.
  3. processState - A state which is passed to iocRecord or record support and can be changed by them via the value they return to dbProcess.

V4 Processing States

processState is passed to process (iocRecord or record support) and is also returned by process. This field has the states:

processStart
processing is starting
processInputActive
waiting for input links to complete
processActive
record is waiting for completion of record specific activity
processOutputActive
waiting for output and process links to complete
processDone
process has completed.

NOTES:

  • Record support can implement substates. For example if a record has multiple input links that are to be processed sequentially, it can keep state describing which link is being processed.
  • Individual record types might use only a subset of the above states.
  • V3 link instances had attributes like CA, CP, CPP. For V4 these should be something like:
    • process - a request to process the record before gets or after puts.
    • wait - a request to wait until it has processed all it's input links
    • parallel - OK to allow simultaneous gets or puts


dbProcess

The functionality described in this section will be divided between iocCore and iocRecord. iocRecord is the V4 successor to the V3 dbCommon. For V4 it is a separate software component.

NOTE: FLNK (forward link) is replaced by an array of process links.


dbProcess implements a state machine that use processState as well a private state dbProcessState which has the following:

dbProcessIdle
record is not being processed
dbProcessIocRecord
iocRecord is in control
dbProcessRecordSupport
record support is in control
dbProcessDone
complete record processing

For each state the following actions are performed:

  • dbProcessIdle
    • prepare record for processing and call iocRecord with processState = processStart
    • If iocRecord returns processInputActive set dbProcessState = dbProcessIocRecord and return
    • If iocRecord returns processActive set processState = processStart and goto state dbProcessRecordSupport
    • If iocRecord returns processDone goto state dbProcessDone
  • dbProcessIocRecord
    • If processState = processInputActive call iocRecord
      • If iocRecord returns processInputActive return
      • If iocRecord returns processActive set processState = processStart and goto state dbProcessRecordSupport
      • If iocRecord returns processDone goto state dbProcessDone
    • If processState = processDone or processOutputActive call iocRecord
      • If iocRecord returns processOutputActive return
      • If iocRecord returns processDone goto state dbProcessDone
  • dbProcessRecordSupport
    • Call record support
      • If record support returns processInputActive return
      • If record support returns processActive handle monitors and return
      • If record support returns processDone goto dbProcessIocRecord
  • dbProcessDone
    • Perform final processing - See below for details.
    • set dbProcessState = dbProcessIdle and processState = processStart
    • return

NOTES:

  • Record support can switch between the states processInputActive, processActive, and processOutputActive as often as it wants.
    • This supports the semantics of both the motor and sequence records.
    • iocRecord can NOT switch between these states.
  • The semantics support the following model
    • iocRecord processes all its input links and then
    • Record support switches between input, active, and output as needed and then
    • iocRecord processes all its output links and then
    • dbProcess does final processing and sets the record idle.


The remaining subsections provide more details about each processState. Keep in mind that some actions are performed by dbProcess, some by iocRecord, some by record support, and some by link support.

processState processStart

If dbProcess is called with dbProcessIdle and processStart then it:

  • calls iocRecord with processState processStart

When iocRecord is called with processState processStart it:

  • initializes severity. See Alarm Processing for details.
  • initializes status. See Alarm Processing for details.
  • starts processing it's input links. Details are given below.
  • returns with processState one of the following:
    • processInputActive - dbProcess will again call iocRecord.
    • processActive - dbProcess will set processState to processStart and call record support.
    • processDone - dbProcess will complete processing. record support will NOT be called and iocRecord will not be called to handle output links

When record support is called with processState processStart it:

  • starts processing it's input links. Details are given below.
  • returns with processState one of the following:
    • processInputActive - dbProcess will again call record support
    • processActive - dbProcess will again call record support
    • processOutputActive - dbProcess will again call record support
    • processDone - dbProcess will call iocRecord to process it's output links

processState processInputActive

This state means that one or more input links to other records have not completed. Links can be processed in parallel, sequentially, or a combination of these. iocRecord handles the links in iocRecord and record support handles record specific input links. The support is responsible for implementing sequential or parallel link processing. It calls link support to do the actual I/O. The Link support methods provide options specifying if the linked record should be processed and provide a callback to call when link processing completes. It is up to iocRecord or record support to again call dbProcess when all input links complete.

If is OK for dbProcess to be called multiple times while input links are being processed. When iocRecord detects that all it's input links have completed, it sets processState = processActive and returns. dbProcess then sets processState = processStart and calls record support so that it starts processing it's input links.

When record support is called it returns one of:

  • processInputActive - dbProcess will again call record support
  • processActive - dbProcess will again call record support
  • processOutputActive - dbProcess will again call record support
  • processDone - dbProcess will call iocRecord to process it's output links


processState processActive

This state means that record support and associated device support are waiting for something to complete, e.g. for a motor to reach some position.

The record, however, can be processed so that it can report intermediate data values, e.g. the current motor position. For example the record can be periodically scanned.

When record support is called it returns one of:

  • processInputActive - dbProcess will again call record support
  • processActive - dbProcess will post monitors and again call record support
  • processOutputActive - dbProcess will again call record support
  • processDone - dbProcess will call iocRecord to process it's output links

processState processOutputActive

This state means that one or more output links to other records have not completed. Links can be processed in parallel, sequencially, or a combination of these.

Record support handles the record specific output links and iocRecord handles the output and process links in iocRecord.


processState processDone

When both record support and iocRecord have reported that they are done, dbProcess does some final steps and then sets the processState to processStart.

The final steps are:

  • If the request to process the record included a callback, the callback is called.
  • If other record completion callback requests are present, call them.
  • Monitors???
  • ???
  • The record is set idle.



Mutual Exclusion

V3 implemented lock sets, i.e. when a record was being processed it and all records linked via database links were locked.

Lock sets solved two Mutual Exclusion problems:

  • V3 allowed dbProcess to be called recursively.
    • process passive database links and forward links were implemented via recursive calls to dbProcess
    • Lock sets prevented deadly embrace problems for circular links.
  • No puts could be done to an record in a lock set while record processing is active.
    • For synchronous records this guarantees that no fields are modified except by record processing itself.
    • For asynchronous records this guarantee is not valid.


V4 does not call dbProcess recursively. Instead a process request is queued. The request fails if either of the following is true:

  • The record is active.
  • The record is already queued.

Note that if all linked records are passive and the same priority then all processing can occur without any context switches. The following occurs:

  • A request is made to a thread to call dbProcess.
    • The request is put on a queue associated with the thread.
  • Sometime later the request is dequeued and dbProcess is called.
    • For each link process request the request is put on the queue for the same thread
    • After all requests are queued dbProcess returns
  • The thread takes each request off the queue and calls dbProcess for the associated linked record
    • When all linked records finish processing a request is queued to call dbProcess for the original record
  • The request for the original record is removed from the queue and it's dbProcess is again called.
    • The record completes.

This leaves the question of what should be done about mutual exclusion.

Each record instance will have an associated lock maintained by dbAccess. When dbProcess is called it takes the lock. Before it returns it unlocks. Thus iocRecord and record support do not need to lock or unlock.

Other code that needs access to fields of a record instance must call dbLock before accessing any fields and dbUnlock after all accesses are complete.

This leaves the problem of how to access fields from two different record instances simultaneously. For example code that implements database access needs such access. Code that needs such access must call dbLockLink before accessing the linked record and dbUnlockLink after access.

dbLockLink is implemented as follows:

  • Each Record instances has a unique ordinal number
  • Assume Record X has a link to record Y
  • If the ordinal number of X is less than that of Y
    • Just call dbLock for record Y
  • If the ordinal numnber of X is less than that of Y
    • call dbUnlock for X
    • call dbLock for Y
    • call dbLock for X

dbUnlockLink just calls dbUnlock for the linked record.


Channel Access/ Database Access field types

Overview

This section discusses how IOC database fields are accessed by channel access or via database links.

For the initial implementation of V4 only the following types of data will be accessable. (Later releases of V4 will support more complicated types.)

  • primitive types
  • string
  • enum
  • one dimensional array of primitive or string
  • struct with fields of following types:
    • primitive
    • string
    • enum
    • one dimensional array of primitive or string


Other fields will only be available as one of the above types. For example a DbfLink field will appear as a string.

Since the field view provides access to subfields of a field and to elements of an array, Information from every DbfType is available.

As an example if a Database Definition is

    struct(calcInpLink) {
        field(link,link(in))
        field(value,float64)
    };
    
    struct(calcInpLink) {
        field(link,link(in))
        field(value,float64)
    ...
        field(inp,array(struct(calcInpLink)[]))
    ...
    }

Then if a client attaches to process variable:

    record.field(calcInpLink[1].link)

The data will be presented as a string describing the link. An example of a string value is:

    acro9440(0,5)


primitive types

This includes DbfBool, DbfOctet, DbfInt16, ... , DbfFloat64. These do not present any problems. The scalar value is transfered.

string

A string is just a UTF_8 encoded character string

DbfMenu and DbfEnum

Handled as an enum


DbfLink and DbfDevice

Both of these have the following:

  • choiceName A string that selects the link or device support.
  • dataStruct Each support has an associated struct for configuration.

How should the be made available? Perhaps:

The field appears as a string with the following syntax:

choiceName(structAssignmentList)

where structAssignmentList has the same syntax as defined in "V4 DB Record Instance Syntax"

timeStamp

This is just transfered a struct with two fields:

  • int64 secondsSinceEpoch
  • int32 nanoSeconds

struct

The database definition syntax allows a struct to be composed of fields of any DbfType. Database access, however, will only allow access to structs with fields of the following types:

  • primitive - DbfBool,...,DbfFloat64
  • DbfString
  • DbfArray of primitive type or string

The individual fields can be accessed directly so this discussion only involves access to the entire structure.

Note that if a struct contains a field that is a DbfStruct, the struct field itself will not be available but it is possible to access the DbfStruct field itself if it is contains fields of the above types.

The following two forms of access available.

  • Access by clients that have knowledge of the structure -
    • In this case the data is transfered as a sequence of field values.
    • Do this via dataAccess property catalog.
  • Access by clients that do not know about the structure -
    • The structure value is presented as a string that has the structAssignmentList syntax defined in "V4 DB Record Instance Syntax"
    • Is this needed? Can we just require that client know property catalog?


array

Arrays of the following types are accessable:

  • primitive - DbfBool,...,DbfFloat64
  • DbfString

Note that arrays of structs are not accessable in the early V4 releases.

Arrays still need lots more discussion!!!

Database Access is implemented so that record/link/device support can transfer an array in segments. Examples:

  • If the array is a circular buffer, it is presented in two segements
  • If the array is stored in hardware, e.g. a transient recorder, the array can be read from the hardware and passed to the client in segments.

The database definition syntax allow an array to be any of the basic types, i.e. DbfBool,...,DbfFloat64, DbfString, DbfArray, DbfStruct.

Database access only allows access to arrays of the following types.

  • primitive - DbfBool,...,DbfFloat64
  • DbfString

An individual element of an array of type DbfStruct can be accessed directly if it satisfies the criteria for accessing a DbfStruct.

How to prevent two clients from trying to access the same array simultaneously?

  • Does it matter?
  • If application wants to allow only single access let the apllication solve the problem
    • runControl records are one solution
    • Other solutions can be envisioned
  • Conclusion is that this is not a database responsibility


MDArray

Not implemented if the early V4 releases.

Associated Data

The typical example is a client that asks for timeStamp, severity, status, and data.

These will be handled automatically as described in "Posting Modifications"


Posting Modifications

The document "V4 Design: dbdIterfaces" provides a design that allows dbAccess to detect all changes to fields of an IOC record.

dbAccess handles all posting on monitors without requiring any help from the code that modifies database fields.

Monitors are handled as follows:

Each field of a record instance has an associated "modified" bit.

dbProcess handles all changes made by iocRecord or record support:

  • Before it calls iocRecord or record support all modified bits are false.
  • Each field modification causes the associated modify but tom be set true.
  • When record support or iocRecord return dbProcess:
    • posts all fields that have changed
    • sets the associated modify bit false

dbLock/dbUnlock implements the same algorithm.

dbLockLink/dbUnlockLink implements the same algorithm for the linked record.

The following is the initial attempt to describe how monitors are handled.

The following definitions are used:

  • monitor - A client has asked to be notified when a field value changes.
  • post - Create storage, copy the current value of the field into the storage, and pass the storage to the monitor subsystem.


The problem is how to implement posting. Separate discussions is required for the following: 1) primitive fields, 2) string fields, 3) structured fields, and 4) array fields.

The following is just some thoughts.

  • primitive fields - No problems.
  • string fields - Memory is an issue.
    • Should there be a way to postpone posting until the monitor is ready to retrieve the value?
    • If so who decides?
  • struct fields - Memory is again an issue.
    • Structure with only primitive field or string should be posted immediately but what about array fields?
    • Who decides?
    • How?
  • array fields - memory is an issue
    • It should be possible to post small arrays of primitive type.
    • Large arrays should not be posted until monitor system is ready to accept the data.
      • It should also be possible to just notify the client that the array changed but the client must issue a read request to retrieve the data.

Alarm Processing

The V4 semantics for status and severity have the following goals:

  • alarmSeverity has the same values as for V3
  • status is independent of alarmSeverity, i.e. it just reports a status.
  • a record starts processing with alarmSeverity = "NO_ALARM" and status empty.
  • status will ONLY be posted if it has changed since the last time it was posted.
  • alarmSeverity will ONLY be posted if it has changed since the last time it was posted

The semantics that implement these goals are:

  • When a record starts processing the following is done
    • The status is saved in previousStatus
    • status is set to an empty string
    • The alarmSeverity is saved in previousAlarmSeverity
    • alarmSeverity is set to NO_ALARM
  • When the status is modified
    • If the new status is different than previousStatus
      • previousStatus is set to the new status
      • the status modified bit is set
  • When the alarmSeverity is modified
    • If the new alarmSeverity is different than previousAlarmSeverity
      • previousAlarmSeverity is set to the new alarmSeverity
      • the alarmSeverity modified bit is set
  • When dynamic monitors are posted
    • If status != previousStatus the status modified bit is set
    • If alarmSeverity != previousAlarmSeverity the alarmSeverity modified bit is set
    • If the status modified bit is set the status is posted and previousStatus set equal to status
    • If the alarmSeverity modified bit is set the alarmSeverity is posted and previousAlarmSeverity set equal to alarmSeverity