Merge ~info-martin-konrad/epics-base:callbackQueueStatus into ~epics-core/epics-base/+git/epics-base:3.16
- Git
- lp:~info-martin-konrad/epics-base
- callbackQueueStatus
- Merge into 3.16
Status: | Merged |
---|---|
Approved by: | Andrew Johnson |
Approved revision: | 6f919c3991bc264e91a313415a402250bab463ac |
Merged at revision: | ec036cb26d6664fa463a076d20cd3b41a09821bd |
Proposed branch: | ~info-martin-konrad/epics-base:callbackQueueStatus |
Merge into: | ~epics-core/epics-base/+git/epics-base:3.16 |
Diff against target: |
631 lines (+232/-20) 11 files modified
src/ioc/db/callback.c (+47/-0) src/ioc/db/callback.h (+9/-0) src/ioc/db/dbIocRegister.c (+24/-0) src/ioc/db/dbScan.c (+37/-0) src/ioc/db/dbScan.h (+9/-0) src/libCom/ring/epicsRingBytes.c (+28/-3) src/libCom/ring/epicsRingBytes.h (+3/-0) src/libCom/ring/epicsRingPointer.cpp (+12/-0) src/libCom/ring/epicsRingPointer.h (+34/-3) src/libCom/test/ringBytesTest.c (+21/-13) src/libCom/test/ringPointerTest.c (+8/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Andrew Johnson | Approve | ||
mdavidsaver | Needs Fixing | ||
Ralph Lange | Pending | ||
Review via email: mp+352918@code.launchpad.net |
Commit message
Description of the change
Allow scanOnce and callback queue utilization to be monitored from the IOC shell and iocStats. Fixes lp:1786540
Ralph Lange (ralph-lange) wrote : | # |
Andrew Johnson (anj) wrote : | # |
Core Group review at ESS: Please implement Ralph's high water mark (which needs implementing inside the queueing code) and use epicsAtomic types to protect access to the usage data.
Martin Konrad (info-martin-konrad) wrote : | # |
I have implemented the high-water mark Ralph suggested. This is in fact very useful on small IOCs which are booting fast. The one I'm mainly testing with is seeing very heavy load after IOC start and I can watch the queue usage drop slowly in the first minutes after IOC boot (so in some cases the PVs showing the current queue usage also make sense).
I'm now also leveraging epicsAtomic to make my code thread safe.
I understand Ralph's concerns about cumulative values. However, I'm a little hesitant to reset the stats at every read since we offer read out from both the IOC shell and via iocStats. If a read by iocStats resets the number of queue overruns once a second chances are I'll never see anything on the IOC shell...
mdavidsaver (mdavidsaver) wrote : | # |
Looking at this again, I find myself wondering about adding so many new public functions. 4 query functions and 1 printer, repeated 3 times. This in addition to the ring buffer feature addition. I'd rather see the 4x query functions combined into one which accepts a struct. Something like:
typedef struct QueueStats {
size_t size;
size_t numUsed[
size_t maxUsed[
size_t numOverflow[
} QueueStats;
void queryQueue(int reset, QueueStates* result);
I think this would be a better match for the (only) prospective outside user, devIocStats.
The printer functions are accessible through iocsh, so it doesn't seem like they need to be public.
Also, I like the idea of combining fetch with (conditional) reset to zero. I've seen and used this idea in the past, and found it a good fit for how I ended up access these stats.
I'm not going to push for this too much if there is a desire to see this in 3.16.2.
At minimum though, the idea of const-ness should apply to the C API as well as C++. eg. epicsRingPointe
Martin Konrad (info-martin-konrad) wrote : | # |
Thanks for the feedback. I reworked this to include Michael's comments. I have also updated my IOCStats PR. You might want to look at my code again - this ended up to be quite a few changes.
Andrew Johnson (anj) wrote : | # |
Hi Martin, if you committed any changes I think you forgot to push them to launchpad, this merge request still only shows your original code. There may still be a chance to get it into the final 3.16.2 release.
Martin Konrad (info-martin-konrad) wrote : | # |
Andrew, I double checked, this is my latest code. It has the const-ness change, the reset feature and it has been modified to pull all values at the same time. Note that I have squashed my commits together to avoid cluttering the history.
Andrew Johnson (anj) wrote : | # |
@mdavidsaver Please re-review this merge, should it go into 3.16.2?
Andrew Johnson (anj) wrote : | # |
Some older C compilers (MSVC, older GCC) will fail while building this code, there are several places where it declares variables after the first statement in a block. It's fine to do that in C++ code, but still not in our C code yet.
I don't understand the use of epicsAtomic functions for accessing callbackQueueSize. The queue size must be set before callbackIsInit and can't be changed after that (unless we go through callbackCleanup() but that clears callbackIsInit anyway and if callbackQueueSt
Martin Konrad (info-martin-konrad) wrote : | # |
I tweaked the code to make pre-C99 compilers happy.
I have also removed the unneeded epicsAtomic from the callbackQueueSize variable.
Let me know if you want me to squash the commits.
Andrew Johnson (anj) wrote : | # |
Thanks for those changes. Once you have created a Merge Request I would ask you to *never* squash commits, as doing that prevents us from seeing what additional changes you have made since we last looked at the proposal (hence my confusion on 2018-10-30). If you really want to hide the evidence of your having taken a U-turn from the final history, resubmitting it as a new Merge Request at the end of the review (or after a major rewrite) is probably the best way to handle that.
However I'm still unsure about accepting this; see my code-specific comments below. Further review from the other core developers is still welcome.
I also think these changes deserve some test code; we have test programs in src/libCom/test for both the bytes and pointer versions of the ring-buffer that could be extended, although the bytes version is single-threaded and just tests basic functionality — no inter-thread issues or tests for a spin-locked ring buffer.
Martin Konrad (info-martin-konrad) wrote : | # |
I'm glad you asked me to write tests - that helped to I catch an issue in the code...
Do we need multi-threaded tests for this feature?
Andrew Johnson (anj) : | # |
Martin Konrad (info-martin-konrad) wrote : | # |
After our discussion I switched from atomics to using the existing spin lock. This should make the code a little bit easier to understand.
Andrew Johnson (anj) wrote : | # |
Merging this, but one change I've made is to rename your QueuePrintStatus() routines and the QueueStatus iocsh commands (and their associated routines and data structures) to QueueShow for consistency with other subsystems. We normally use a Show suffix for routines that print stuff to stdout, and Status for querying whether a subsystem is happy, so your QueueStatus() API routines are fine. This is also necessary to maintain command-line compatibility with the VxWorks shell.
Preview Diff
1 | diff --git a/src/ioc/db/callback.c b/src/ioc/db/callback.c | |||
2 | index ae07414..1b6c249 100644 | |||
3 | --- a/src/ioc/db/callback.c | |||
4 | +++ b/src/ioc/db/callback.c | |||
5 | @@ -54,6 +54,7 @@ typedef struct cbQueueSet { | |||
6 | 54 | epicsEventId semWakeUp; | 54 | epicsEventId semWakeUp; |
7 | 55 | epicsRingPointerId queue; | 55 | epicsRingPointerId queue; |
8 | 56 | int queueOverflow; | 56 | int queueOverflow; |
9 | 57 | int queueOverflows; | ||
10 | 57 | int shutdown; | 58 | int shutdown; |
11 | 58 | int threadsConfigured; | 59 | int threadsConfigured; |
12 | 59 | int threadsRunning; | 60 | int threadsRunning; |
13 | @@ -103,6 +104,51 @@ int callbackSetQueueSize(int size) | |||
14 | 103 | return 0; | 104 | return 0; |
15 | 104 | } | 105 | } |
16 | 105 | 106 | ||
17 | 107 | int callbackQueueStatus(const int reset, callbackQueueStats *result) | ||
18 | 108 | { | ||
19 | 109 | int ret; | ||
20 | 110 | if (!callbackIsInit) return -1; | ||
21 | 111 | if (result) { | ||
22 | 112 | int prio; | ||
23 | 113 | result->size = callbackQueueSize; | ||
24 | 114 | for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { | ||
25 | 115 | epicsRingPointerId qId = callbackQueue[prio].queue; | ||
26 | 116 | result->numUsed[prio] = epicsRingPointerGetUsed(qId); | ||
27 | 117 | result->maxUsed[prio] = epicsRingPointerGetHighWaterMark(qId); | ||
28 | 118 | result->numOverflow[prio] = epicsAtomicGetIntT(&callbackQueue[prio].queueOverflows); | ||
29 | 119 | } | ||
30 | 120 | ret = 0; | ||
31 | 121 | } else { | ||
32 | 122 | ret = -2; | ||
33 | 123 | } | ||
34 | 124 | if (reset) { | ||
35 | 125 | int prio; | ||
36 | 126 | for(prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { | ||
37 | 127 | epicsRingPointerResetHighWaterMark(callbackQueue[prio].queue); | ||
38 | 128 | } | ||
39 | 129 | } | ||
40 | 130 | return ret; | ||
41 | 131 | } | ||
42 | 132 | |||
43 | 133 | void callbackQueuePrintStatus(const int reset) | ||
44 | 134 | { | ||
45 | 135 | callbackQueueStats stats; | ||
46 | 136 | if (callbackQueueStatus(reset, &stats) == -1) { | ||
47 | 137 | fprintf(stderr, "Callback system not initialized, yet. Please run " | ||
48 | 138 | "iocInit before using this command.\n"); | ||
49 | 139 | } else { | ||
50 | 140 | int prio; | ||
51 | 141 | printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); | ||
52 | 142 | for (prio = 0; prio < NUM_CALLBACK_PRIORITIES; prio++) { | ||
53 | 143 | double qusage = 100.0 * stats.numUsed[prio] / stats.size; | ||
54 | 144 | printf("%8s %15d %10d %6d %6.1f %11d\n", | ||
55 | 145 | threadNamePrefix[prio], stats.maxUsed[prio], | ||
56 | 146 | stats.numUsed[prio], stats.size, qusage, | ||
57 | 147 | stats.numOverflow[prio]); | ||
58 | 148 | } | ||
59 | 149 | } | ||
60 | 150 | } | ||
61 | 151 | |||
62 | 106 | int callbackParallelThreads(int count, const char *prio) | 152 | int callbackParallelThreads(int count, const char *prio) |
63 | 107 | { | 153 | { |
64 | 108 | if (callbackIsInit) { | 154 | if (callbackIsInit) { |
65 | @@ -290,6 +336,7 @@ int callbackRequest(CALLBACK *pcallback) | |||
66 | 290 | if (!pushOK) { | 336 | if (!pushOK) { |
67 | 291 | epicsInterruptContextMessage(fullMessage[priority]); | 337 | epicsInterruptContextMessage(fullMessage[priority]); |
68 | 292 | mySet->queueOverflow = TRUE; | 338 | mySet->queueOverflow = TRUE; |
69 | 339 | epicsAtomicIncrIntT(&mySet->queueOverflows); | ||
70 | 293 | return S_db_bufFull; | 340 | return S_db_bufFull; |
71 | 294 | } | 341 | } |
72 | 295 | epicsEventSignal(mySet->semWakeUp); | 342 | epicsEventSignal(mySet->semWakeUp); |
73 | diff --git a/src/ioc/db/callback.h b/src/ioc/db/callback.h | |||
74 | index fa626d1..dd13cdb 100644 | |||
75 | --- a/src/ioc/db/callback.h | |||
76 | +++ b/src/ioc/db/callback.h | |||
77 | @@ -48,6 +48,13 @@ typedef epicsCallback CALLBACK; | |||
78 | 48 | 48 | ||
79 | 49 | typedef void (*CALLBACKFUNC)(struct callbackPvt*); | 49 | typedef void (*CALLBACKFUNC)(struct callbackPvt*); |
80 | 50 | 50 | ||
81 | 51 | typedef struct callbackQueueStats { | ||
82 | 52 | int size; | ||
83 | 53 | int numUsed[NUM_CALLBACK_PRIORITIES]; | ||
84 | 54 | int maxUsed[NUM_CALLBACK_PRIORITIES]; | ||
85 | 55 | int numOverflow[NUM_CALLBACK_PRIORITIES]; | ||
86 | 56 | } callbackQueueStats; | ||
87 | 57 | |||
88 | 51 | #define callbackSetCallback(PFUN, PCALLBACK) \ | 58 | #define callbackSetCallback(PFUN, PCALLBACK) \ |
89 | 52 | ( (PCALLBACK)->callback = (PFUN) ) | 59 | ( (PCALLBACK)->callback = (PFUN) ) |
90 | 53 | #define callbackSetPriority(PRIORITY, PCALLBACK) \ | 60 | #define callbackSetPriority(PRIORITY, PCALLBACK) \ |
91 | @@ -73,6 +80,8 @@ epicsShareFunc void callbackCancelDelayed(CALLBACK *pcallback); | |||
92 | 73 | epicsShareFunc void callbackRequestProcessCallbackDelayed( | 80 | epicsShareFunc void callbackRequestProcessCallbackDelayed( |
93 | 74 | CALLBACK *pCallback, int Priority, void *pRec, double seconds); | 81 | CALLBACK *pCallback, int Priority, void *pRec, double seconds); |
94 | 75 | epicsShareFunc int callbackSetQueueSize(int size); | 82 | epicsShareFunc int callbackSetQueueSize(int size); |
95 | 83 | epicsShareFunc int callbackQueueStatus(const int reset, callbackQueueStats *result); | ||
96 | 84 | void callbackQueuePrintStatus(const int reset); | ||
97 | 76 | epicsShareFunc int callbackParallelThreads(int count, const char *prio); | 85 | epicsShareFunc int callbackParallelThreads(int count, const char *prio); |
98 | 77 | 86 | ||
99 | 78 | #ifdef __cplusplus | 87 | #ifdef __cplusplus |
100 | diff --git a/src/ioc/db/dbIocRegister.c b/src/ioc/db/dbIocRegister.c | |||
101 | index 4d0b88c..cecf756 100644 | |||
102 | --- a/src/ioc/db/dbIocRegister.c | |||
103 | +++ b/src/ioc/db/dbIocRegister.c | |||
104 | @@ -296,6 +296,17 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args) | |||
105 | 296 | scanOnceSetQueueSize(args[0].ival); | 296 | scanOnceSetQueueSize(args[0].ival); |
106 | 297 | } | 297 | } |
107 | 298 | 298 | ||
108 | 299 | /* scanOnceQueueStatus */ | ||
109 | 300 | static const iocshArg scanOnceQueueStatusArg0 = { "reset",iocshArgInt}; | ||
110 | 301 | static const iocshArg * const scanOnceQueueStatusArgs[1] = | ||
111 | 302 | {&scanOnceQueueStatusArg0}; | ||
112 | 303 | static const iocshFuncDef scanOnceQueueStatusFuncDef = | ||
113 | 304 | {"scanOnceQueueStatus",1,scanOnceQueueStatusArgs}; | ||
114 | 305 | static void scanOnceQueueStatusCallFunc(const iocshArgBuf *args) | ||
115 | 306 | { | ||
116 | 307 | scanOnceQueuePrintStatus(args[0].ival); | ||
117 | 308 | } | ||
118 | 309 | |||
119 | 299 | /* scanppl */ | 310 | /* scanppl */ |
120 | 300 | static const iocshArg scanpplArg0 = { "rate",iocshArgDouble}; | 311 | static const iocshArg scanpplArg0 = { "rate",iocshArgDouble}; |
121 | 301 | static const iocshArg * const scanpplArgs[1] = {&scanpplArg0}; | 312 | static const iocshArg * const scanpplArgs[1] = {&scanpplArg0}; |
122 | @@ -335,6 +346,17 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args) | |||
123 | 335 | callbackSetQueueSize(args[0].ival); | 346 | callbackSetQueueSize(args[0].ival); |
124 | 336 | } | 347 | } |
125 | 337 | 348 | ||
126 | 349 | /* callbackQueueStatus */ | ||
127 | 350 | static const iocshArg callbackQueueStatusArg0 = { "reset", iocshArgInt}; | ||
128 | 351 | static const iocshArg * const callbackQueueStatusArgs[1] = | ||
129 | 352 | {&callbackQueueStatusArg0}; | ||
130 | 353 | static const iocshFuncDef callbackQueueStatusFuncDef = | ||
131 | 354 | {"callbackQueueStatus",1,callbackQueueStatusArgs}; | ||
132 | 355 | static void callbackQueueStatusCallFunc(const iocshArgBuf *args) | ||
133 | 356 | { | ||
134 | 357 | callbackQueuePrintStatus(args[0].ival); | ||
135 | 358 | } | ||
136 | 359 | |||
137 | 338 | /* callbackParallelThreads */ | 360 | /* callbackParallelThreads */ |
138 | 339 | static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt}; | 361 | static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgInt}; |
139 | 340 | static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString}; | 362 | static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString}; |
140 | @@ -441,12 +463,14 @@ void dbIocRegister(void) | |||
141 | 441 | iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc); | 463 | iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc); |
142 | 442 | 464 | ||
143 | 443 | iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc); | 465 | iocshRegister(&scanOnceSetQueueSizeFuncDef,scanOnceSetQueueSizeCallFunc); |
144 | 466 | iocshRegister(&scanOnceQueueStatusFuncDef,scanOnceQueueStatusCallFunc); | ||
145 | 444 | iocshRegister(&scanpplFuncDef,scanpplCallFunc); | 467 | iocshRegister(&scanpplFuncDef,scanpplCallFunc); |
146 | 445 | iocshRegister(&scanpelFuncDef,scanpelCallFunc); | 468 | iocshRegister(&scanpelFuncDef,scanpelCallFunc); |
147 | 446 | iocshRegister(&postEventFuncDef,postEventCallFunc); | 469 | iocshRegister(&postEventFuncDef,postEventCallFunc); |
148 | 447 | iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); | 470 | iocshRegister(&scanpiolFuncDef,scanpiolCallFunc); |
149 | 448 | 471 | ||
150 | 449 | iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); | 472 | iocshRegister(&callbackSetQueueSizeFuncDef,callbackSetQueueSizeCallFunc); |
151 | 473 | iocshRegister(&callbackQueueStatusFuncDef,callbackQueueStatusCallFunc); | ||
152 | 450 | iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc); | 474 | iocshRegister(&callbackParallelThreadsFuncDef,callbackParallelThreadsCallFunc); |
153 | 451 | 475 | ||
154 | 452 | /* Needed before callback system is initialized */ | 476 | /* Needed before callback system is initialized */ |
155 | diff --git a/src/ioc/db/dbScan.c b/src/ioc/db/dbScan.c | |||
156 | index e5c78fe..e0e14e0 100644 | |||
157 | --- a/src/ioc/db/dbScan.c | |||
158 | +++ b/src/ioc/db/dbScan.c | |||
159 | @@ -24,6 +24,7 @@ | |||
160 | 24 | #include "cantProceed.h" | 24 | #include "cantProceed.h" |
161 | 25 | #include "dbDefs.h" | 25 | #include "dbDefs.h" |
162 | 26 | #include "ellLib.h" | 26 | #include "ellLib.h" |
163 | 27 | #include "epicsAtomic.h" | ||
164 | 27 | #include "epicsEvent.h" | 28 | #include "epicsEvent.h" |
165 | 28 | #include "epicsMutex.h" | 29 | #include "epicsMutex.h" |
166 | 29 | #include "epicsPrint.h" | 30 | #include "epicsPrint.h" |
167 | @@ -63,6 +64,7 @@ static volatile enum ctl scanCtl; | |||
168 | 63 | static int onceQueueSize = 1000; | 64 | static int onceQueueSize = 1000; |
169 | 64 | static epicsEventId onceSem; | 65 | static epicsEventId onceSem; |
170 | 65 | static epicsRingBytesId onceQ; | 66 | static epicsRingBytesId onceQ; |
171 | 67 | static int onceQOverruns = 0; | ||
172 | 66 | static epicsThreadId onceTaskId; | 68 | static epicsThreadId onceTaskId; |
173 | 67 | static void *exitOnce; | 69 | static void *exitOnce; |
174 | 68 | 70 | ||
175 | @@ -676,6 +678,7 @@ int scanOnceCallback(struct dbCommon *precord, once_complete cb, void *usr) | |||
176 | 676 | if (!pushOK) { | 678 | if (!pushOK) { |
177 | 677 | if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); | 679 | if (newOverflow) errlogPrintf("scanOnce: Ring buffer overflow\n"); |
178 | 678 | newOverflow = FALSE; | 680 | newOverflow = FALSE; |
179 | 681 | epicsAtomicIncrIntT(&onceQOverruns); | ||
180 | 679 | } else { | 682 | } else { |
181 | 680 | newOverflow = TRUE; | 683 | newOverflow = TRUE; |
182 | 681 | } | 684 | } |
183 | @@ -722,6 +725,40 @@ int scanOnceSetQueueSize(int size) | |||
184 | 722 | return 0; | 725 | return 0; |
185 | 723 | } | 726 | } |
186 | 724 | 727 | ||
187 | 728 | int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result) | ||
188 | 729 | { | ||
189 | 730 | int ret; | ||
190 | 731 | if (!onceQ) return -1; | ||
191 | 732 | if (result) { | ||
192 | 733 | result->size = epicsRingBytesSize(onceQ) / sizeof(onceEntry); | ||
193 | 734 | result->numUsed = epicsRingBytesUsedBytes(onceQ) / sizeof(onceEntry); | ||
194 | 735 | result->maxUsed = epicsRingBytesHighWaterMark(onceQ) / sizeof(onceEntry); | ||
195 | 736 | result->numOverflow = epicsAtomicGetIntT(&onceQOverruns); | ||
196 | 737 | ret = 0; | ||
197 | 738 | } else { | ||
198 | 739 | ret = -2; | ||
199 | 740 | } | ||
200 | 741 | if (reset) { | ||
201 | 742 | epicsRingBytesResetHighWaterMark(onceQ); | ||
202 | 743 | } | ||
203 | 744 | return ret; | ||
204 | 745 | } | ||
205 | 746 | |||
206 | 747 | void scanOnceQueuePrintStatus(const int reset) | ||
207 | 748 | { | ||
208 | 749 | scanOnceQueueStats stats; | ||
209 | 750 | if (scanOnceQueueStatus(reset, &stats) == -1) { | ||
210 | 751 | fprintf(stderr, "scanOnce system not initialized, yet. Please run " | ||
211 | 752 | "iocInit before using this command.\n"); | ||
212 | 753 | } else { | ||
213 | 754 | double qusage = 100.0 * stats.numUsed / stats.size; | ||
214 | 755 | printf("PRIORITY HIGH-WATER MARK ITEMS IN Q Q SIZE %% USED Q OVERFLOWS\n"); | ||
215 | 756 | printf("%8s %15d %10d %6d %6.1f %11d\n", "scanOnce", stats.maxUsed, | ||
216 | 757 | stats.numUsed, stats.size, qusage, | ||
217 | 758 | epicsAtomicGetIntT(&onceQOverruns)); | ||
218 | 759 | } | ||
219 | 760 | } | ||
220 | 761 | |||
221 | 725 | static void initOnce(void) | 762 | static void initOnce(void) |
222 | 726 | { | 763 | { |
223 | 727 | if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { | 764 | if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) { |
224 | diff --git a/src/ioc/db/dbScan.h b/src/ioc/db/dbScan.h | |||
225 | index d483a0c..830d3a8 100644 | |||
226 | --- a/src/ioc/db/dbScan.h | |||
227 | +++ b/src/ioc/db/dbScan.h | |||
228 | @@ -42,6 +42,13 @@ struct dbCommon; | |||
229 | 42 | typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); | 42 | typedef void (*io_scan_complete)(void *usr, IOSCANPVT, int prio); |
230 | 43 | typedef void (*once_complete)(void *usr, struct dbCommon*); | 43 | typedef void (*once_complete)(void *usr, struct dbCommon*); |
231 | 44 | 44 | ||
232 | 45 | typedef struct scanOnceQueueStats { | ||
233 | 46 | int size; | ||
234 | 47 | int numUsed; | ||
235 | 48 | int maxUsed; | ||
236 | 49 | int numOverflow; | ||
237 | 50 | } scanOnceQueueStats; | ||
238 | 51 | |||
239 | 45 | epicsShareFunc long scanInit(void); | 52 | epicsShareFunc long scanInit(void); |
240 | 46 | epicsShareFunc void scanRun(void); | 53 | epicsShareFunc void scanRun(void); |
241 | 47 | epicsShareFunc void scanPause(void); | 54 | epicsShareFunc void scanPause(void); |
242 | @@ -57,6 +64,8 @@ epicsShareFunc double scanPeriod(int scan); | |||
243 | 57 | epicsShareFunc int scanOnce(struct dbCommon *); | 64 | epicsShareFunc int scanOnce(struct dbCommon *); |
244 | 58 | epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); | 65 | epicsShareFunc int scanOnceCallback(struct dbCommon *, once_complete cb, void *usr); |
245 | 59 | epicsShareFunc int scanOnceSetQueueSize(int size); | 66 | epicsShareFunc int scanOnceSetQueueSize(int size); |
246 | 67 | epicsShareFunc int scanOnceQueueStatus(const int reset, scanOnceQueueStats *result); | ||
247 | 68 | void scanOnceQueuePrintStatus(const int reset); | ||
248 | 60 | 69 | ||
249 | 61 | /*print periodic lists*/ | 70 | /*print periodic lists*/ |
250 | 62 | epicsShareFunc int scanppl(double rate); | 71 | epicsShareFunc int scanppl(double rate); |
251 | diff --git a/src/libCom/ring/epicsRingBytes.c b/src/libCom/ring/epicsRingBytes.c | |||
252 | index cb7e52e..ab048e4 100644 | |||
253 | --- a/src/libCom/ring/epicsRingBytes.c | |||
254 | +++ b/src/libCom/ring/epicsRingBytes.c | |||
255 | @@ -38,6 +38,7 @@ typedef struct ringPvt { | |||
256 | 38 | volatile int nextPut; | 38 | volatile int nextPut; |
257 | 39 | volatile int nextGet; | 39 | volatile int nextGet; |
258 | 40 | int size; | 40 | int size; |
259 | 41 | int highWaterMark; | ||
260 | 41 | volatile char buffer[1]; /* actually larger */ | 42 | volatile char buffer[1]; /* actually larger */ |
261 | 42 | }ringPvt; | 43 | }ringPvt; |
262 | 43 | 44 | ||
263 | @@ -47,6 +48,7 @@ epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int size) | |||
264 | 47 | if(!pring) | 48 | if(!pring) |
265 | 48 | return NULL; | 49 | return NULL; |
266 | 49 | pring->size = size + SLOP; | 50 | pring->size = size + SLOP; |
267 | 51 | pring->highWaterMark = 0; | ||
268 | 50 | pring->nextGet = 0; | 52 | pring->nextGet = 0; |
269 | 51 | pring->nextPut = 0; | 53 | pring->nextPut = 0; |
270 | 52 | pring->lock = 0; | 54 | pring->lock = 0; |
271 | @@ -118,7 +120,7 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( | |||
272 | 118 | { | 120 | { |
273 | 119 | ringPvt *pring = (ringPvt *)id; | 121 | ringPvt *pring = (ringPvt *)id; |
274 | 120 | int nextGet, nextPut, size; | 122 | int nextGet, nextPut, size; |
276 | 121 | int freeCount, copyCount, topCount; | 123 | int freeCount, copyCount, topCount, used; |
277 | 122 | 124 | ||
278 | 123 | if (pring->lock) epicsSpinLock(pring->lock); | 125 | if (pring->lock) epicsSpinLock(pring->lock); |
279 | 124 | nextGet = pring->nextGet; | 126 | nextGet = pring->nextGet; |
280 | @@ -131,8 +133,9 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( | |||
281 | 131 | if (pring->lock) epicsSpinUnlock(pring->lock); | 133 | if (pring->lock) epicsSpinUnlock(pring->lock); |
282 | 132 | return 0; | 134 | return 0; |
283 | 133 | } | 135 | } |
285 | 134 | if (nbytes) | 136 | if (nbytes) { |
286 | 135 | memcpy ((void *)&pring->buffer[nextPut], value, nbytes); | 137 | memcpy ((void *)&pring->buffer[nextPut], value, nbytes); |
287 | 138 | } | ||
288 | 136 | nextPut += nbytes; | 139 | nextPut += nbytes; |
289 | 137 | } | 140 | } |
290 | 138 | else { | 141 | else { |
291 | @@ -143,8 +146,9 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( | |||
292 | 143 | } | 146 | } |
293 | 144 | topCount = size - nextPut; | 147 | topCount = size - nextPut; |
294 | 145 | copyCount = (nbytes > topCount) ? topCount : nbytes; | 148 | copyCount = (nbytes > topCount) ? topCount : nbytes; |
296 | 146 | if (copyCount) | 149 | if (copyCount) { |
297 | 147 | memcpy ((void *)&pring->buffer[nextPut], value, copyCount); | 150 | memcpy ((void *)&pring->buffer[nextPut], value, copyCount); |
298 | 151 | } | ||
299 | 148 | nextPut += copyCount; | 152 | nextPut += copyCount; |
300 | 149 | if (nextPut == size) { | 153 | if (nextPut == size) { |
301 | 150 | int nLeft = nbytes - copyCount; | 154 | int nLeft = nbytes - copyCount; |
302 | @@ -155,6 +159,10 @@ epicsShareFunc int epicsShareAPI epicsRingBytesPut( | |||
303 | 155 | } | 159 | } |
304 | 156 | pring->nextPut = nextPut; | 160 | pring->nextPut = nextPut; |
305 | 157 | 161 | ||
306 | 162 | used = nextPut - nextGet; | ||
307 | 163 | if (used < 0) used += pring->size; | ||
308 | 164 | if (used > pring->highWaterMark) pring->highWaterMark = used; | ||
309 | 165 | |||
310 | 158 | if (pring->lock) epicsSpinUnlock(pring->lock); | 166 | if (pring->lock) epicsSpinUnlock(pring->lock); |
311 | 159 | return nbytes; | 167 | return nbytes; |
312 | 160 | } | 168 | } |
313 | @@ -224,3 +232,20 @@ epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id) | |||
314 | 224 | { | 232 | { |
315 | 225 | return (epicsRingBytesFreeBytes(id) <= 0); | 233 | return (epicsRingBytesFreeBytes(id) <= 0); |
316 | 226 | } | 234 | } |
317 | 235 | |||
318 | 236 | epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id) | ||
319 | 237 | { | ||
320 | 238 | ringPvt *pring = (ringPvt *)id; | ||
321 | 239 | return pring->highWaterMark; | ||
322 | 240 | } | ||
323 | 241 | |||
324 | 242 | epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id) | ||
325 | 243 | { | ||
326 | 244 | ringPvt *pring = (ringPvt *)id; | ||
327 | 245 | int used; | ||
328 | 246 | if (pring->lock) epicsSpinLock(pring->lock); | ||
329 | 247 | used = pring->nextGet - pring->nextPut; | ||
330 | 248 | if (used < 0) used += pring->size; | ||
331 | 249 | pring->highWaterMark = used; | ||
332 | 250 | if (pring->lock) epicsSpinUnlock(pring->lock); | ||
333 | 251 | } | ||
334 | diff --git a/src/libCom/ring/epicsRingBytes.h b/src/libCom/ring/epicsRingBytes.h | |||
335 | index 011829b..3dc0081 100644 | |||
336 | --- a/src/libCom/ring/epicsRingBytes.h | |||
337 | +++ b/src/libCom/ring/epicsRingBytes.h | |||
338 | @@ -24,6 +24,7 @@ extern "C" { | |||
339 | 24 | #include "shareLib.h" | 24 | #include "shareLib.h" |
340 | 25 | 25 | ||
341 | 26 | typedef void *epicsRingBytesId; | 26 | typedef void *epicsRingBytesId; |
342 | 27 | typedef void const *epicsRingBytesIdConst; | ||
343 | 27 | 28 | ||
344 | 28 | epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes); | 29 | epicsShareFunc epicsRingBytesId epicsShareAPI epicsRingBytesCreate(int nbytes); |
345 | 29 | /* Same, but secured by a spinlock */ | 30 | /* Same, but secured by a spinlock */ |
346 | @@ -39,6 +40,8 @@ epicsShareFunc int epicsShareAPI epicsRingBytesUsedBytes(epicsRingBytesId id); | |||
347 | 39 | epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id); | 40 | epicsShareFunc int epicsShareAPI epicsRingBytesSize(epicsRingBytesId id); |
348 | 40 | epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id); | 41 | epicsShareFunc int epicsShareAPI epicsRingBytesIsEmpty(epicsRingBytesId id); |
349 | 41 | epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id); | 42 | epicsShareFunc int epicsShareAPI epicsRingBytesIsFull(epicsRingBytesId id); |
350 | 43 | epicsShareFunc int epicsShareAPI epicsRingBytesHighWaterMark(epicsRingBytesIdConst id); | ||
351 | 44 | epicsShareFunc void epicsShareAPI epicsRingBytesResetHighWaterMark(epicsRingBytesId id); | ||
352 | 42 | 45 | ||
353 | 43 | #ifdef __cplusplus | 46 | #ifdef __cplusplus |
354 | 44 | } | 47 | } |
355 | diff --git a/src/libCom/ring/epicsRingPointer.cpp b/src/libCom/ring/epicsRingPointer.cpp | |||
356 | index 9c144ce..709ab65 100644 | |||
357 | --- a/src/libCom/ring/epicsRingPointer.cpp | |||
358 | +++ b/src/libCom/ring/epicsRingPointer.cpp | |||
359 | @@ -90,3 +90,15 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id) | |||
360 | 90 | voidPointer *pvoidPointer = reinterpret_cast<voidPointer*>(id); | 90 | voidPointer *pvoidPointer = reinterpret_cast<voidPointer*>(id); |
361 | 91 | return((pvoidPointer->isFull()) ? 1 : 0); | 91 | return((pvoidPointer->isFull()) ? 1 : 0); |
362 | 92 | } | 92 | } |
363 | 93 | |||
364 | 94 | epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id) | ||
365 | 95 | { | ||
366 | 96 | voidPointer const *pvoidPointer = reinterpret_cast<voidPointer const*>(id); | ||
367 | 97 | return(pvoidPointer->getHighWaterMark()); | ||
368 | 98 | } | ||
369 | 99 | |||
370 | 100 | epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id) | ||
371 | 101 | { | ||
372 | 102 | voidPointer *pvoidPointer = reinterpret_cast<voidPointer*>(id); | ||
373 | 103 | pvoidPointer->resetHighWaterMark(); | ||
374 | 104 | } | ||
375 | diff --git a/src/libCom/ring/epicsRingPointer.h b/src/libCom/ring/epicsRingPointer.h | |||
376 | index 48d6203..68bf8f5 100644 | |||
377 | --- a/src/libCom/ring/epicsRingPointer.h | |||
378 | +++ b/src/libCom/ring/epicsRingPointer.h | |||
379 | @@ -40,18 +40,22 @@ public: /* Functions */ | |||
380 | 40 | int getSize() const; | 40 | int getSize() const; |
381 | 41 | bool isEmpty() const; | 41 | bool isEmpty() const; |
382 | 42 | bool isFull() const; | 42 | bool isFull() const; |
383 | 43 | int getHighWaterMark() const; | ||
384 | 44 | void resetHighWaterMark(); | ||
385 | 43 | 45 | ||
386 | 44 | private: /* Prevent compiler-generated member functions */ | 46 | private: /* Prevent compiler-generated member functions */ |
387 | 45 | /* default constructor, copy constructor, assignment operator */ | 47 | /* default constructor, copy constructor, assignment operator */ |
388 | 46 | epicsRingPointer(); | 48 | epicsRingPointer(); |
389 | 47 | epicsRingPointer(const epicsRingPointer &); | 49 | epicsRingPointer(const epicsRingPointer &); |
390 | 48 | epicsRingPointer& operator=(const epicsRingPointer &); | 50 | epicsRingPointer& operator=(const epicsRingPointer &); |
391 | 51 | int getUsedNoLock() const; | ||
392 | 49 | 52 | ||
393 | 50 | private: /* Data */ | 53 | private: /* Data */ |
394 | 51 | epicsSpinId lock; | 54 | epicsSpinId lock; |
395 | 52 | volatile int nextPush; | 55 | volatile int nextPush; |
396 | 53 | volatile int nextPop; | 56 | volatile int nextPop; |
397 | 54 | int size; | 57 | int size; |
398 | 58 | int highWaterMark; | ||
399 | 55 | T * volatile * buffer; | 59 | T * volatile * buffer; |
400 | 56 | }; | 60 | }; |
401 | 57 | 61 | ||
402 | @@ -59,6 +63,7 @@ extern "C" { | |||
403 | 59 | #endif /*__cplusplus */ | 63 | #endif /*__cplusplus */ |
404 | 60 | 64 | ||
405 | 61 | typedef void *epicsRingPointerId; | 65 | typedef void *epicsRingPointerId; |
406 | 66 | typedef void const *epicsRingPointerIdConst; | ||
407 | 62 | 67 | ||
408 | 63 | epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size); | 68 | epicsShareFunc epicsRingPointerId epicsShareAPI epicsRingPointerCreate(int size); |
409 | 64 | /* Same, but secured by a spinlock */ | 69 | /* Same, but secured by a spinlock */ |
410 | @@ -74,6 +79,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerGetUsed(epicsRingPointerId id) | |||
411 | 74 | epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id); | 79 | epicsShareFunc int epicsShareAPI epicsRingPointerGetSize(epicsRingPointerId id); |
412 | 75 | epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id); | 80 | epicsShareFunc int epicsShareAPI epicsRingPointerIsEmpty(epicsRingPointerId id); |
413 | 76 | epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); | 81 | epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); |
414 | 82 | epicsShareFunc int epicsShareAPI epicsRingPointerGetHighWaterMark(epicsRingPointerIdConst id); | ||
415 | 83 | epicsShareFunc void epicsShareAPI epicsRingPointerResetHighWaterMark(epicsRingPointerId id); | ||
416 | 77 | 84 | ||
417 | 78 | /* This routine was incorrectly named in previous releases */ | 85 | /* This routine was incorrectly named in previous releases */ |
418 | 79 | #define epicsRingPointerSize epicsRingPointerGetSize | 86 | #define epicsRingPointerSize epicsRingPointerGetSize |
419 | @@ -95,7 +102,8 @@ epicsShareFunc int epicsShareAPI epicsRingPointerIsFull(epicsRingPointerId id); | |||
420 | 95 | 102 | ||
421 | 96 | template <class T> | 103 | template <class T> |
422 | 97 | inline epicsRingPointer<T>::epicsRingPointer(int sz, bool locked) : | 104 | inline epicsRingPointer<T>::epicsRingPointer(int sz, bool locked) : |
424 | 98 | lock(0), nextPush(0), nextPop(0), size(sz+1), buffer(new T* [sz+1]) | 105 | lock(0), nextPush(0), nextPop(0), size(sz+1), highWaterMark(0), |
425 | 106 | buffer(new T* [sz+1]) | ||
426 | 99 | { | 107 | { |
427 | 100 | if (locked) | 108 | if (locked) |
428 | 101 | lock = epicsSpinCreate(); | 109 | lock = epicsSpinCreate(); |
429 | @@ -121,6 +129,8 @@ inline bool epicsRingPointer<T>::push(T *p) | |||
430 | 121 | } | 129 | } |
431 | 122 | buffer[next] = p; | 130 | buffer[next] = p; |
432 | 123 | nextPush = newNext; | 131 | nextPush = newNext; |
433 | 132 | int used = getUsedNoLock(); | ||
434 | 133 | if (used > highWaterMark) highWaterMark = used; | ||
435 | 124 | if (lock) epicsSpinUnlock(lock); | 134 | if (lock) epicsSpinUnlock(lock); |
436 | 125 | return(true); | 135 | return(true); |
437 | 126 | } | 136 | } |
438 | @@ -162,11 +172,18 @@ inline int epicsRingPointer<T>::getFree() const | |||
439 | 162 | } | 172 | } |
440 | 163 | 173 | ||
441 | 164 | template <class T> | 174 | template <class T> |
443 | 165 | inline int epicsRingPointer<T>::getUsed() const | 175 | inline int epicsRingPointer<T>::getUsedNoLock() const |
444 | 166 | { | 176 | { |
445 | 167 | if (lock) epicsSpinLock(lock); | ||
446 | 168 | int n = nextPush - nextPop; | 177 | int n = nextPush - nextPop; |
447 | 169 | if (n < 0) n += size; | 178 | if (n < 0) n += size; |
448 | 179 | return n; | ||
449 | 180 | } | ||
450 | 181 | |||
451 | 182 | template <class T> | ||
452 | 183 | inline int epicsRingPointer<T>::getUsed() const | ||
453 | 184 | { | ||
454 | 185 | if (lock) epicsSpinLock(lock); | ||
455 | 186 | int n = getUsedNoLock(); | ||
456 | 170 | if (lock) epicsSpinUnlock(lock); | 187 | if (lock) epicsSpinUnlock(lock); |
457 | 171 | return n; | 188 | return n; |
458 | 172 | } | 189 | } |
459 | @@ -196,6 +213,20 @@ inline bool epicsRingPointer<T>::isFull() const | |||
460 | 196 | return((count == 0) || (count == size)); | 213 | return((count == 0) || (count == size)); |
461 | 197 | } | 214 | } |
462 | 198 | 215 | ||
463 | 216 | template <class T> | ||
464 | 217 | inline int epicsRingPointer<T>::getHighWaterMark() const | ||
465 | 218 | { | ||
466 | 219 | return highWaterMark; | ||
467 | 220 | } | ||
468 | 221 | |||
469 | 222 | template <class T> | ||
470 | 223 | inline void epicsRingPointer<T>::resetHighWaterMark() | ||
471 | 224 | { | ||
472 | 225 | if (lock) epicsSpinLock(lock); | ||
473 | 226 | highWaterMark = getUsedNoLock(); | ||
474 | 227 | if (lock) epicsSpinUnlock(lock); | ||
475 | 228 | } | ||
476 | 229 | |||
477 | 199 | #endif /* __cplusplus */ | 230 | #endif /* __cplusplus */ |
478 | 200 | 231 | ||
479 | 201 | #endif /* INCepicsRingPointerh */ | 232 | #endif /* INCepicsRingPointerh */ |
480 | diff --git a/src/libCom/test/ringBytesTest.c b/src/libCom/test/ringBytesTest.c | |||
481 | index 6cef933..bb91d02 100644 | |||
482 | --- a/src/libCom/test/ringBytesTest.c | |||
483 | +++ b/src/libCom/test/ringBytesTest.c | |||
484 | @@ -30,7 +30,8 @@ typedef struct info { | |||
485 | 30 | epicsRingBytesId ring; | 30 | epicsRingBytesId ring; |
486 | 31 | }info; | 31 | }info; |
487 | 32 | 32 | ||
489 | 33 | static void check(epicsRingBytesId ring, int expectedFree) | 33 | static void check(epicsRingBytesId ring, int expectedFree, |
490 | 34 | int expectedHighWaterMark) | ||
491 | 34 | { | 35 | { |
492 | 35 | int expectedUsed = RINGSIZE - expectedFree; | 36 | int expectedUsed = RINGSIZE - expectedFree; |
493 | 36 | int expectedEmpty = (expectedUsed == 0); | 37 | int expectedEmpty = (expectedUsed == 0); |
494 | @@ -39,11 +40,14 @@ static void check(epicsRingBytesId ring, int expectedFree) | |||
495 | 39 | int nUsed = epicsRingBytesUsedBytes(ring); | 40 | int nUsed = epicsRingBytesUsedBytes(ring); |
496 | 40 | int isEmpty = epicsRingBytesIsEmpty(ring); | 41 | int isEmpty = epicsRingBytesIsEmpty(ring); |
497 | 41 | int isFull = epicsRingBytesIsFull(ring); | 42 | int isFull = epicsRingBytesIsFull(ring); |
498 | 43 | int highWaterMark = epicsRingBytesHighWaterMark(ring); | ||
499 | 42 | 44 | ||
500 | 43 | testOk(nFree == expectedFree, "Free: %d == %d", nFree, expectedFree); | 45 | testOk(nFree == expectedFree, "Free: %d == %d", nFree, expectedFree); |
501 | 44 | testOk(nUsed == expectedUsed, "Used: %d == %d", nUsed, expectedUsed); | 46 | testOk(nUsed == expectedUsed, "Used: %d == %d", nUsed, expectedUsed); |
502 | 45 | testOk(isEmpty == expectedEmpty, "Empty: %d == %d", isEmpty, expectedEmpty); | 47 | testOk(isEmpty == expectedEmpty, "Empty: %d == %d", isEmpty, expectedEmpty); |
503 | 46 | testOk(isFull == expectedFull, "Full: %d == %d", isFull, expectedFull); | 48 | testOk(isFull == expectedFull, "Full: %d == %d", isFull, expectedFull); |
504 | 49 | testOk(highWaterMark == expectedHighWaterMark, "HighWaterMark: %d == %d", | ||
505 | 50 | highWaterMark, expectedHighWaterMark); | ||
506 | 47 | } | 51 | } |
507 | 48 | 52 | ||
508 | 49 | MAIN(ringBytesTest) | 53 | MAIN(ringBytesTest) |
509 | @@ -55,7 +59,7 @@ MAIN(ringBytesTest) | |||
510 | 55 | char get[RINGSIZE+1]; | 59 | char get[RINGSIZE+1]; |
511 | 56 | epicsRingBytesId ring; | 60 | epicsRingBytesId ring; |
512 | 57 | 61 | ||
514 | 58 | testPlan(245); | 62 | testPlan(292); |
515 | 59 | 63 | ||
516 | 60 | pinfo = calloc(1,sizeof(info)); | 64 | pinfo = calloc(1,sizeof(info)); |
517 | 61 | if (!pinfo) { | 65 | if (!pinfo) { |
518 | @@ -70,50 +74,54 @@ MAIN(ringBytesTest) | |||
519 | 70 | if (!ring) { | 74 | if (!ring) { |
520 | 71 | testAbort("epicsRingBytesCreate failed"); | 75 | testAbort("epicsRingBytesCreate failed"); |
521 | 72 | } | 76 | } |
523 | 73 | check(ring, RINGSIZE); | 77 | check(ring, RINGSIZE, 0); |
524 | 74 | 78 | ||
525 | 75 | for (i = 0 ; i < sizeof(put) ; i++) | 79 | for (i = 0 ; i < sizeof(put) ; i++) |
526 | 76 | put[i] = i; | 80 | put[i] = i; |
527 | 77 | for(i = 0 ; i < RINGSIZE ; i++) { | 81 | for(i = 0 ; i < RINGSIZE ; i++) { |
528 | 78 | n = epicsRingBytesPut(ring, put, i); | 82 | n = epicsRingBytesPut(ring, put, i); |
529 | 79 | testOk(n==i, "ring put %d", i); | 83 | testOk(n==i, "ring put %d", i); |
531 | 80 | check(ring, RINGSIZE-i); | 84 | check(ring, RINGSIZE-i, i); |
532 | 81 | n = epicsRingBytesGet(ring, get, i); | 85 | n = epicsRingBytesGet(ring, get, i); |
533 | 82 | testOk(n==i, "ring get %d", i); | 86 | testOk(n==i, "ring get %d", i); |
535 | 83 | check(ring, RINGSIZE); | 87 | check(ring, RINGSIZE, i); |
536 | 84 | testOk(memcmp(put,get,i)==0, "get matches write"); | 88 | testOk(memcmp(put,get,i)==0, "get matches write"); |
537 | 85 | } | 89 | } |
538 | 86 | 90 | ||
539 | 91 | epicsRingBytesResetHighWaterMark(ring); | ||
540 | 92 | |||
541 | 87 | for(i = 0 ; i < RINGSIZE ; i++) { | 93 | for(i = 0 ; i < RINGSIZE ; i++) { |
542 | 88 | n = epicsRingBytesPut(ring, put+i, 1); | 94 | n = epicsRingBytesPut(ring, put+i, 1); |
543 | 89 | testOk(n==1, "ring put 1, %d", i); | 95 | testOk(n==1, "ring put 1, %d", i); |
545 | 90 | check(ring, RINGSIZE-1-i); | 96 | check(ring, RINGSIZE-1-i, i + 1); |
546 | 91 | } | 97 | } |
547 | 92 | n = epicsRingBytesPut(ring, put+RINGSIZE, 1); | 98 | n = epicsRingBytesPut(ring, put+RINGSIZE, 1); |
548 | 93 | testOk(n==0, "put to full ring"); | 99 | testOk(n==0, "put to full ring"); |
550 | 94 | check(ring, 0); | 100 | check(ring, 0, RINGSIZE); |
551 | 95 | for(i = 0 ; i < RINGSIZE ; i++) { | 101 | for(i = 0 ; i < RINGSIZE ; i++) { |
552 | 96 | n = epicsRingBytesGet(ring, get+i, 1); | 102 | n = epicsRingBytesGet(ring, get+i, 1); |
553 | 97 | testOk(n==1, "ring get 1, %d", i); | 103 | testOk(n==1, "ring get 1, %d", i); |
555 | 98 | check(ring, 1+i); | 104 | check(ring, 1+i, RINGSIZE); |
556 | 99 | } | 105 | } |
557 | 100 | testOk(memcmp(put,get,RINGSIZE)==0, "get matches write"); | 106 | testOk(memcmp(put,get,RINGSIZE)==0, "get matches write"); |
558 | 101 | n = epicsRingBytesGet(ring, get+RINGSIZE, 1); | 107 | n = epicsRingBytesGet(ring, get+RINGSIZE, 1); |
559 | 102 | testOk(n==0, "get from empty ring"); | 108 | testOk(n==0, "get from empty ring"); |
561 | 103 | check(ring, RINGSIZE); | 109 | check(ring, RINGSIZE, RINGSIZE); |
562 | 110 | |||
563 | 111 | epicsRingBytesResetHighWaterMark(ring); | ||
564 | 104 | 112 | ||
565 | 105 | n = epicsRingBytesPut(ring, put, RINGSIZE+1); | 113 | n = epicsRingBytesPut(ring, put, RINGSIZE+1); |
566 | 106 | testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); | 114 | testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); |
568 | 107 | check(ring, RINGSIZE); | 115 | check(ring, RINGSIZE, 0); |
569 | 108 | n = epicsRingBytesPut(ring, put, 1); | 116 | n = epicsRingBytesPut(ring, put, 1); |
570 | 109 | testOk(n==1, "ring put %d", 1); | 117 | testOk(n==1, "ring put %d", 1); |
572 | 110 | check(ring, RINGSIZE-1); | 118 | check(ring, RINGSIZE-1, 1); |
573 | 111 | n = epicsRingBytesPut(ring, put, RINGSIZE); | 119 | n = epicsRingBytesPut(ring, put, RINGSIZE); |
574 | 112 | testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); | 120 | testOk(n==0, "ring put beyond ring capacity (%d, expected 0)",n); |
576 | 113 | check(ring, RINGSIZE-1); | 121 | check(ring, RINGSIZE-1, 1); |
577 | 114 | n = epicsRingBytesGet(ring, get, 1); | 122 | n = epicsRingBytesGet(ring, get, 1); |
578 | 115 | testOk(n==1, "ring get %d", 1); | 123 | testOk(n==1, "ring get %d", 1); |
580 | 116 | check(ring, RINGSIZE); | 124 | check(ring, RINGSIZE, 1); |
581 | 117 | 125 | ||
582 | 118 | epicsRingBytesDelete(ring); | 126 | epicsRingBytesDelete(ring); |
583 | 119 | epicsEventDestroy(consumerEvent); | 127 | epicsEventDestroy(consumerEvent); |
584 | diff --git a/src/libCom/test/ringPointerTest.c b/src/libCom/test/ringPointerTest.c | |||
585 | index 65a3494..d351708 100644 | |||
586 | --- a/src/libCom/test/ringPointerTest.c | |||
587 | +++ b/src/libCom/test/ringPointerTest.c | |||
588 | @@ -64,6 +64,7 @@ static void testSingle(void) | |||
589 | 64 | testOk1(epicsRingPointerGetFree(ring)==rsize); | 64 | testOk1(epicsRingPointerGetFree(ring)==rsize); |
590 | 65 | testOk1(epicsRingPointerGetSize(ring)==rsize); | 65 | testOk1(epicsRingPointerGetSize(ring)==rsize); |
591 | 66 | testOk1(epicsRingPointerGetUsed(ring)==0); | 66 | testOk1(epicsRingPointerGetUsed(ring)==0); |
592 | 67 | testOk1(epicsRingPointerGetHighWaterMark(ring)==0); | ||
593 | 67 | 68 | ||
594 | 68 | testOk1(epicsRingPointerPop(ring)==NULL); | 69 | testOk1(epicsRingPointerPop(ring)==NULL); |
595 | 69 | 70 | ||
596 | @@ -75,6 +76,10 @@ static void testSingle(void) | |||
597 | 75 | testOk1(epicsRingPointerGetFree(ring)==rsize-1); | 76 | testOk1(epicsRingPointerGetFree(ring)==rsize-1); |
598 | 76 | testOk1(epicsRingPointerGetSize(ring)==rsize); | 77 | testOk1(epicsRingPointerGetSize(ring)==rsize); |
599 | 77 | testOk1(epicsRingPointerGetUsed(ring)==1); | 78 | testOk1(epicsRingPointerGetUsed(ring)==1); |
600 | 79 | testOk1(epicsRingPointerGetHighWaterMark(ring)==1); | ||
601 | 80 | |||
602 | 81 | epicsRingPointerResetHighWaterMark(ring); | ||
603 | 82 | testOk1(epicsRingPointerGetHighWaterMark(ring)==1); | ||
604 | 78 | 83 | ||
605 | 79 | testDiag("Fill it up"); | 84 | testDiag("Fill it up"); |
606 | 80 | for(i=2; i<2*rsize; i++) { | 85 | for(i=2; i<2*rsize; i++) { |
607 | @@ -92,6 +97,7 @@ static void testSingle(void) | |||
608 | 92 | testOk1(epicsRingPointerGetFree(ring)==0); | 97 | testOk1(epicsRingPointerGetFree(ring)==0); |
609 | 93 | testOk1(epicsRingPointerGetSize(ring)==rsize); | 98 | testOk1(epicsRingPointerGetSize(ring)==rsize); |
610 | 94 | testOk1(epicsRingPointerGetUsed(ring)==rsize); | 99 | testOk1(epicsRingPointerGetUsed(ring)==rsize); |
611 | 100 | testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); | ||
612 | 95 | 101 | ||
613 | 96 | testDiag("Drain it out"); | 102 | testDiag("Drain it out"); |
614 | 97 | for(i=1; i<2*rsize; i++) { | 103 | for(i=1; i<2*rsize; i++) { |
615 | @@ -108,6 +114,7 @@ static void testSingle(void) | |||
616 | 108 | testOk1(epicsRingPointerGetFree(ring)==rsize); | 114 | testOk1(epicsRingPointerGetFree(ring)==rsize); |
617 | 109 | testOk1(epicsRingPointerGetSize(ring)==rsize); | 115 | testOk1(epicsRingPointerGetSize(ring)==rsize); |
618 | 110 | testOk1(epicsRingPointerGetUsed(ring)==0); | 116 | testOk1(epicsRingPointerGetUsed(ring)==0); |
619 | 117 | testOk1(epicsRingPointerGetHighWaterMark(ring)==rsize); | ||
620 | 111 | 118 | ||
621 | 112 | testDiag("Fill it up again"); | 119 | testDiag("Fill it up again"); |
622 | 113 | for(i=2; i<2*rsize; i++) { | 120 | for(i=2; i<2*rsize; i++) { |
623 | @@ -236,7 +243,7 @@ MAIN(ringPointerTest) | |||
624 | 236 | { | 243 | { |
625 | 237 | int prio = epicsThreadGetPrioritySelf(); | 244 | int prio = epicsThreadGetPrioritySelf(); |
626 | 238 | 245 | ||
628 | 239 | testPlan(37); | 246 | testPlan(42); |
629 | 240 | testSingle(); | 247 | testSingle(); |
630 | 241 | if (prio) | 248 | if (prio) |
631 | 242 | epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityScanLow); | 249 | epicsThreadSetPriority(epicsThreadGetIdSelf(), epicsThreadPriorityScanLow); |
I like the idea of adding queue health status.
Number of overruns is good and useful data.
Given the common usage pattern of those queues (a large number of entries is put on the queue by a driver or at IOC init for PINI processing, then a number of threads are working on taking items off) I think that an iocStats or manual check of the current usage does not tell much as it most probably misses the moment of high usage.
A simple high water mark mechanism might generate data that is a lot more useful.
I would also consider resetting the stats at every read of their values for easy one-record reporting by iocStats. (Cumulative values are not nice in the archiver.)