Merge lp:~epics-documenters/epics-appdev/parallel-cbthreads-2 into lp:~epics-documenters/epics-appdev/3.16
- parallel-cbthreads-2
- Merge into 3.16
Proposed by
Ralph Lange
| Status: | Merged |
|---|---|
| Merged at revision: | 60 |
| Proposed branch: | lp:~epics-documenters/epics-appdev/parallel-cbthreads-2 |
| Merge into: | lp:~epics-documenters/epics-appdev/3.16 |
| Diff against target: |
409 lines (+153/-33) 4 files modified
tex/generalPurposeTasks.tex (+38/-15) tex/libCom.tex (+46/-9) tex/libComOsi.tex (+46/-1) tex/scanning.tex (+23/-8) |
| To merge this branch: | bzr merge lp:~epics-documenters/epics-appdev/parallel-cbthreads-2 |
| Related bugs: | |
| Related blueprints: |
Parallel Callback Threads
(High)
|
| Reviewer | Review Type | Date Requested | Status |
|---|---|---|---|
| EPICS Documentation Team | Pending | ||
|
Review via email:
|
|||
Commit message
Description of the change
Matching documentation for lp:~epics-core/epics-base/parallel-cbthreads-2
To post a comment you must log in.
- 52. By Ralph Lange
-
Merge ioc-shutdown branch.
- 53. By Ralph Lange
-
generalPurposeT
asks: Move callback request error codes to the "Notes:" section, small fixes.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
| 1 | === modified file 'tex/generalPurposeTasks.tex' |
| 2 | --- tex/generalPurposeTasks.tex 2010-11-15 18:16:16 +0000 |
| 3 | +++ tex/generalPurposeTasks.tex 2013-10-23 09:48:56 +0000 |
| 4 | @@ -21,17 +21,21 @@ |
| 5 | other tasks are not delayed. In addition the message can be sent to a system wide error message file. |
| 6 | |
| 7 | \section{General Purpose Callback Tasks} |
| 8 | +\label{sec:callbackThreads} |
| 9 | |
| 10 | \subsection{Overview} |
| 11 | |
| 12 | -EPICS provides three general purpose IOC callback tasks. The only difference between the tasks is their scheduling |
| 13 | -priority; low, medium or high. The low priority task runs at a priority just higher than Channel Access, the medium at a |
| 14 | -priority about equal to the median of the periodic scan tasks, and the high at a priority higher than the event scan task. The |
| 15 | +EPICS provides three sets of general purpose IOC callback tasks. The only difference between the task sets is their scheduling |
| 16 | +priority: low, medium or high. The low priority tasks runs at a priority just higher than Channel Access, the medium priority tasks at a |
| 17 | +priority about equal to the median of the periodic scan tasks, and the high priority tasks at a priority higher than the event scan task. The |
| 18 | callback tasks are available for any software component that needs a task under which to run some job either immediately |
| 19 | -or after some delay. Jobs can also be cancelled during their delay period. The three callback tasks register themselves with |
| 20 | +or after some delay. Jobs can also be cancelled during their delay period. The callback tasks register themselves with |
| 21 | the task watchdog (described below). They are created with a generous amount of stack space and can thus be used for |
| 22 | invoking record processing. For example the I/O event scanner uses the general purpose callback tasks. |
| 23 | |
| 24 | +The number of general purpose threads per priority level is configurable. |
| 25 | +On SMP systems with multi-core CPUs, the throughput can be improved and the latency (time between job scheduling and processing) can be lowered by running multiple parallel callback tasks, which the OS scheduler may assign to different CPU cores. Parallel callback tasks must be explicitly enabled (see \ref{Parallel Callback Tasks} below), as this feature is disabled by default for compatibility reasons. |
| 26 | + |
| 27 | The following steps must be taken in order to use the general purpose callback tasks: |
| 28 | |
| 29 | \begin{enumerate} |
| 30 | @@ -102,14 +106,13 @@ |
| 31 | \index{callbackRequest} |
| 32 | \index{callbackRequestProcessCallback} |
| 33 | \begin{verbatim} |
| 34 | -callbackRequest(CALLBACK *pcb); |
| 35 | -callbackRequestProcessCallback(CALLBACK *pcb, int prio, void *prec); |
| 36 | +int callbackRequest(CALLBACK *pcb); |
| 37 | +int callbackRequestProcessCallback(CALLBACK *pcb, int prio, void *prec); |
| 38 | \end{verbatim} |
| 39 | |
| 40 | -Both can be called from interrupt level code. The callback routine is passed a single argument, which is the same |
| 41 | -argument that was passed to \verb|callbackRequest|, i.e., the address of the \verb|CALLBACK| structure. The second |
| 42 | -routine is a shortcut for calling both \verb|callbackSetProcess| and \verb|callbackRequest|. The following delayed |
| 43 | -versions wait for the given time before queueing the callback routine for the relevent task to execute. |
| 44 | +Both can be called from interrupt level code. The callback routine is passed a single argument, which is the same argument that was passed to \verb|callbackRequest|, i.e., the address of the \verb|CALLBACK| structure. The second routine is a shortcut for calling both \verb|callbackSetProcess| and \verb|callbackRequest|. Both return zero in case of success, or an error code (see below). |
| 45 | + |
| 46 | +The following delayed versions wait for the given time before queueing the callback routine for the relevant thread set to execute. |
| 47 | |
| 48 | \index{callbackRequestDelayed} |
| 49 | \index{callbackRequestProcessCallbackDelayed} |
| 50 | @@ -127,6 +130,7 @@ |
| 51 | The following calls are provided: |
| 52 | |
| 53 | \index{callbackInit} |
| 54 | +\index{callbackShutdown} |
| 55 | \index{callbackSetCallback} |
| 56 | \index{callbackSetPriority} |
| 57 | \index{callbackSetUser} |
| 58 | @@ -141,6 +145,7 @@ |
| 59 | Notes: |
| 60 | \begin{verbatim} |
| 61 | void callbackInit(void); |
| 62 | +void callbackShutdown(void); |
| 63 | |
| 64 | void callbackSetCallback(void *pcallbackFunction, |
| 65 | CALLBACK *pcallback); |
| 66 | @@ -149,9 +154,9 @@ |
| 67 | void callbackGetUser(void *user, CALLBACK *pcallback); |
| 68 | void callbackSetProcess(CALLBACK *pcallback, int Priority, void *prec); |
| 69 | |
| 70 | -void callbackRequest(CALLBACK *); |
| 71 | -void callbackRequestProcessCallback(CALLBACK *pCallback, |
| 72 | -int Priority, void *prec); |
| 73 | +int callbackRequest(CALLBACK *); |
| 74 | +int callbackRequestProcessCallback( |
| 75 | + CALLBACK *pCallback, int Priority, void *prec); |
| 76 | void callbackRequestDelayed(CALLBACK *pCallback, double seconds); |
| 77 | void callbackRequestProcessCallbackDelayed( |
| 78 | CALLBACK *pCallback, int Priority, void *prec, double seconds); |
| 79 | @@ -160,12 +165,13 @@ |
| 80 | \end{verbatim} |
| 81 | |
| 82 | \begin{itemize} |
| 83 | -\item \verb|callbackInit| is performed automatically at IOC initialization, thus user code never calls this function. |
| 84 | +\item \verb|callbackInit| and \verb|callbackShutdown| are performed automatically at IOC initialization or shutdown, |
| 85 | +thus user code never calls these functions. |
| 86 | |
| 87 | \item \verb|callbackSetCallback|, \verb|callbackSetPriority|, \verb|callbackSetUser|, and \verb|callbackGetUser| are |
| 88 | actually macros. |
| 89 | |
| 90 | -\item \verb|Both callbackRequest| and \verb|callbackRequestProcessCallback| may be called from interrupt context. |
| 91 | +\item \verb|Both callbackRequest| and \verb|callbackRequestProcessCallback| may be called from interrupt context. Both return zero for success, or one of the following error codes: \verb|S_db_notInit| for a NULL callback pointer, \verb|S_db_badChoice| for an illegal priority value, or \verb|S_db_bufFull| when the associated queue is full. |
| 92 | |
| 93 | \item The delayed versions of the \verb|callbackRequest| routines wait the given time before queueing the callback. |
| 94 | |
| 95 | @@ -250,6 +256,23 @@ |
| 96 | int callbackSetQueueSize(int size) |
| 97 | \end{verbatim} |
| 98 | |
| 99 | +\subsection{Parallel Callback Tasks} |
| 100 | +\label{Parallel Callback Tasks} |
| 101 | + |
| 102 | +To enable multiple parallel callback tasks, and set the number of tasks to be started for each priority level, call |
| 103 | +\verb|callbackParallelThreads| before \verb|iocInit| in the startup file. The syntax is: |
| 104 | + |
| 105 | +\index{callbackParallelThreads} |
| 106 | +\begin{verbatim} |
| 107 | +int callbackParallelThreads(int count, const char *prio) |
| 108 | +\end{verbatim} |
| 109 | + |
| 110 | +The count argument is the number of tasks to start, with 0 indicating to use the default (number of CPUs), and negative numbers indicating to use the number of CPUs minus the specified amount. |
| 111 | + |
| 112 | +The prio argument specifies the priority level, with "" (empty string), "*", or NULL indicating to apply the definition to all priority levels. |
| 113 | + |
| 114 | +The default value is stored in the variable \verb|callbackParallelThreadsDefault| (initialized to the number of CPUs), which can be changed using the iocShell's \verb|var| command. |
| 115 | + |
| 116 | \section{Task Watchdog} |
| 117 | \label{Task Watchdog} |
| 118 | \index{Task Watchdog} |
| 119 | |
| 120 | === modified file 'tex/libCom.tex' |
| 121 | --- tex/libCom.tex 2012-01-17 18:04:20 +0000 |
| 122 | +++ tex/libCom.tex 2013-10-23 09:48:56 +0000 |
| 123 | @@ -541,13 +541,24 @@ |
| 124 | \index{ellFree2} |
| 125 | \index{ellFree} |
| 126 | \index{ellVerify} |
| 127 | + |
| 128 | + |
| 129 | \section{epicsRingBytes} |
| 130 | |
| 131 | -\verb|epicsRingBytes.h| contains |
| 132 | +\index{epicsRingBytes} |
| 133 | +\index{epicsRingBytes.h} |
| 134 | +\verb|epicsRingBytes.h| describes a C facility for a commonly used type of ring buffer. |
| 135 | + |
| 136 | +\subsection{C interface} |
| 137 | + |
| 138 | +EpicsRingBytes provides methods for creating and using ring buffers (first in first out circular buffers) that store bytes. |
| 139 | +The unlocked variant is designed so that one writer thread and one reader thread can access the ring simultaneously without requiring mutual exclusion. |
| 140 | +The locked variant uses an epicsSpinLock, and works with any numbers of writer and reader threads. |
| 141 | |
| 142 | \index{epicsRingBytes.h} |
| 143 | \begin{verbatim} |
| 144 | epicsRingBytesId epicsRingBytesCreate(int nbytes); |
| 145 | +epicsRingBytesId epicsRingBytesLockedCreate(int nbytes); |
| 146 | void epicsRingBytesDelete(epicsRingBytesId id); |
| 147 | int epicsRingBytesGet(epicsRingBytesId id, char *value,int nbytes); |
| 148 | int epicsRingBytesPut(epicsRingBytesId id, char *value,int nbytes); |
| 149 | @@ -561,6 +572,7 @@ |
| 150 | |
| 151 | \index{epicsRingBytesId} |
| 152 | \index{epicsRingBytesCreate} |
| 153 | +\index{epicsRingBytesLockedCreate} |
| 154 | \index{epicsRingBytesDelete} |
| 155 | \index{epicsRingBytesGet} |
| 156 | \index{epicsRingBytesPut} |
| 157 | @@ -575,6 +587,7 @@ |
| 158 | \textbf{Method} & \textbf{Meaning}\\ |
| 159 | \hline |
| 160 | epicsRingBytesCreate() & Create a new ring buffer of size nbytes. The returned epicsRingBytesId is passed to the other ring methods.\\ |
| 161 | +epicsRingBytesLockedCreate() & Same as epicsRingBytesCreate, but create the spin lock secured variant of the ring buffer.\\ |
| 162 | epicsRingBytesDelete() & Delete the ring buffer and free any associated memory.\\ |
| 163 | epicsRingBytesGet() & Move up to nbytes from the ring buffer to value. The number of bytes actually moved is returned.\\ |
| 164 | epicsRingBytesPut() & Move nbytes from value to the ring buffer if there is enough free space available to hold them. The number of bytes actually moved is returned, which will be zero if insufficient space exists.\\ |
| 165 | @@ -600,6 +613,7 @@ |
| 166 | |
| 167 | \end{itemize} |
| 168 | |
| 169 | + |
| 170 | \section{epicsRingPointer} |
| 171 | |
| 172 | \index{epicsRingPointer} |
| 173 | @@ -609,13 +623,14 @@ |
| 174 | \subsection{C++ Interface} |
| 175 | |
| 176 | EpicsRingPointer provides methods for creating and using ring buffers (first in first out circular buffers) that store pointers. |
| 177 | -It is designed so that a writer thread and reader thread can access the ring simultaneously without requiring mutual exclusion. |
| 178 | +The unlocked variant is designed so that one writer thread and one reader thread can access the ring simultaneously without requiring mutual exclusion. |
| 179 | +The locked variant uses an epicsSpinLock, and works with any numbers of writer and reader threads. |
| 180 | |
| 181 | \begin{verbatim} |
| 182 | template <class T> |
| 183 | class epicsRingPointer { |
| 184 | public: |
| 185 | - epicsRingPointer(int size); |
| 186 | + epicsRingPointer(int size, bool locked); |
| 187 | ~epicsRingPointer(); |
| 188 | bool push(T *p); |
| 189 | T* pop(); |
| 190 | @@ -654,21 +669,19 @@ |
| 191 | \begin{longtable}{p{1.27778in}p{5.0in}} |
| 192 | \textbf{Method} & \textbf{Meaning}\\ |
| 193 | \hline |
| 194 | -epicsRingPointer() & Constructor. The size is the maximum number of elements (pointers) that can be stored in the ring.\\ |
| 195 | +epicsRingPointer() & Constructor. The size is the maximum number of elements (pointers) that can be stored in the ring. If locked is true, the spin lock secured variant is created.\\ |
| 196 | \~{}epicsRingPointer() & Destructor.\\ |
| 197 | -push() & Push a new entry on the ring. It returns (false,true) is (failure, success). Failure means the ring was full. If a single writer is present it does not have to use a lock while performing the push. If multiple writers are present they must use a common lock while issuing the push. \\ |
| 198 | -pop() & Take a element off the ring. It returns 0 (null) if the ring was empty. If a single reader is present it does not have to lock while issuing the pop. If multiple readers are present they must use a common lock while issuing the pop.\\ |
| 199 | -flush() & Remove all elements from the ring. If this operation is performed then all access to the ring should be locked.\\ |
| 200 | +push() & Push a new entry on the ring. It returns (false,true) is (failure, success). Failure means the ring was full.\\ |
| 201 | +pop() & Take a element off the ring. It returns 0 (null) if the ring was empty.\\ |
| 202 | +flush() & Remove all elements from the ring. If this operation is performed on a ring buffer of the unsecured variant, all access to the ring should be locked.\\ |
| 203 | getFree() & Return the amount of empty space in the ring, i.e. how many additional elements it can hold.\\ |
| 204 | getUsed() & Return the number of elements stored on the ring\\ |
| 205 | getSize() & Return the size of the ring, i.e. the value of size specified when the ring was created.\\ |
| 206 | isEmpty() & Returns true if the ring is empty, else false.\\ |
| 207 | isFull() & Returns true if the ring is full, else false. |
| 208 | \end{longtable} |
| 209 | - |
| 210 | \end{center} |
| 211 | |
| 212 | - |
| 213 | \subsection{C interface} |
| 214 | |
| 215 | \index{ringPointerId} |
| 216 | @@ -685,6 +698,7 @@ |
| 217 | \begin{verbatim} |
| 218 | typedef void *epicsRingPointerId; |
| 219 | epicsRingPointerId epicsRingPointerCreate(int size); |
| 220 | + epicsRingPointerId epicsRingPointerLockedCreate(int size); |
| 221 | void epicsRingPointerDelete(epicsRingPointerId id); |
| 222 | /*epicsRingPointerPop returns 0 if the ring was empty */ |
| 223 | void * epicsRingPointerPop(epicsRingPointerId id) ; |
| 224 | @@ -700,6 +714,9 @@ |
| 225 | |
| 226 | Each C function corresponds to one of the C++ methods. |
| 227 | |
| 228 | +epicsRingPointerCreate() creates the unsecured variant, epicsRingPointerLockedCreate() creates the spin lock secured variant of the ring buffer. |
| 229 | + |
| 230 | + |
| 231 | \section{epicsTimer} |
| 232 | |
| 233 | \index{epicsTimer} |
| 234 | @@ -1567,6 +1584,26 @@ |
| 235 | call to the runTestFunc() routine. |
| 236 | The last test program or the harness program itself must finish by calling epicsExit() which triggers the test summary mechanism to generate its result outputs (from an epicsAtExit callback routine). |
| 237 | |
| 238 | +Some tests require the context of an IOC to be run. This conflicts with the idea of running multiple tests within a test harness, as iocInit() is only allowed to be called once, and some parts of the full IOC (e.g., the rsrv CA server) can not be shut down cleanly. |
| 239 | +The function iocBuildNoCA() allows to start an IOC without its Channel Access parts, so that it can be shutdown quite cleany using iocShutdown(). This feature is only intended to be used from test programs. Do not use it on productional IOCs. After building the IOC using iocBuildNoCA() or iocBuild(), it has to be started by calling iocRun(). The suggested call sequence in a test program that needs to run the IOC without Channel Access is: |
| 240 | + |
| 241 | +\begin{verbatim} |
| 242 | +#include "iocInit.h" |
| 243 | + |
| 244 | +MAIN(iocTest) |
| 245 | +{ |
| 246 | + iocBuildNoCA() || iocRun(); |
| 247 | + |
| 248 | +[... test code ...] |
| 249 | + |
| 250 | + iocShutdown(); |
| 251 | + dbFreeBase(pdbbase); |
| 252 | + registryFree(); |
| 253 | + pdbbase = NULL; |
| 254 | + return testDone(); |
| 255 | +} |
| 256 | +\end{verbatim} |
| 257 | + |
| 258 | To make it easier to create a single test program that can be built for both the embedded and workstation operating system harnesses, the header file \verb|testMain.h| provides a convenience macro MAIN() that adjusts the name of the test program according to the platform it is running on: main() on workstations and a regular function name on embedded systems. |
| 259 | |
| 260 | \index{testMain.h} |
| 261 | |
| 262 | === modified file 'tex/libComOsi.tex' |
| 263 | --- tex/libComOsi.tex 2013-05-17 14:29:34 +0000 |
| 264 | +++ tex/libComOsi.tex 2013-10-23 09:48:56 +0000 |
| 265 | @@ -841,6 +841,49 @@ |
| 266 | |
| 267 | A posix version is implemented via pthreads. |
| 268 | |
| 269 | + |
| 270 | +\section{epicsSpin} |
| 271 | + |
| 272 | +\index{epicsSpin} |
| 273 | +\index{epicsSpin.h} |
| 274 | +\verb|epicsSpin.h| contains definitions for a spin lock semaphore. |
| 275 | + |
| 276 | +\subsection{C Interface} |
| 277 | + |
| 278 | +\begin{verbatim} |
| 279 | +typedef struct epicsSpin *epicsSpinId; |
| 280 | + |
| 281 | +epicsSpinId epicsSpinCreate(); |
| 282 | +void epicsSpinDestroy(epicsSpinId); |
| 283 | + |
| 284 | +void epicsSpinLock(epicsSpinId); |
| 285 | +int epicsSpinTryLock(epicsSpinId); |
| 286 | +void epicsSpinUnlock(epicsSpinId); |
| 287 | +\end{verbatim} |
| 288 | + |
| 289 | +\index{epicsSpinId} |
| 290 | +\index{epicsSpinCreate} |
| 291 | +\index{epicsSpinDestroy} |
| 292 | +\index{epicsSpinLock} |
| 293 | +\index{epicsSpinTryLock} |
| 294 | +\index{epicsSpinUnlock} |
| 295 | +\begin{center} |
| 296 | +\begin{longtable}{p{1.38889in}p{5.0in}} |
| 297 | +\textbf{Method} & \textbf{Meaning}\\ |
| 298 | +\hline |
| 299 | +epicsSpinCreate & Create a spin lock, allocate required resources, and initialize it to an unlocked state.\\ |
| 300 | +epicsSpinDestroy() & Remove the spin lock and any resources it uses. Any further use of the spin lock may result in unknown (most certainly bad) behavior. The results are also undefined if epicsSpinDestroy is used when a thread holds the lock.\\ |
| 301 | +epicsSpinLock() & Wait (by spinning, i.e. busy waiting) until the resource is free. After a successful lock, no additional, i.e. recursive, locking attempts may be issued.\\ |
| 302 | +epicsSpinTryLock() & Similar to lock except that, if the resource is owned by another thread, the call completes immediately. The return value is 0 if the resource is owned by the caller.\\ |
| 303 | +epicsSpinUnlock() & Release the spin lock resource which was locked by epicsSpinLock or epicsSpinTryLock. The results are undefined if the lock is not held by the calling thread, or if epicsSpinUnlock is used on an uninitialized spin lock.\\ |
| 304 | +\end{longtable} |
| 305 | +\end{center} |
| 306 | + |
| 307 | +\subsection{Implementation Notes} |
| 308 | + |
| 309 | +The default implementation uses POSIX spin locks on POSIX compliant systems, and epicsMutex for non-POSIX environments. |
| 310 | + |
| 311 | + |
| 312 | \section{epicsStdlib} |
| 313 | |
| 314 | \index{epicsStdlib} |
| 315 | @@ -864,7 +907,6 @@ |
| 316 | They are provided because some architectures have implementations of scanf which do not accept NAN or INFINITY. |
| 317 | |
| 318 | |
| 319 | - |
| 320 | \section{epicsStdio} |
| 321 | |
| 322 | \index{epicsStdio} |
| 323 | @@ -1018,6 +1060,7 @@ |
| 324 | double epicsThreadSleepQuantum(void); |
| 325 | epicsThreadId epicsThreadGetIdSelf(void); |
| 326 | epicsThreadId epicsThreadGetId(const char *name); |
| 327 | +int epicsThreadGetCPUs(void); |
| 328 | |
| 329 | const char * epicsThreadGetNameSelf(void); |
| 330 | void epicsThreadGetName(epicsThreadId id, char *name, size_t size); |
| 331 | @@ -1071,6 +1114,7 @@ |
| 332 | \index{epicsThreadSleepQuantum} |
| 333 | \index{epicsThreadGetIdSelf} |
| 334 | \index{epicsThreadGetId} |
| 335 | +\index{epicsThreadGetCPUs} |
| 336 | \index{epicsThreadGetNameSelf} |
| 337 | \index{epicsThreadGetName} |
| 338 | \index{epicsThreadIsOkToBlock} |
| 339 | @@ -1103,6 +1147,7 @@ |
| 340 | epicsThreadSleepQuantum & This function returns the minimum slumber interval obtainable with epicsThreadSleep() in seconds. On most OS there is a system scheduler interrupt interval which determines the value of this parameter. Knowledge of this parameter is used by the various components of EPICS to improve scheduling of software tasks intime when the reduction of average time scheduling errors is important.If this parameter is unknown or is unpredictable for a particular OS then it is safe to return zero.\\ |
| 341 | epicsThreadGetIdSelf & Get the threadId of the calling thread.\\ |
| 342 | epicsThreadGetId & Get the threadId of the specified thread. A return of 0 means that no thread was found with the specified name.\\ |
| 343 | +epicsThreadGetCPUs & Get the number of CPUs (logical cores) available to the IOC. On systems that use Hyper-Threading, this may be up to twice the number of physical cores.\\ |
| 344 | epicsThreadGetNameSelf & Get the name of the calling thread.\\ |
| 345 | epicsThreadGetName & Get the name of the specified thread. The value is copied to a caller specified buffer so that if the thread terminates the caller is not left with a pointer to something that may no longer exist.\\ |
| 346 | epicsThreadIsOkToBlock & Is it OK for a thread to block? This can be called by support code that does not know if it is called in a thread that should not block. For example the errlog system calls this to decide when messages should be displayed on the console.\\ |
| 347 | |
| 348 | === modified file 'tex/scanning.tex' |
| 349 | --- tex/scanning.tex 2010-11-15 18:16:16 +0000 |
| 350 | +++ tex/scanning.tex 2013-10-23 09:48:56 +0000 |
| 351 | @@ -123,6 +123,7 @@ |
| 352 | |
| 353 | /*definitions for I/O Interrupt Scanning */ |
| 354 | typedef struct io_scan_list *IOSCANPVT; |
| 355 | +typedef void (*io_scan_complete)(void *, IOSCANPVT, int); |
| 356 | |
| 357 | long scanInit(void); |
| 358 | void scanRun(void); |
| 359 | @@ -140,7 +141,8 @@ |
| 360 | int scanpiol(void); /* print io_event list*/ |
| 361 | |
| 362 | void scanIoInit(IOSCANPVT *); |
| 363 | -void scanIoRequest(IOSCANPVT); |
| 364 | +unsigned int scanIoRequest(IOSCANPVT); |
| 365 | +void scanIoSetComplete(IOSCANPVT, io_scan_complete, void*); |
| 366 | \end{verbatim} |
| 367 | |
| 368 | \index{SCAN\_1ST\_PERIODIC}The first set of definitions defines the various scan types. The next definition \verb|IOSCANPVT| is used when interfacing with |
| 369 | @@ -210,7 +212,7 @@ |
| 370 | \begin{enumerate} |
| 371 | \item Include \verb|<dbScan.h>| |
| 372 | |
| 373 | -\item For each separate event source the following must be done: |
| 374 | +\item For each separate I/O event source the following must be done: |
| 375 | |
| 376 | \begin{enumerate} |
| 377 | |
| 378 | @@ -242,12 +244,25 @@ |
| 379 | \item Whenever an I/O event is detected call \verb|scanIoRequest|, e.g. |
| 380 | |
| 381 | \begin{verbatim} |
| 382 | -scanIoRequest(ioscanpvt) |
| 383 | -\end{verbatim} |
| 384 | - |
| 385 | -This routine can be called from interrupt level. The request is actually directed to one of the standard callback |
| 386 | -tasks. The actual one is determined by the \verb|PRIO| field of \verb|dbCommon|. |
| 387 | - |
| 388 | +scanIoRequest(ioscanpvt); |
| 389 | +\end{verbatim} |
| 390 | + |
| 391 | +This routine can be called from interrupt level. The request is queued and will be handled by one of the standard callback threads. There are three sets of callback threads feeding from three queues, one for each priority level (see \ref{sec:callbackThreads}), and the \verb|PRIO| field of the record determines which one is being used. |
| 392 | + |
| 393 | +\verb|scanIoRequest| returns a bit mask that indicates the priority levels for which record processing requests have been queued. |
| 394 | + |
| 395 | +Device and driver support that wants to implement flow control can set up a completion callback by calling \verb|scanIoSetComplete|, e.g. |
| 396 | + |
| 397 | +\begin{verbatim} |
| 398 | +static void myCallback(void *arg, IOSCANPVT pvt, int prio) |
| 399 | +{ |
| 400 | +[...] |
| 401 | +} |
| 402 | + |
| 403 | +scanIoSetComplete(ioscanpvt, myCallback, (void*)arg); |
| 404 | +\end{verbatim} |
| 405 | + |
| 406 | +The completion callback will be called from the callback thread context, once per priority used (bits set in the return value of \verb|scanIoRequest|), after the list of records with that priority level has been processed. Note that for records with asynchronous device support, record processing might not have completed when the callback is issued. |
| 407 | \end{enumerate} |
| 408 | |
| 409 | The following code fragment shows an event record device support module that supports I/O event scanning: |
