Merge ~johill-lanl/epics-base/+git/epics-base:timer-queue-fix into epics-base:7.0
- Git
- lp:~johill-lanl/epics-base/+git/epics-base
- timer-queue-fix
- Merge into 7.0
Status: | Needs review |
---|---|
Proposed branch: | ~johill-lanl/epics-base/+git/epics-base:timer-queue-fix |
Merge into: | epics-base:7.0 |
Diff against target: |
7004 lines (+3832/-1151) (has conflicts) 47 files modified
configure/CONFIG.gnuCommon (+2/-2) dev/null (+0/-1) modules/libcom/src/Makefile (+2/-0) modules/libcom/src/misc/AllocatorArena.h (+944/-0) modules/libcom/src/misc/AllocatorArenaUntyped.cpp (+199/-0) modules/libcom/src/misc/Makefile (+3/-0) modules/libcom/src/osi/Makefile (+10/-0) modules/libcom/src/osi/compiler/clang/compilerSpecific.h (+6/-0) modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h (+2/-2) modules/libcom/src/osi/compiler/default/compilerDependentDemangle.cpp (+28/-0) modules/libcom/src/osi/compiler/default/compilerSpecific.h (+7/-2) modules/libcom/src/osi/compiler/default/epicsAtomicCD.h (+2/-2) modules/libcom/src/osi/compiler/default/epicsStaticInstanceCD.h (+20/-0) modules/libcom/src/osi/compiler/gcc/compilerDependentDemangle.cpp (+168/-0) modules/libcom/src/osi/compiler/gcc/compilerSpecific.h (+10/-0) modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h (+2/-2) modules/libcom/src/osi/compiler/gcc/epicsStaticInstanceCD.h (+26/-0) modules/libcom/src/osi/compiler/msvc/compilerSpecific.h (+6/-2) modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h (+2/-2) modules/libcom/src/osi/compiler/msvc/epicsStaticInstanceCD.h (+32/-0) modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h (+2/-2) modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h (+2/-2) modules/libcom/src/osi/compilerDependencies.h (+42/-0) modules/libcom/src/osi/epicsDemangle.h (+26/-0) modules/libcom/src/osi/epicsStaticInstance.h (+52/-0) modules/libcom/src/osi/epicsStaticInstanceSaneCmplr.h (+45/-0) modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.cpp (+88/-0) modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.h (+53/-0) modules/libcom/src/timer/epicsTimer.cpp (+107/-102) modules/libcom/src/timer/epicsTimer.h (+61/-34) modules/libcom/src/timer/timer.cpp (+160/-160) modules/libcom/src/timer/timerPrivate.h (+228/-134) modules/libcom/src/timer/timerQueue.cpp (+179/-131) modules/libcom/src/timer/timerQueueActive.cpp (+53/-95) modules/libcom/src/timer/timerQueueActiveMgr.cpp (+17/-17) modules/libcom/src/timer/timerQueuePassive.cpp (+21/-17) modules/libcom/src/valgrind/epicsMemChk.h (+42/-0) modules/libcom/src/valgrind/memcheck.h (+303/-0) modules/libcom/src/valgrind/valgrind.h (+324/-239) modules/libcom/test/AllocatorArenaTest.cpp (+98/-0) modules/libcom/test/Makefile (+17/-0) modules/libcom/test/epicsDemangleTest.cpp (+75/-0) modules/libcom/test/epicsStaticInstanceTest.cpp (+66/-0) modules/libcom/test/epicsTimerTest.cpp (+297/-200) modules/pvAccess (+1/-1) modules/pvDatabase (+1/-1) modules/pva2pva (+1/-1) Conflict in modules/libcom/test/Makefile |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
EPICS Core Developers | Pending | ||
Review via email:
|
Commit message
o changed timer library to use heap algorithm
o merged thread private C++ allocator - AllocatorArena
o merged compiler, and compiler version, independent thread safe static C++ object library
o merged compiler independent C++ symbol demangler
o added C++ 11 compatibility keywords to compiler dependent library
o upgraded and or added tests
o updated valgrind header versions to newer version, added missing valgrind header files
o added mechanism to turn off valgrind macros, which have runtime penalty in optimized builds
Description of the change
Changed timer queue library to use heap algorithm.
Also upgraded the timer test coverage, and accuracy statistics emitted as diagnostics during the tests.
In order to simplify the merge I moved the timer library to a modernized thread private C++ STL compliant allocation library - AllocatorArena. This avoids (personal) difficulties with maintaining two different versions {EPICS 7.0, LANSCE DAR} using the original cxx free list library interface, and an enhanced newer template interface version of that original library. If you prefer I could change the timer library to just use new and delete as the timer objects themselves may not be created and destroyed periodically by users (difficult to speculate). I could also rebase on the older version of the cxx free list library, but again I would have more maintenance labor to attend to over time.
The AllocatorArena also pulls in the (small) static instance library and the C++ demangle libraries. There are also some tests for these layered components.
I have also added some c++ 11 compatibility macros to the compiler dependencies library. I also removed the throw compatibility macro because no code uses it, and no one should be using the C++ throw specification on new code (according to C++ standards committee, Meyers, and many others).
Updated valgrind header versions to newer version, and added missing valgrind header files. Added mechanism to turn off valgrind macros, which have runtime penalty in optimized builds.
BTW: On a similar note, I would also prefer to see assert tests removed from optimized builds. Otherwise developers may leave out assert calls for fear of runtime penalty. I have not made that change here, however.

Jeff Hill (johill-lanl) wrote : | # |
Martin,
I will hopefully attend to to the build errors tomorrow. Suspect that MSVC will never work with valgrind? If so we can simply ifdef the valgrind macros out? Need to take closer look.
Also need to look at your performance metrics.
Jeff
_______
From: <email address hidden> <email address hidden> on behalf of Martin Konrad <email address hidden>
Sent: Friday, April 24, 2020 1:27 PM
To: Hill, Jeff
Subject: [EXTERNAL] Re: [Merge] ~johill-
I haven't looked at the code, yet, but it builds fine on Ubuntu 19.10 with GCC 9.2: no warnings, all tests pass :-) A few compiler/OS combinations fail to build on Travis (https:/
Before digging into the code, I benchmarked the old and the proposed timer queue implementation. You can find the results here:
https:/
The old implementation takes more than 100 us to create and start a new timer if there are already 30,000 timers in the queue (this number is based on a real-world scenario with Asyn/Stream at FRIB) and the new timer needs to be inserted at the beginning of the list (30,000 existing timers with 60 s, the new timer has 30 s duration). In this case the old code has to walk a linked list from the back all the way to the front (the code was optimized for inserting timers with the same duration). The new implementation performs two orders of magnitude better in this scenario. Not bad! None of the cases I benchmarked is performing significantly worse than with the old implementation. For comparison I also benchmarked boost::asio's timers - its performance is in the same order of magnitude.
See https:/
--
https:/
You are the owner of ~johill-

Martin Konrad (info-martin-konrad) wrote : | # |
I have pushed a few suggestions to https:/
- 9b266f7... by Jeff Hill
-
fixed bug occuring when timer restarted from within timer callback
- 1eee997... by Jeff Hill
-
merged from 7.0
- 85b70eb... by Jeff Hill
-
Merge branch 'timer-queue-fix' of git+ssh:
//git.launchpad .net/~johill- lanl/epics- base/+git/ epics-base into timer-queue-fix - 437efa7... by Jeff Hill
-
improved comments
- 854b08a... by Jeff Hill
-
test timer callback restarts timer path
- 78b49c4... by Jeff Hill
-
cosmetics

Jeff Hill (johill-lanl) wrote : | # |
Hi Martin,
Sorry about delayed response; I have been focusing on another project.
I had a quick look at your branch, and all of it looks in principal good. My spelling is unfortunately egregious and of course the doc is a needed upgrade. Some of the macros I use in other codes, to ease into C++ 11, but if some of them are already in 7.0 under different names then I will need to switch to those names of course. I don't recall seeing these duplications under different names; I will need to look closer.
The API on the gnu demangler function is unfortunately tricky to use, and I had to make some adjustments in the demangler support early on, but haven't touched that code for a year or so. I haven't looked at support for other compilers such as clang, but perhaps it (clang) will be compatible to gcc. As I recall the MSVC provides names from type_info::name already in human readable form.
I have another merge request in progress which actually uses some of the c++ 11 compatibility macros you are removing. They could be added back in for that proposal of course.
I could merge, probably all of, your changes into my branch. Is that how you want to proceed?
Jeff
_______
From: <email address hidden> <email address hidden> on behalf of Martin Konrad <email address hidden>
Sent: Monday, April 27, 2020 11:30 AM
To: Hill, Jeff
Subject: [EXTERNAL] Re: [Merge] ~johill-
I have pushed a few suggestions to https:/
--
https:/
You are the owner of ~johill-
Unmerged commits
- 78b49c4... by Jeff Hill
-
cosmetics
- 854b08a... by Jeff Hill
-
test timer callback restarts timer path
- 437efa7... by Jeff Hill
-
improved comments
- 85b70eb... by Jeff Hill
-
Merge branch 'timer-queue-fix' of git+ssh:
//git.launchpad .net/~johill- lanl/epics- base/+git/ epics-base into timer-queue-fix - 1eee997... by Jeff Hill
-
merged from 7.0
- 9b266f7... by Jeff Hill
-
fixed bug occuring when timer restarted from within timer callback
- c112d69... by Jeff Hill
-
Merge branch '7.0' into timer-queue-fix
- 099c0ea... by Jeff Hill
-
fixed spelling in comment
- eb4fa08... by Jeff Hill
-
o switched from epicsTime :: getCurrent to epicsTime :: getMonotonic as per EPICS 7 convention
Nevertheless, personally I have some concerns
-------------- ------- ------- ------- ------- --- I do understand that general time can cause time discontinuities, and that discontinuities are
bad for a timer queue. However, I also worry about this being a backward incompatible API change,
and about users subtracting monotonic epicsTime time from ordinary epicsTime to produce a garbage
difference amount. Perhaps a different user defined type should have been used for monotonic
time. Also, does monotonic epicsTime convert to a date the same as ordinary epicsTime? - 9dafde4... by Jeff Hill
-
resrve heap space during timer instantiation
Preview Diff
1 | diff --git a/.ci b/.ci | |||
2 | 0 | deleted file mode 160000 | 0 | deleted file mode 160000 |
3 | index e91a588..0000000 | |||
4 | --- a/.ci | |||
5 | +++ /dev/null | |||
6 | @@ -1 +0,0 @@ | |||
7 | 1 | Subproject commit e91a5883704e9fa57792953436eb7020baf37063 | ||
8 | diff --git a/configure/CONFIG.gnuCommon b/configure/CONFIG.gnuCommon | |||
9 | index c4fd8ce..96eab65 100644 | |||
10 | --- a/configure/CONFIG.gnuCommon | |||
11 | +++ b/configure/CONFIG.gnuCommon | |||
12 | @@ -35,7 +35,7 @@ CODE_CFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) | |||
13 | 35 | WARN_CFLAGS_YES = -Wall | 35 | WARN_CFLAGS_YES = -Wall |
14 | 36 | WARN_CFLAGS_NO = -w | 36 | WARN_CFLAGS_NO = -w |
15 | 37 | OPT_CFLAGS_YES = -O3 | 37 | OPT_CFLAGS_YES = -O3 |
17 | 38 | OPT_CFLAGS_NO = -g | 38 | OPT_CFLAGS_NO = -g -DMEMCHECK |
18 | 39 | 39 | ||
19 | 40 | PROF_CXXFLAGS_YES = -p | 40 | PROF_CXXFLAGS_YES = -p |
20 | 41 | GPROF_CXXFLAGS_YES = -pg | 41 | GPROF_CXXFLAGS_YES = -pg |
21 | @@ -44,7 +44,7 @@ CODE_CXXFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) | |||
22 | 44 | WARN_CXXFLAGS_YES = -Wall | 44 | WARN_CXXFLAGS_YES = -Wall |
23 | 45 | WARN_CXXFLAGS_NO = -w | 45 | WARN_CXXFLAGS_NO = -w |
24 | 46 | OPT_CXXFLAGS_YES = -O3 | 46 | OPT_CXXFLAGS_YES = -O3 |
26 | 47 | OPT_CXXFLAGS_NO = -g | 47 | OPT_CXXFLAGS_NO = -g -DMEMCHECK |
27 | 48 | 48 | ||
28 | 49 | CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) | 49 | CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) |
29 | 50 | CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN)) | 50 | CODE_LDFLAGS += $(ASAN_LDFLAGS_$(ENABLE_ASAN)) |
30 | diff --git a/modules/libcom/src/Makefile b/modules/libcom/src/Makefile | |||
31 | index 57533ba..c966e2c 100644 | |||
32 | --- a/modules/libcom/src/Makefile | |||
33 | +++ b/modules/libcom/src/Makefile | |||
34 | @@ -15,6 +15,8 @@ include $(TOP)/configure/CONFIG | |||
35 | 15 | #USR_CFLAGS += -DNVALGRIND | 15 | #USR_CFLAGS += -DNVALGRIND |
36 | 16 | 16 | ||
37 | 17 | INC += valgrind/valgrind.h | 17 | INC += valgrind/valgrind.h |
38 | 18 | INC += valgrind/memcheck.h | ||
39 | 19 | INC += valgrind/epicsMemChk.h | ||
40 | 18 | 20 | ||
41 | 19 | INC += libComVersion.h | 21 | INC += libComVersion.h |
42 | 20 | INC += libComVersionNum.h | 22 | INC += libComVersionNum.h |
43 | diff --git a/modules/libcom/src/misc/AllocatorArena.h b/modules/libcom/src/misc/AllocatorArena.h | |||
44 | 21 | new file mode 100644 | 23 | new file mode 100644 |
45 | index 0000000..33a46c5 | |||
46 | --- /dev/null | |||
47 | +++ b/modules/libcom/src/misc/AllocatorArena.h | |||
48 | @@ -0,0 +1,944 @@ | |||
49 | 1 | |||
50 | 2 | /*************************************************************************\ | ||
51 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
52 | 4 | * National Laboratory | ||
53 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
54 | 6 | * in file LICENSE that is included with this distribution. | ||
55 | 7 | \*************************************************************************/ | ||
56 | 8 | |||
57 | 9 | /* | ||
58 | 10 | * Author Jeffrey O. Hill | ||
59 | 11 | */ | ||
60 | 12 | |||
61 | 13 | // | ||
62 | 14 | // Arena Allocator | ||
63 | 15 | // | ||
64 | 16 | // A thread private allocator for N of type T allocated in-mass, and | ||
65 | 17 | // deallocated in-mass. The individual calls to allocate run very | ||
66 | 18 | // efficiently because they simply return a pointer to preallocated | ||
67 | 19 | // space for a type T, and advance the index identifying the space for | ||
68 | 20 | // the next allocation, using thread private storage sans mutex | ||
69 | 21 | // protection overhead. The in-bulk deallocation is postponed until | ||
70 | 22 | // the last active T, residing within a bulk allocated block, is | ||
71 | 23 | // deallocated. | ||
72 | 24 | // | ||
73 | 25 | // The allocator falls back to ordinary global new/delete if the user | ||
74 | 26 | // requests more than one element for allocation using the vector | ||
75 | 27 | // form of new. | ||
76 | 28 | // | ||
77 | 29 | // This facility is fully safe for use in a multi-threaded environment. | ||
78 | 30 | // A thread private variable, and a thread private cleanup, are used to | ||
79 | 31 | // maintain a thread private context so that the overhead associated | ||
80 | 32 | // with a mutual exclusion locking can be avoided. | ||
81 | 33 | // | ||
82 | 34 | // Be aware that the storage overhead for any type T is sizeof ( T ) plus | ||
83 | 35 | // sizeof ( void * ), a substantial consideration probably only if small | ||
84 | 36 | // objects are stored, and storage efficency is more important than | ||
85 | 37 | // performance considerations. Furthermore, storage overhead increases | ||
86 | 38 | // for C++ compilers predating C++ 11. | ||
87 | 39 | // | ||
88 | 40 | // In this code a contiguous bulk block of storage for N of type T | ||
89 | 41 | // is template type Rack<S,A,N> where S is the size of T, and A is the | ||
90 | 42 | // required alignment. The thread-private allocaton occurs when peeling | ||
91 | 43 | // off storage for an individual T from the thread's private Rack. When | ||
92 | 44 | // the thread's private Rack is exausted, then a new Rack is allocated | ||
93 | 45 | // from the specified Rack allocator type. Currently two global Rack | ||
94 | 46 | // allocator implementations are provided. One that uses ordinary global | ||
95 | 47 | // new/delete, and one that uses a mutex protected global free list | ||
96 | 48 | // (the default) designated by rack allocator policies rap_pool and | ||
97 | 49 | // rap_freeList respectively. | ||
98 | 50 | // | ||
99 | 51 | // Define ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE in order to | ||
100 | 52 | // temporarily bypass bulk allocation and return to ordinary global | ||
101 | 53 | // operator new and delete, so that memory access inspectors such as | ||
102 | 54 | // purify or valgrind might be more effective. Furthermore, when the | ||
103 | 55 | // allocator _is_ active, and it is a debug build, specialized | ||
104 | 56 | // valgrind macros document with valgrind memory regions owned by this | ||
105 | 57 | // allocater for which ownership has not yet passed to an end application | ||
106 | 58 | // so that they do not appear as undesireable noise in valgrind leak | ||
107 | 59 | // reports. | ||
108 | 60 | // | ||
109 | 61 | |||
110 | 62 | #ifndef epicsAllocatorArena_h | ||
111 | 63 | #define epicsAllocatorArena_h | ||
112 | 64 | |||
113 | 65 | // see comment above | ||
114 | 66 | #if 0 | ||
115 | 67 | #define ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE | ||
116 | 68 | #endif | ||
117 | 69 | |||
118 | 70 | #if __cplusplus >= 201103L | ||
119 | 71 | # include <cstdint> | ||
120 | 72 | #endif | ||
121 | 73 | |||
122 | 74 | #include <new> | ||
123 | 75 | #include <cstdlib> | ||
124 | 76 | #include <string> | ||
125 | 77 | #include <cstdio> | ||
126 | 78 | #include <typeinfo> | ||
127 | 79 | |||
128 | 80 | #include "epicsAtomic.h" | ||
129 | 81 | #include "epicsMutex.h" | ||
130 | 82 | #include "epicsThread.h" | ||
131 | 83 | #include "epicsStaticInstance.h" | ||
132 | 84 | #include "compilerDependencies.h" | ||
133 | 85 | #include "valgrind/epicsMemChk.h" | ||
134 | 86 | #include "shareLib.h" | ||
135 | 87 | |||
136 | 88 | |||
137 | 89 | #if __cplusplus >= 201103L | ||
138 | 90 | # if defined ( __GNUC__ ) | ||
139 | 91 | # define GCC_VERSION_AA \ | ||
140 | 92 | (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) | ||
141 | 93 | // perhaps the nios2 cross compiler doesnt properly implement | ||
142 | 94 | // alignment even at gcc 4.8, based on experimental evidence | ||
143 | 95 | # define ALIGNAS_CMPLR_OK ( GCC_VERSION_AA >= 50000 ) | ||
144 | 96 | # else | ||
145 | 97 | # define ALIGNAS_CMPLR_OK 1 | ||
146 | 98 | # endif | ||
147 | 99 | #else | ||
148 | 100 | # define ALIGNAS_CMPLR_OK 0 | ||
149 | 101 | #endif | ||
150 | 102 | |||
151 | 103 | #ifndef SIZE_MAX | ||
152 | 104 | # define SIZE_MAX ( static_cast < size_t > ( -1L ) ) | ||
153 | 105 | #endif | ||
154 | 106 | |||
155 | 107 | namespace epics { | ||
156 | 108 | namespace _impl { | ||
157 | 109 | |||
158 | 110 | using std :: size_t; | ||
159 | 111 | using std :: type_info; | ||
160 | 112 | using std :: ptrdiff_t; | ||
161 | 113 | using std :: allocator; | ||
162 | 114 | using std :: bad_alloc; | ||
163 | 115 | |||
164 | 116 | // | ||
165 | 117 | // S - the allocation size (the size of T) | ||
166 | 118 | // A - the required alignment | ||
167 | 119 | // N - the block size for pool allocation | ||
168 | 120 | // (pool allocation size will be approximately S * N) | ||
169 | 121 | // | ||
170 | 122 | template < size_t S, size_t A, size_t N = 256 > class Rack; | ||
171 | 123 | |||
172 | 124 | // | ||
173 | 125 | // R - the rack type | ||
174 | 126 | // P - the allocation policy type | ||
175 | 127 | // TRACE - if true, print a message each time that a new racks allocated | ||
176 | 128 | // counter power of two is exceeded for a particular allocation type | ||
177 | 129 | // | ||
178 | 130 | enum RackAllocPolicy { rap_pool, rap_freeList }; | ||
179 | 131 | template < typename R, RackAllocPolicy P, bool TRACE > class RackAlloc; | ||
180 | 132 | |||
181 | 133 | // | ||
182 | 134 | // T - the allocation type | ||
183 | 135 | // G - the discriminator for the thread private variable group | ||
184 | 136 | // (proper scaling requires an independent thread private variable | ||
185 | 137 | // group for each library) any type is allowed and appropriate for | ||
186 | 138 | // identifying an independent thread private context | ||
187 | 139 | // N - the block size for pool allocation | ||
188 | 140 | // A - the rack allocator | ||
189 | 141 | // | ||
190 | 142 | // on old compilers prior to ALIGNAS_CMPLR_OK all blocks are aligned | ||
191 | 143 | // for worst case alignment requirements | ||
192 | 144 | // | ||
193 | 145 | template < typename T, typename G, size_t N = 256, | ||
194 | 146 | RackAllocPolicy P = rap_freeList, bool TRACE = false > | ||
195 | 147 | class AllocatorArena { | ||
196 | 148 | public: | ||
197 | 149 | typedef T value_type; | ||
198 | 150 | typedef value_type * pointer; | ||
199 | 151 | typedef const value_type * const_pointer; | ||
200 | 152 | typedef value_type & reference; | ||
201 | 153 | typedef const value_type & const_reference; | ||
202 | 154 | typedef size_t size_type; | ||
203 | 155 | typedef ptrdiff_t difference_type; | ||
204 | 156 | typedef typename allocator < void > :: const_pointer void_const_pointer; | ||
205 | 157 | typedef typename allocator < void > :: pointer void_pointer; | ||
206 | 158 | |||
207 | 159 | template < typename T0 > | ||
208 | 160 | struct rebind { | ||
209 | 161 | typedef AllocatorArena < T0, G, N, P, TRACE > other; | ||
210 | 162 | }; | ||
211 | 163 | |||
212 | 164 | AllocatorArena (); | ||
213 | 165 | |||
214 | 166 | template < typename Y > | ||
215 | 167 | AllocatorArena ( const Y & ); | ||
216 | 168 | |||
217 | 169 | // address | ||
218 | 170 | static pointer address ( reference r ); | ||
219 | 171 | static const_pointer address ( const_reference r ); | ||
220 | 172 | |||
221 | 173 | // memory allocation | ||
222 | 174 | static pointer allocate ( size_type nAlloc, | ||
223 | 175 | void_const_pointer pNearbyHint = 0 ); | ||
224 | 176 | static void deallocate ( const pointer p, size_type nAlloc ); | ||
225 | 177 | |||
226 | 178 | // helpers, for creating class specific operator new and delete | ||
227 | 179 | static void * allocateOctets ( size_type size ); | ||
228 | 180 | static void * allocateOctets ( size_type size, const std :: nothrow_t & ); | ||
229 | 181 | static void deallocateOctets ( void * p, size_type size ); | ||
230 | 182 | |||
231 | 183 | // remove the allocator's thread private reference to any partially | ||
232 | 184 | // consumed block so that it can be released back into system pool | ||
233 | 185 | // (this action is automated at thread exit) | ||
234 | 186 | static void cleanup (); | ||
235 | 187 | |||
236 | 188 | // size | ||
237 | 189 | static size_type max_size (); | ||
238 | 190 | |||
239 | 191 | // construction / destruction of target | ||
240 | 192 | #if __cplusplus >= 201103L | ||
241 | 193 | template < typename T0, typename ... Args > | ||
242 | 194 | static void construct ( T0 *, Args && ... ); // depricated in c++ 17 | ||
243 | 195 | #else | ||
244 | 196 | template < typename T0, typename A0 > | ||
245 | 197 | static void construct ( T0 *, A0 ); | ||
246 | 198 | template < typename T0, typename A0, typename A1 > | ||
247 | 199 | static void construct ( T0 *, A0, A1 ); | ||
248 | 200 | template < typename T0, typename A0, typename A1, typename A2 > | ||
249 | 201 | static void construct ( T0 *, A0, A1, A2 ); | ||
250 | 202 | template < typename T0, typename A0, typename A1, typename A2, | ||
251 | 203 | typename A3 > | ||
252 | 204 | static void construct ( T0 *, A0, A1, A2, A3 ); | ||
253 | 205 | template < typename T0, typename A0, typename A1, typename A2, | ||
254 | 206 | typename A3, typename A4 > | ||
255 | 207 | static void construct ( T0 *, A0, A1, A2, A3, A4 ); | ||
256 | 208 | #endif | ||
257 | 209 | template < class U > | ||
258 | 210 | static void destroy ( U * ); // depricated in c++ 17 | ||
259 | 211 | |||
260 | 212 | bool operator == ( AllocatorArena const & ); | ||
261 | 213 | bool operator != ( AllocatorArena const & ); | ||
262 | 214 | |||
263 | 215 | static size_t rackCount (); | ||
264 | 216 | static size_t byteCount (); | ||
265 | 217 | private: | ||
266 | 218 | #if ALIGNAS_CMPLR_OK | ||
267 | 219 | typedef Rack < sizeof ( T ), alignof ( T ), N > M_Rack; | ||
268 | 220 | #else | ||
269 | 221 | /* worst case alignment is used */ | ||
270 | 222 | typedef Rack < sizeof ( T ), 0u, N > M_Rack; | ||
271 | 223 | #endif | ||
272 | 224 | typedef RackAlloc < M_Rack, P, TRACE > M_RackAlloc; | ||
273 | 225 | static pointer m_threadPrivateAlloc (); | ||
274 | 226 | static void m_rackCleanup ( void * ); | ||
275 | 227 | }; // end of class AllocatorArena | ||
276 | 228 | |||
277 | 229 | class epicsShareClass ThreadPrivateIdBadAlloc : public bad_alloc | ||
278 | 230 | { | ||
279 | 231 | const char * what () const throw (); | ||
280 | 232 | }; | ||
281 | 233 | |||
282 | 234 | class epicsShareClass AtThreadExitBadAlloc : public bad_alloc | ||
283 | 235 | { | ||
284 | 236 | const char * what () const throw (); | ||
285 | 237 | }; | ||
286 | 238 | |||
287 | 239 | #if __cplusplus >= 201103L | ||
288 | 240 | typedef std :: uint8_t Octet; | ||
289 | 241 | #else | ||
290 | 242 | typedef unsigned char Octet; | ||
291 | 243 | #endif | ||
292 | 244 | |||
293 | 245 | typedef Octet * POctet; | ||
294 | 246 | |||
295 | 247 | // align for all possible types, similar to malloc | ||
296 | 248 | union MaxAlign { | ||
297 | 249 | struct SomeStruct {}; | ||
298 | 250 | long double m_ld; | ||
299 | 251 | long long m_ll; | ||
300 | 252 | long double * m_pd; | ||
301 | 253 | SomeStruct * m_pss; | ||
302 | 254 | void (* m_pf) (); | ||
303 | 255 | long double SomeStruct :: * m_pmd; | ||
304 | 256 | void ( SomeStruct :: * m_pmf ) (); | ||
305 | 257 | /* | ||
306 | 258 | * eye of newt ... | ||
307 | 259 | */ | ||
308 | 260 | }; | ||
309 | 261 | |||
310 | 262 | template < size_t S, size_t A, size_t N > | ||
311 | 263 | class Rack { | ||
312 | 264 | public: | ||
313 | 265 | typedef size_t size_type; | ||
314 | 266 | typedef ptrdiff_t difference_type; | ||
315 | 267 | |||
316 | 268 | Rack (); | ||
317 | 269 | ~Rack (); | ||
318 | 270 | bool empty () const; | ||
319 | 271 | void * alloc (); | ||
320 | 272 | size_t removeReference (); | ||
321 | 273 | void addReference (); | ||
322 | 274 | |||
323 | 275 | static const size_t number; | ||
324 | 276 | static const size_t alignment; | ||
325 | 277 | static Rack * dealloc ( void * ); | ||
326 | 278 | private: | ||
327 | 279 | struct M_Wrapper { | ||
328 | 280 | VALGRIND_RED_ZONE ( m_redZoneBefore ) | ||
329 | 281 | # if ALIGNAS_CMPLR_OK | ||
330 | 282 | alignas ( A ) Octet m_bufForObj [ S ]; | ||
331 | 283 | # else | ||
332 | 284 | union { | ||
333 | 285 | Octet m_bufForObj [ S ]; | ||
334 | 286 | MaxAlign m_maxAlign; | ||
335 | 287 | }; | ||
336 | 288 | # endif | ||
337 | 289 | VALGRIND_RED_ZONE ( m_redZoneAfter ) | ||
338 | 290 | void * m_pRack; | ||
339 | 291 | }; | ||
340 | 292 | typedef M_Wrapper * PWrapper; | ||
341 | 293 | M_Wrapper m_wrapped[N]; | ||
342 | 294 | size_t m_nAlloc; | ||
343 | 295 | size_t m_refCount; | ||
344 | 296 | #if __cplusplus >= 201103L | ||
345 | 297 | Rack ( const Rack & ) = delete; | ||
346 | 298 | Rack & operator = ( const Rack & ) = delete; | ||
347 | 299 | #endif | ||
348 | 300 | }; | ||
349 | 301 | |||
350 | 302 | struct RackManager { | ||
351 | 303 | void * m_pRack; | ||
352 | 304 | void ( *m_pThreadExitFunc )( void * pRack ); | ||
353 | 305 | }; | ||
354 | 306 | |||
355 | 307 | class epicsShareClass AllocCtxCom { | ||
356 | 308 | public: | ||
357 | 309 | AllocCtxCom (); | ||
358 | 310 | RackManager * getRackHandlerPtr ( size_t idx ); | ||
359 | 311 | void cleanup ( size_t idx ); | ||
360 | 312 | size_t allocIdx (); | ||
361 | 313 | private: | ||
362 | 314 | epicsThreadPrivateId m_threadPrivateId; | ||
363 | 315 | size_t m_curIdx; | ||
364 | 316 | static void m_threadExitFunc ( void * const pPriv ); | ||
365 | 317 | static const size_t m_initialCapacity; | ||
366 | 318 | }; | ||
367 | 319 | |||
368 | 320 | // | ||
369 | 321 | // G - the discriminator for the thread private variable group | ||
370 | 322 | // (proper scaling requires an indepent thread private variable | ||
371 | 323 | // group for each library) any type is allowed and appropriate | ||
372 | 324 | // for identifying an independent thread private context | ||
373 | 325 | // | ||
374 | 326 | template < typename G > class AllocCtxGrouped : public AllocCtxCom {}; | ||
375 | 327 | |||
376 | 328 | // | ||
377 | 329 | // G - the discriminator for the thread private variable group | ||
378 | 330 | // (proper scaling requires an indepent thread private variable | ||
379 | 331 | // group for each library) any type is allowed and appropriate | ||
380 | 332 | // for identifying an independent thread private context | ||
381 | 333 | // | ||
382 | 334 | template < typename G > | ||
383 | 335 | class AllocCtx { | ||
384 | 336 | public: | ||
385 | 337 | AllocCtx (); | ||
386 | 338 | void cleanup (); | ||
387 | 339 | RackManager * getRackHandlerPtr (); | ||
388 | 340 | private: | ||
389 | 341 | size_t m_idx; | ||
390 | 342 | }; | ||
391 | 343 | |||
392 | 344 | // | ||
393 | 345 | // T - the allocation type | ||
394 | 346 | // G - the discriminator for the thread private variable group | ||
395 | 347 | // (proper scaling requires an indepent thread private variable | ||
396 | 348 | // group for each library) any type is allowed and appropriate | ||
397 | 349 | // for identifying an independent thread private context | ||
398 | 350 | // | ||
399 | 351 | template < typename T, typename G > | ||
400 | 352 | class AllocCtxTyped : public AllocCtx < G > {}; | ||
401 | 353 | |||
402 | 354 | template < bool TRACE > | ||
403 | 355 | class epicsShareClass AllocCounter; | ||
404 | 356 | |||
405 | 357 | template <> | ||
406 | 358 | class epicsShareClass AllocCounter < true > { | ||
407 | 359 | public: | ||
408 | 360 | AllocCounter (); | ||
409 | 361 | void increment ( size_t nBytesThisTime, const type_info & ); | ||
410 | 362 | void decrement ( size_t nBytesThisTime ); | ||
411 | 363 | size_t rackCount () const { return m_nRacks; } | ||
412 | 364 | size_t byteCount () const { return m_bytes; } | ||
413 | 365 | void show ( const type_info & ) const; | ||
414 | 366 | private: | ||
415 | 367 | size_t m_nRacksTrace; | ||
416 | 368 | size_t m_nRacks; | ||
417 | 369 | size_t m_bytes; | ||
418 | 370 | }; | ||
419 | 371 | |||
420 | 372 | template <> | ||
421 | 373 | class epicsShareClass AllocCounter < false > { | ||
422 | 374 | public: | ||
423 | 375 | void increment ( size_t nBytesThisTime, | ||
424 | 376 | const type_info & ) {} | ||
425 | 377 | void decrement ( size_t nBytesThisTime ) {} | ||
426 | 378 | size_t rackCount () const { return 0u; } | ||
427 | 379 | size_t byteCount () const { return 0u; } | ||
428 | 380 | void show ( const char * pContext ) const {} | ||
429 | 381 | }; | ||
430 | 382 | |||
431 | 383 | template < typename R, bool TRACE > | ||
432 | 384 | class RackAlloc < R, rap_pool, TRACE > { | ||
433 | 385 | public: | ||
434 | 386 | typedef R Rack; | ||
435 | 387 | template < typename R0 > | ||
436 | 388 | struct rebind { | ||
437 | 389 | typedef RackAlloc < R0, rap_pool, TRACE > other; | ||
438 | 390 | }; | ||
439 | 391 | static R * create ( const type_info & ti ); | ||
440 | 392 | static void destroy ( R * p ); | ||
441 | 393 | static size_t rackCount (); | ||
442 | 394 | static size_t byteCount (); | ||
443 | 395 | private: | ||
444 | 396 | static AllocCounter < TRACE > m_counter; | ||
445 | 397 | }; | ||
446 | 398 | |||
447 | 399 | template < typename R, bool TRACE > | ||
448 | 400 | class RackAlloc < R, rap_freeList, TRACE > { | ||
449 | 401 | public: | ||
450 | 402 | template < typename R0 > | ||
451 | 403 | struct rebind { | ||
452 | 404 | typedef RackAlloc < R0, rap_freeList, TRACE > other; | ||
453 | 405 | }; | ||
454 | 406 | typedef R Rack; | ||
455 | 407 | RackAlloc (); | ||
456 | 408 | ~RackAlloc (); | ||
457 | 409 | R * create ( const type_info & ); | ||
458 | 410 | void destroy ( R * p ); | ||
459 | 411 | size_t rackCount () const; | ||
460 | 412 | size_t byteCount () const; | ||
461 | 413 | private: | ||
462 | 414 | typedef epicsMutex Mutex; | ||
463 | 415 | typedef epicsGuard < epicsMutex > Guard; | ||
464 | 416 | struct M_Alias { | ||
465 | 417 | VALGRIND_RED_ZONE ( m_redZoneBefore ) | ||
466 | 418 | union { | ||
467 | 419 | # if ALIGNAS_CMPLR_OK | ||
468 | 420 | alignas ( R :: alignment ) | ||
469 | 421 | Octet m_rackBuf [ sizeof ( R ) ]; | ||
470 | 422 | # else | ||
471 | 423 | union { | ||
472 | 424 | Octet m_rackBuf [ sizeof ( R ) ]; | ||
473 | 425 | MaxAlign m_maxAlign; | ||
474 | 426 | }; | ||
475 | 427 | # endif | ||
476 | 428 | struct M_Alias * m_pNext; | ||
477 | 429 | }; | ||
478 | 430 | VALGRIND_RED_ZONE ( m_redZoneAfter ) | ||
479 | 431 | }; | ||
480 | 432 | M_Alias * m_pAlias; | ||
481 | 433 | Mutex m_mutex; | ||
482 | 434 | AllocCounter < TRACE > m_counter; | ||
483 | 435 | RackAlloc ( const RackAlloc & ) epicsDeleteMethod; | ||
484 | 436 | RackAlloc operator = ( const RackAlloc & ) epicsDeleteMethod; | ||
485 | 437 | }; | ||
486 | 438 | |||
487 | 439 | template < typename G > | ||
488 | 440 | inline AllocCtx < G > :: AllocCtx () : | ||
489 | 441 | m_idx ( staticInstance < AllocCtxGrouped < G > > ().allocIdx () ) | ||
490 | 442 | { | ||
491 | 443 | } | ||
492 | 444 | |||
493 | 445 | template < typename G > | ||
494 | 446 | inline RackManager * AllocCtx < G > :: getRackHandlerPtr () | ||
495 | 447 | { | ||
496 | 448 | AllocCtxCom & grp = staticInstance < AllocCtxGrouped < G > > (); | ||
497 | 449 | return grp.getRackHandlerPtr ( m_idx ); | ||
498 | 450 | } | ||
499 | 451 | |||
500 | 452 | template < typename G > | ||
501 | 453 | void AllocCtx < G > :: cleanup () | ||
502 | 454 | { | ||
503 | 455 | AllocCtxCom & grp = staticInstance < AllocCtxGrouped < G > > (); | ||
504 | 456 | grp.cleanup ( m_idx ); | ||
505 | 457 | } | ||
506 | 458 | |||
507 | 459 | template < typename R, bool TRACE > | ||
508 | 460 | inline R * RackAlloc < R, rap_pool, TRACE > :: create ( const type_info & ti ) | ||
509 | 461 | { | ||
510 | 462 | R * const p = new R (); | ||
511 | 463 | m_counter.increment ( sizeof ( R ), ti ); | ||
512 | 464 | return p; | ||
513 | 465 | } | ||
514 | 466 | |||
515 | 467 | template < typename R, bool TRACE > | ||
516 | 468 | inline void RackAlloc < R, rap_pool, TRACE > :: destroy ( R * p ) | ||
517 | 469 | { | ||
518 | 470 | m_counter.decrement ( sizeof ( R ) ); | ||
519 | 471 | delete p; | ||
520 | 472 | } | ||
521 | 473 | |||
522 | 474 | template < typename R, bool TRACE > | ||
523 | 475 | inline size_t RackAlloc < R, rap_pool, TRACE > :: rackCount () | ||
524 | 476 | { | ||
525 | 477 | return m_counter.rackCount (); | ||
526 | 478 | } | ||
527 | 479 | |||
528 | 480 | template < typename R, bool TRACE > | ||
529 | 481 | inline size_t RackAlloc < R, rap_pool, TRACE > :: byteCount () | ||
530 | 482 | { | ||
531 | 483 | return m_counter.byteCount (); | ||
532 | 484 | } | ||
533 | 485 | |||
534 | 486 | template < typename R, bool TRACE > | ||
535 | 487 | inline RackAlloc < R, rap_freeList, TRACE > :: RackAlloc () : | ||
536 | 488 | m_pAlias ( 0 ) | ||
537 | 489 | { | ||
538 | 490 | VALGRIND_CREATE_MEMPOOL_EXT ( this, sizeof (epicsMemChkRedZone), | ||
539 | 491 | false, VALGRIND_MEMPOOL_METAPOOL ); | ||
540 | 492 | } | ||
541 | 493 | |||
542 | 494 | template < typename R, bool TRACE > | ||
543 | 495 | RackAlloc < R, rap_freeList, TRACE > :: ~RackAlloc () | ||
544 | 496 | { | ||
545 | 497 | Guard guard ( m_mutex ); | ||
546 | 498 | M_Alias * p = m_pAlias; | ||
547 | 499 | while ( p ) { | ||
548 | 500 | M_Alias * pNext = p->m_pNext; | ||
549 | 501 | { | ||
550 | 502 | delete p; | ||
551 | 503 | } | ||
552 | 504 | p = pNext; | ||
553 | 505 | }; | ||
554 | 506 | VALGRIND_DESTROY_MEMPOOL ( this ); | ||
555 | 507 | } | ||
556 | 508 | |||
557 | 509 | template < typename R, bool TRACE > | ||
558 | 510 | R * RackAlloc < R, rap_freeList, TRACE > :: create ( | ||
559 | 511 | const type_info & ti ) | ||
560 | 512 | { | ||
561 | 513 | M_Alias * pA = 0; | ||
562 | 514 | { | ||
563 | 515 | Guard guard ( m_mutex ); | ||
564 | 516 | if ( m_pAlias ) { | ||
565 | 517 | pA = m_pAlias; | ||
566 | 518 | m_pAlias = m_pAlias->m_pNext; | ||
567 | 519 | } | ||
568 | 520 | } | ||
569 | 521 | if ( ! pA ) { | ||
570 | 522 | pA = new M_Alias; | ||
571 | 523 | VALGRIND_MAKE_MEM_NOACCESS ( pA, sizeof ( *pA ) ); | ||
572 | 524 | } | ||
573 | 525 | VALGRIND_MEMPOOL_ALLOC ( this, pA->m_rackBuf, | ||
574 | 526 | sizeof ( pA->m_rackBuf ) ); | ||
575 | 527 | m_counter.increment ( sizeof ( R ), ti ); | ||
576 | 528 | |||
577 | 529 | return new ( pA->m_rackBuf ) R (); | ||
578 | 530 | } | ||
579 | 531 | |||
580 | 532 | template < typename R, bool TRACE > | ||
581 | 533 | void RackAlloc < R, rap_freeList, TRACE > :: destroy ( R * const pR ) | ||
582 | 534 | { | ||
583 | 535 | if ( pR ) { | ||
584 | 536 | pR->~R (); | ||
585 | 537 | static const size_t bufOffset = offsetof ( M_Alias, m_rackBuf ); | ||
586 | 538 | const POctet pOctets = reinterpret_cast < POctet > ( pR ) - bufOffset; | ||
587 | 539 | M_Alias * const pA = reinterpret_cast < M_Alias * > ( pOctets ); | ||
588 | 540 | VALGRIND_MEMPOOL_FREE ( this, pA->m_rackBuf ); | ||
589 | 541 | VALGRIND_MAKE_MEM_UNDEFINED ( &pA->m_pNext, sizeof ( pA->m_pNext ) ); | ||
590 | 542 | { | ||
591 | 543 | Guard guard ( m_mutex ); | ||
592 | 544 | pA->m_pNext = m_pAlias; | ||
593 | 545 | m_pAlias = pA; | ||
594 | 546 | } | ||
595 | 547 | VALGRIND_MAKE_MEM_DEFINED ( &pA->m_pNext, sizeof ( pA->m_pNext ) ); | ||
596 | 548 | m_counter.decrement ( sizeof ( R ) ); | ||
597 | 549 | } | ||
598 | 550 | } | ||
599 | 551 | |||
600 | 552 | template < typename R, bool TRACE > | ||
601 | 553 | inline size_t RackAlloc < R, rap_freeList, TRACE > :: rackCount () const | ||
602 | 554 | { | ||
603 | 555 | return m_counter.rackCount (); | ||
604 | 556 | } | ||
605 | 557 | |||
606 | 558 | template < typename R, bool TRACE > | ||
607 | 559 | inline size_t RackAlloc < R, rap_freeList, TRACE > :: byteCount () const | ||
608 | 560 | { | ||
609 | 561 | return m_counter.byteCount (); | ||
610 | 562 | } | ||
611 | 563 | |||
612 | 564 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
613 | 565 | inline AllocatorArena < T, G, N, P, TRACE > :: AllocatorArena () | ||
614 | 566 | { | ||
615 | 567 | } | ||
616 | 568 | |||
617 | 569 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
618 | 570 | template < typename Y > | ||
619 | 571 | inline AllocatorArena < T, G, N, P, TRACE > :: AllocatorArena ( const Y & ) | ||
620 | 572 | { | ||
621 | 573 | } | ||
622 | 574 | |||
623 | 575 | // address | ||
624 | 576 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
625 | 577 | inline typename AllocatorArena < T, G, N, P, TRACE > :: pointer | ||
626 | 578 | AllocatorArena < T, G, N, P, TRACE > :: address ( reference r ) | ||
627 | 579 | { | ||
628 | 580 | return & r; | ||
629 | 581 | } | ||
630 | 582 | |||
631 | 583 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
632 | 584 | inline typename AllocatorArena < T, G, N, P, TRACE > :: const_pointer | ||
633 | 585 | AllocatorArena < T, G, N, P, TRACE > :: address ( const_reference r ) | ||
634 | 586 | { | ||
635 | 587 | return & r; | ||
636 | 588 | } | ||
637 | 589 | |||
638 | 590 | // | ||
639 | 591 | // memory allocation | ||
640 | 592 | // (inline arranges for pNearbyHint to be eliminated | ||
641 | 593 | // at compile time) | ||
642 | 594 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
643 | 595 | inline typename AllocatorArena < T, G, N, P, TRACE > :: pointer | ||
644 | 596 | AllocatorArena < T, G, N, P, TRACE > :: allocate ( size_type nAlloc, | ||
645 | 597 | void_const_pointer pNearbyHint ) | ||
646 | 598 | { | ||
647 | 599 | pointer p; | ||
648 | 600 | if ( nAlloc == 1u ) { | ||
649 | 601 | #if defined ( ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE ) | ||
650 | 602 | # if __cplusplus >= 201703L | ||
651 | 603 | void * const pVoid = :: operator new ( sizeof (T), | ||
652 | 604 | alignof ( T ) ); | ||
653 | 605 | # else | ||
654 | 606 | // This function is required to return a pointer suitably | ||
655 | 607 | // aligned to hold an object of any fundamental alignment. | ||
656 | 608 | void * const pVoid = :: operator new ( sizeof (T) ); | ||
657 | 609 | # endif | ||
658 | 610 | p = static_cast < pointer > ( pVoid ); | ||
659 | 611 | #else | ||
660 | 612 | p = m_threadPrivateAlloc (); | ||
661 | 613 | #endif | ||
662 | 614 | } | ||
663 | 615 | else { | ||
664 | 616 | # if __cplusplus >= 201703L | ||
665 | 617 | void * const pVoid = :: operator new ( sizeof (T) * nAlloc, | ||
666 | 618 | alignof ( T ) ); | ||
667 | 619 | # else | ||
668 | 620 | // This function is required to return a pointer suitably | ||
669 | 621 | // aligned to hold an object of any fundamental alignment. | ||
670 | 622 | void * const pVoid = :: operator new ( sizeof (T) * nAlloc ); | ||
671 | 623 | # endif | ||
672 | 624 | p = static_cast < pointer > ( pVoid ); | ||
673 | 625 | } | ||
674 | 626 | return p; | ||
675 | 627 | } | ||
676 | 628 | |||
677 | 629 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
678 | 630 | inline void AllocatorArena < T, G, N, P, TRACE > :: deallocate ( const pointer p, | ||
679 | 631 | size_type nAlloc ) | ||
680 | 632 | { | ||
681 | 633 | if ( nAlloc == 1u ) { | ||
682 | 634 | #if defined ( ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE ) | ||
683 | 635 | :: operator delete ( p ); | ||
684 | 636 | #else | ||
685 | 637 | M_Rack * const pRack = M_Rack :: dealloc ( p ); | ||
686 | 638 | if ( pRack ) { | ||
687 | 639 | staticInstance < M_RackAlloc > ().destroy ( pRack ); | ||
688 | 640 | } | ||
689 | 641 | #endif | ||
690 | 642 | } | ||
691 | 643 | else { | ||
692 | 644 | :: operator delete ( p ); | ||
693 | 645 | } | ||
694 | 646 | } | ||
695 | 647 | |||
696 | 648 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
697 | 649 | inline void * AllocatorArena < T, G, N, P, TRACE > :: | ||
698 | 650 | allocateOctets ( size_type sz ) | ||
699 | 651 | { | ||
700 | 652 | if ( sz == sizeof ( T ) ) { | ||
701 | 653 | return AllocatorArena :: allocate ( 1u ); | ||
702 | 654 | } | ||
703 | 655 | else { | ||
704 | 656 | // This function is required to return a pointer suitably | ||
705 | 657 | // aligned to hold an object of any fundamental alignment. | ||
706 | 658 | return :: operator new ( sz ); | ||
707 | 659 | } | ||
708 | 660 | } | ||
709 | 661 | |||
710 | 662 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
711 | 663 | inline void * AllocatorArena < T, G, N, P, TRACE > :: | ||
712 | 664 | allocateOctets ( size_type sz, | ||
713 | 665 | const std :: nothrow_t & ) | ||
714 | 666 | { | ||
715 | 667 | if ( sz == sizeof ( T ) ) { | ||
716 | 668 | try { | ||
717 | 669 | return AllocatorArena :: allocate ( 1u ); | ||
718 | 670 | } | ||
719 | 671 | catch ( const std :: bad_alloc & ) { | ||
720 | 672 | return static_cast < void * > ( 0 ); | ||
721 | 673 | } | ||
722 | 674 | } | ||
723 | 675 | else { | ||
724 | 676 | // This function is required to return a pointer suitably | ||
725 | 677 | // aligned to hold an object of any fundamental alignment. | ||
726 | 678 | return :: operator new ( sz, std :: nothrow ); | ||
727 | 679 | } | ||
728 | 680 | } | ||
729 | 681 | |||
730 | 682 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
731 | 683 | inline void AllocatorArena < T, G, N, P, TRACE > :: | ||
732 | 684 | deallocateOctets ( void * const p, size_type sz ) | ||
733 | 685 | { | ||
734 | 686 | if ( sz == sizeof ( T ) ) { | ||
735 | 687 | T * const pT = static_cast < T * > ( p ); | ||
736 | 688 | AllocatorArena :: deallocate ( pT, 1u ); | ||
737 | 689 | } | ||
738 | 690 | else { | ||
739 | 691 | #if __cplusplus >= 201400L | ||
740 | 692 | :: operator delete ( p, sz ); | ||
741 | 693 | #else | ||
742 | 694 | :: operator delete ( p ); | ||
743 | 695 | #endif | ||
744 | 696 | } | ||
745 | 697 | } | ||
746 | 698 | |||
747 | 699 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
748 | 700 | inline typename AllocatorArena < T, G, N, P, TRACE > :: size_type | ||
749 | 701 | AllocatorArena < T, G, N, P, TRACE > :: max_size () | ||
750 | 702 | { | ||
751 | 703 | // This class is intended only for use with an allocation size of one; | ||
752 | 704 | // if requests for more than one object occur it falls back to ordinary | ||
753 | 705 | // new and delete based allocation. | ||
754 | 706 | return 1u; | ||
755 | 707 | } | ||
756 | 708 | |||
757 | 709 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
758 | 710 | typename AllocatorArena < T, G, N, P, TRACE > :: pointer | ||
759 | 711 | AllocatorArena < T, G, N, P, TRACE > :: m_threadPrivateAlloc () | ||
760 | 712 | { | ||
761 | 713 | AllocCtx < G > & ctx = staticInstance < AllocCtxTyped < T, G > > (); | ||
762 | 714 | RackManager * const pRM = ctx.getRackHandlerPtr (); | ||
763 | 715 | M_Rack * pRack = static_cast < M_Rack * > ( pRM->m_pRack ); | ||
764 | 716 | if ( pRack ) { | ||
765 | 717 | pointer p = static_cast < T * > ( pRack->alloc () ); | ||
766 | 718 | if ( p ) { | ||
767 | 719 | if ( pRack->empty () ) { | ||
768 | 720 | assert ( pRM->m_pThreadExitFunc ); | ||
769 | 721 | ( *pRM->m_pThreadExitFunc ) ( pRack ); | ||
770 | 722 | pRM->m_pThreadExitFunc = 0; | ||
771 | 723 | pRM->m_pRack = 0; | ||
772 | 724 | } | ||
773 | 725 | return p; | ||
774 | 726 | } | ||
775 | 727 | assert ( pRM->m_pThreadExitFunc ); | ||
776 | 728 | ( *pRM->m_pThreadExitFunc ) ( pRack ); | ||
777 | 729 | pRM->m_pThreadExitFunc = 0; | ||
778 | 730 | pRM->m_pRack = 0; | ||
779 | 731 | } | ||
780 | 732 | pRack = staticInstance < M_RackAlloc > ().create ( typeid ( T ) ); | ||
781 | 733 | assert ( pRack ); | ||
782 | 734 | pRack->addReference (); | ||
783 | 735 | pRM->m_pThreadExitFunc = m_rackCleanup; | ||
784 | 736 | pRM->m_pRack = pRack; | ||
785 | 737 | pointer p = static_cast < pointer > ( pRack->alloc () ); | ||
786 | 738 | assert ( p ); | ||
787 | 739 | return p; | ||
788 | 740 | } | ||
789 | 741 | |||
790 | 742 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
791 | 743 | void AllocatorArena < T, G, N, P, TRACE > :: m_rackCleanup ( void * const pPriv ) | ||
792 | 744 | { | ||
793 | 745 | assert ( pPriv ); | ||
794 | 746 | M_Rack * pRack = static_cast < M_Rack * > ( pPriv ); | ||
795 | 747 | if ( pRack->removeReference () == 0u ) { | ||
796 | 748 | staticInstance < M_RackAlloc > ().destroy ( pRack ); | ||
797 | 749 | } | ||
798 | 750 | } | ||
799 | 751 | |||
800 | 752 | #if __cplusplus >= 201103L | ||
801 | 753 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
802 | 754 | template < typename T0, typename ... Args > | ||
803 | 755 | inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( T0 * p, | ||
804 | 756 | Args && ... args ) | ||
805 | 757 | { | ||
806 | 758 | void * const pvoid = p; | ||
807 | 759 | :: new ( pvoid ) T0 ( std :: forward <Args> ( args ) ... ); | ||
808 | 760 | } | ||
809 | 761 | #else | ||
810 | 762 | |||
811 | 763 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
812 | 764 | template < typename T0, typename A0 > | ||
813 | 765 | inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( | ||
814 | 766 | T0 * p, A0 a0 ) | ||
815 | 767 | { | ||
816 | 768 | new ( p ) T ( a0 ); | ||
817 | 769 | } | ||
818 | 770 | |||
819 | 771 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
820 | 772 | template < typename T0, typename A0, typename A1 > | ||
821 | 773 | inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( | ||
822 | 774 | T0 * p, A0 a0, A1 a1 ) | ||
823 | 775 | { | ||
824 | 776 | new ( p ) T ( a0, a1 ); | ||
825 | 777 | } | ||
826 | 778 | |||
827 | 779 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
828 | 780 | template < typename T0, typename A0, typename A1, typename A2 > | ||
829 | 781 | inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( | ||
830 | 782 | T0 * p, A0 a0, A1 a1, A2 a2 ) | ||
831 | 783 | { | ||
832 | 784 | new ( p ) T ( a0, a1, a2 ); | ||
833 | 785 | } | ||
834 | 786 | |||
835 | 787 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
836 | 788 | template < typename T0, typename A0, typename A1, typename A2, | ||
837 | 789 | typename A3 > | ||
838 | 790 | inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( | ||
839 | 791 | T0 * p, A0 a0, A1 a1, A2 a2, A3 a3 ) | ||
840 | 792 | { | ||
841 | 793 | new ( p ) T ( a0, a1, a2, a3 ); | ||
842 | 794 | } | ||
843 | 795 | |||
844 | 796 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
845 | 797 | template < typename T0, typename A0, typename A1, typename A2, | ||
846 | 798 | typename A3, typename A4 > | ||
847 | 799 | inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( | ||
848 | 800 | T0 * p, A0 a0, A1 a1, A2 a2, A3 a3, | ||
849 | 801 | A4 a4 ) | ||
850 | 802 | { | ||
851 | 803 | new ( p ) T ( a0, a1, a2, a3, a4 ); | ||
852 | 804 | } | ||
853 | 805 | |||
854 | 806 | #endif | ||
855 | 807 | |||
856 | 808 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
857 | 809 | template < class U > | ||
858 | 810 | inline void AllocatorArena < T, G, N, P, TRACE > :: destroy ( U * p ) | ||
859 | 811 | { | ||
860 | 812 | p->~U(); | ||
861 | 813 | } | ||
862 | 814 | |||
863 | 815 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
864 | 816 | inline bool AllocatorArena < T, G, N, P, TRACE > :: operator == ( | ||
865 | 817 | AllocatorArena const & ao ) | ||
866 | 818 | { | ||
867 | 819 | return true; | ||
868 | 820 | } | ||
869 | 821 | |||
870 | 822 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
871 | 823 | inline bool AllocatorArena < T, G, N, P, TRACE > :: operator != ( | ||
872 | 824 | AllocatorArena const & a ) | ||
873 | 825 | { | ||
874 | 826 | return ! operator == ( a ); | ||
875 | 827 | } | ||
876 | 828 | |||
877 | 829 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
878 | 830 | inline void AllocatorArena < T, G, N, P, TRACE > :: cleanup () | ||
879 | 831 | { | ||
880 | 832 | AllocCtx < G > & ctx = staticInstance < AllocCtxTyped < T, G > > (); | ||
881 | 833 | ctx.cleanup (); | ||
882 | 834 | } | ||
883 | 835 | |||
884 | 836 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
885 | 837 | inline size_t AllocatorArena < T, G, N, P, TRACE > :: rackCount () | ||
886 | 838 | { | ||
887 | 839 | return staticInstance < M_RackAlloc > ().rackCount(); | ||
888 | 840 | } | ||
889 | 841 | |||
890 | 842 | template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > | ||
891 | 843 | inline size_t AllocatorArena < T, G, N, P, TRACE > :: byteCount () | ||
892 | 844 | { | ||
893 | 845 | return staticInstance < M_RackAlloc > ().byteCount(); | ||
894 | 846 | } | ||
895 | 847 | |||
896 | 848 | template < size_t S, size_t A, size_t N > | ||
897 | 849 | const size_t Rack < S, A, N > :: number = N; | ||
898 | 850 | |||
899 | 851 | template < size_t S, size_t A, size_t N > | ||
900 | 852 | const size_t Rack < S, A, N > :: alignment = A; | ||
901 | 853 | |||
902 | 854 | template < size_t S, size_t A, size_t N > | ||
903 | 855 | inline Rack < S, A, N > :: Rack () : | ||
904 | 856 | m_nAlloc ( 0u ), | ||
905 | 857 | m_refCount ( 0u ) | ||
906 | 858 | { | ||
907 | 859 | VALGRIND_MAKE_MEM_NOACCESS ( m_wrapped, sizeof ( m_wrapped ) ); | ||
908 | 860 | } | ||
909 | 861 | |||
910 | 862 | template < size_t S, size_t A, size_t N > | ||
911 | 863 | inline Rack < S, A, N > :: ~Rack () | ||
912 | 864 | { | ||
913 | 865 | // if a Rack allocating free list is destroyed, and it destroys | ||
914 | 866 | // a rack with outstanding references remaining then problems | ||
915 | 867 | // will ensue | ||
916 | 868 | assert ( m_refCount == 0u ); | ||
917 | 869 | VALGRIND_MAKE_MEM_NOACCESS ( m_wrapped, sizeof ( m_wrapped ) ); | ||
918 | 870 | } | ||
919 | 871 | |||
920 | 872 | template < size_t S, size_t A, size_t N > | ||
921 | 873 | inline bool Rack < S, A, N > :: empty () const | ||
922 | 874 | { | ||
923 | 875 | return m_nAlloc >= N; | ||
924 | 876 | } | ||
925 | 877 | |||
926 | 878 | template < size_t S, size_t A, size_t N > | ||
927 | 879 | void * Rack < S, A, N > :: alloc () | ||
928 | 880 | { | ||
929 | 881 | void * p = 0; | ||
930 | 882 | if ( m_nAlloc < N ) { | ||
931 | 883 | M_Wrapper * const pAlloc = & m_wrapped[m_nAlloc]; | ||
932 | 884 | m_nAlloc++; | ||
933 | 885 | this->addReference (); | ||
934 | 886 | VALGRIND_MAKE_MEM_UNDEFINED ( &pAlloc->m_pRack, | ||
935 | 887 | sizeof ( pAlloc->m_pRack ) ); | ||
936 | 888 | atomic :: set ( pAlloc->m_pRack, this ); | ||
937 | 889 | VALGRIND_MAKE_MEM_DEFINED ( &pAlloc->m_pRack, | ||
938 | 890 | sizeof ( pAlloc->m_pRack ) ); | ||
939 | 891 | p = static_cast < void * > ( pAlloc->m_bufForObj ); | ||
940 | 892 | VALGRIND_MALLOCLIKE_BLOCK ( pAlloc->m_bufForObj, | ||
941 | 893 | sizeof ( pAlloc->m_bufForObj ), | ||
942 | 894 | sizeof ( epicsMemChkRedZone ), | ||
943 | 895 | false ); | ||
944 | 896 | } | ||
945 | 897 | return p; | ||
946 | 898 | } | ||
947 | 899 | |||
948 | 900 | template < size_t S, size_t A, size_t N > | ||
949 | 901 | Rack < S, A , N > * Rack < S, A, N > :: dealloc ( void * const p ) | ||
950 | 902 | { | ||
951 | 903 | static const size_t bufOffset = offsetof ( M_Wrapper, m_bufForObj ); | ||
952 | 904 | const POctet pOctets = static_cast < POctet > ( p ) - bufOffset; | ||
953 | 905 | const PWrapper pDealloc = reinterpret_cast < PWrapper > ( pOctets ); | ||
954 | 906 | VALGRIND_FREELIKE_BLOCK ( pDealloc->m_bufForObj, | ||
955 | 907 | sizeof ( epicsMemChkRedZone ) ); | ||
956 | 908 | Rack * const pRack = static_cast < Rack * > | ||
957 | 909 | ( atomic :: get ( pDealloc->m_pRack ) ); | ||
958 | 910 | pDealloc->m_pRack = 0; // for imperfect error detection purposes | ||
959 | 911 | assert ( pRack ); | ||
960 | 912 | if ( pRack->removeReference () == 0u ) { | ||
961 | 913 | return pRack; | ||
962 | 914 | } | ||
963 | 915 | return 0; | ||
964 | 916 | } | ||
965 | 917 | |||
966 | 918 | template < size_t S, size_t A, size_t N > | ||
967 | 919 | inline void Rack < S, A, N > :: addReference () | ||
968 | 920 | { | ||
969 | 921 | assert ( m_refCount < SIZE_MAX ); | ||
970 | 922 | atomic :: increment ( m_refCount ); | ||
971 | 923 | } | ||
972 | 924 | |||
973 | 925 | template < size_t S, size_t A, size_t N > | ||
974 | 926 | inline size_t Rack < S, A, N > :: removeReference () | ||
975 | 927 | { | ||
976 | 928 | assert ( m_refCount > 0u ); | ||
977 | 929 | return atomic :: decrement ( m_refCount ); | ||
978 | 930 | } | ||
979 | 931 | |||
980 | 932 | } // end of name space _impl | ||
981 | 933 | |||
982 | 934 | using _impl :: Rack; | ||
983 | 935 | using _impl :: RackAlloc; | ||
984 | 936 | using _impl :: RackAllocPolicy; | ||
985 | 937 | using _impl :: rap_pool; | ||
986 | 938 | using _impl :: rap_freeList; | ||
987 | 939 | using _impl :: AllocatorArena; | ||
988 | 940 | |||
989 | 941 | } // end of name space epics | ||
990 | 942 | |||
991 | 943 | #endif // if not defined epicsAllocatorArena_h | ||
992 | 944 | |||
993 | diff --git a/modules/libcom/src/misc/AllocatorArenaUntyped.cpp b/modules/libcom/src/misc/AllocatorArenaUntyped.cpp | |||
994 | 0 | new file mode 100644 | 945 | new file mode 100644 |
995 | index 0000000..91693a2 | |||
996 | --- /dev/null | |||
997 | +++ b/modules/libcom/src/misc/AllocatorArenaUntyped.cpp | |||
998 | @@ -0,0 +1,199 @@ | |||
999 | 1 | |||
1000 | 2 | /*************************************************************************\ | ||
1001 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1002 | 4 | * National Laboratory | ||
1003 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1004 | 6 | * in file LICENSE that is included with this distribution. | ||
1005 | 7 | \*************************************************************************/ | ||
1006 | 8 | |||
1007 | 9 | /* | ||
1008 | 10 | * Author Jeffrey O. Hill | ||
1009 | 11 | */ | ||
1010 | 12 | |||
1011 | 13 | #include <cstdio> | ||
1012 | 14 | #include <memory> | ||
1013 | 15 | |||
1014 | 16 | #define epicsExportSharedSymbols | ||
1015 | 17 | #include "AllocatorArena.h" | ||
1016 | 18 | #include "errlog.h" | ||
1017 | 19 | #include "epicsTypes.h" | ||
1018 | 20 | #include "epicsExit.h" | ||
1019 | 21 | #include "epicsDemangle.h" | ||
1020 | 22 | |||
1021 | 23 | namespace epics { | ||
1022 | 24 | namespace _impl { | ||
1023 | 25 | |||
1024 | 26 | const char * ThreadPrivateIdBadAlloc :: what () const throw () | ||
1025 | 27 | { | ||
1026 | 28 | return "epicsThreadPrivateCreate returned nill"; | ||
1027 | 29 | } | ||
1028 | 30 | |||
1029 | 31 | const char * AtThreadExitBadAlloc :: what () const throw () | ||
1030 | 32 | { | ||
1031 | 33 | return "epicsAtThreadExit was unsuccessful"; | ||
1032 | 34 | } | ||
1033 | 35 | |||
1034 | 36 | const size_t AllocCtxCom :: m_initialCapacity = 16u; | ||
1035 | 37 | |||
1036 | 38 | AllocCtxCom :: AllocCtxCom () : | ||
1037 | 39 | m_threadPrivateId ( epicsThreadPrivateCreate () ), | ||
1038 | 40 | m_curIdx ( 0 ) | ||
1039 | 41 | { | ||
1040 | 42 | } | ||
1041 | 43 | |||
1042 | 44 | union RackManagerUnion { | ||
1043 | 45 | RackManager m_entry; | ||
1044 | 46 | size_t m_capacity; | ||
1045 | 47 | }; | ||
1046 | 48 | |||
1047 | 49 | size_t AllocCtxCom :: allocIdx () | ||
1048 | 50 | { | ||
1049 | 51 | return atomic :: increment ( m_curIdx ); | ||
1050 | 52 | } | ||
1051 | 53 | |||
1052 | 54 | /* | ||
1053 | 55 | * from the bit twiddling hacks web site | ||
1054 | 56 | */ | ||
1055 | 57 | static inline epicsUInt32 nextPwrOf2 ( epicsUInt32 v ) | ||
1056 | 58 | { | ||
1057 | 59 | v--; | ||
1058 | 60 | v |= v >> 1u; | ||
1059 | 61 | v |= v >> 2u; | ||
1060 | 62 | v |= v >> 4u; | ||
1061 | 63 | v |= v >> 8u; | ||
1062 | 64 | v |= v >> 16u; | ||
1063 | 65 | v++; | ||
1064 | 66 | return v; | ||
1065 | 67 | } | ||
1066 | 68 | |||
1067 | 69 | RackManager * AllocCtxCom :: getRackHandlerPtr ( const size_t idx ) | ||
1068 | 70 | { | ||
1069 | 71 | void * const pPriv = epicsThreadPrivateGet ( m_threadPrivateId ); | ||
1070 | 72 | RackManagerUnion * pThrPriv; | ||
1071 | 73 | if ( pPriv ) { | ||
1072 | 74 | pThrPriv = static_cast < RackManagerUnion * > ( pPriv ); | ||
1073 | 75 | if ( idx >= pThrPriv->m_capacity ) { | ||
1074 | 76 | const size_t newCapacity = nextPwrOf2 ( idx + 1u ); | ||
1075 | 77 | RackManagerUnion * const pThrPrivNew = | ||
1076 | 78 | new RackManagerUnion [newCapacity]; | ||
1077 | 79 | assert ( newCapacity > idx ); | ||
1078 | 80 | size_t i; | ||
1079 | 81 | for ( i = 1u; i < pThrPriv->m_capacity; i++ ) { | ||
1080 | 82 | pThrPrivNew[i] = pThrPriv[i]; | ||
1081 | 83 | } | ||
1082 | 84 | for ( i = pThrPriv->m_capacity; i < newCapacity; i++ ) { | ||
1083 | 85 | pThrPrivNew[i].m_entry.m_pRack = 0; | ||
1084 | 86 | pThrPrivNew[i].m_entry.m_pThreadExitFunc = 0; | ||
1085 | 87 | } | ||
1086 | 88 | pThrPrivNew->m_capacity = newCapacity; | ||
1087 | 89 | delete [] pThrPriv; | ||
1088 | 90 | epicsThreadPrivateSet ( m_threadPrivateId, pThrPrivNew ); | ||
1089 | 91 | pThrPriv = pThrPrivNew; | ||
1090 | 92 | } | ||
1091 | 93 | } | ||
1092 | 94 | else { | ||
1093 | 95 | size_t capacity; | ||
1094 | 96 | if ( idx >= m_initialCapacity ) { | ||
1095 | 97 | capacity = nextPwrOf2 ( idx + 1u ); | ||
1096 | 98 | } | ||
1097 | 99 | else { | ||
1098 | 100 | capacity = m_initialCapacity; | ||
1099 | 101 | } | ||
1100 | 102 | pThrPriv = new RackManagerUnion [capacity]; | ||
1101 | 103 | pThrPriv->m_capacity = capacity; | ||
1102 | 104 | for ( size_t i = 1u; i < capacity; i++ ) { | ||
1103 | 105 | pThrPriv[i].m_entry.m_pRack = 0; | ||
1104 | 106 | pThrPriv[i].m_entry.m_pThreadExitFunc = 0; | ||
1105 | 107 | } | ||
1106 | 108 | int status = epicsAtThreadExit ( m_threadExitFunc, this ); | ||
1107 | 109 | if ( status != 0 ) { | ||
1108 | 110 | delete [] pThrPriv; | ||
1109 | 111 | throw AtThreadExitBadAlloc (); | ||
1110 | 112 | } | ||
1111 | 113 | epicsThreadPrivateSet ( m_threadPrivateId, pThrPriv ); | ||
1112 | 114 | } | ||
1113 | 115 | return & pThrPriv[idx].m_entry; | ||
1114 | 116 | } | ||
1115 | 117 | |||
1116 | 118 | void AllocCtxCom :: cleanup ( const size_t idx ) | ||
1117 | 119 | { | ||
1118 | 120 | void * const pPriv = epicsThreadPrivateGet ( m_threadPrivateId ); | ||
1119 | 121 | if ( pPriv ) { | ||
1120 | 122 | RackManagerUnion * pThrPriv = | ||
1121 | 123 | static_cast < RackManagerUnion * > ( pPriv ); | ||
1122 | 124 | if ( idx < pThrPriv->m_capacity ) { | ||
1123 | 125 | if ( pThrPriv[idx].m_entry.m_pThreadExitFunc && | ||
1124 | 126 | pThrPriv[idx].m_entry.m_pRack ) { | ||
1125 | 127 | ( *pThrPriv[idx].m_entry.m_pThreadExitFunc ) | ||
1126 | 128 | ( pThrPriv[idx].m_entry.m_pRack ); | ||
1127 | 129 | pThrPriv[idx].m_entry.m_pRack = 0; | ||
1128 | 130 | pThrPriv[idx].m_entry.m_pThreadExitFunc = 0; | ||
1129 | 131 | } | ||
1130 | 132 | } | ||
1131 | 133 | } | ||
1132 | 134 | } | ||
1133 | 135 | |||
1134 | 136 | void AllocCtxCom :: m_threadExitFunc ( void * const pPriv ) | ||
1135 | 137 | { | ||
1136 | 138 | AllocCtxCom * const pCtx = static_cast < AllocCtxCom * > ( pPriv ); | ||
1137 | 139 | void * const pThrPrivVoid = epicsThreadPrivateGet ( pCtx->m_threadPrivateId ); | ||
1138 | 140 | if ( pThrPrivVoid ) { | ||
1139 | 141 | RackManagerUnion * const pThrPriv = | ||
1140 | 142 | static_cast < RackManagerUnion * > ( pThrPrivVoid ); | ||
1141 | 143 | for ( size_t i = 1u; i < pThrPriv->m_capacity; i++ ) { | ||
1142 | 144 | if ( pThrPriv[i].m_entry.m_pThreadExitFunc && | ||
1143 | 145 | pThrPriv[i].m_entry.m_pRack ) { | ||
1144 | 146 | ( *pThrPriv[i].m_entry.m_pThreadExitFunc ) | ||
1145 | 147 | ( pThrPriv[i].m_entry.m_pRack ); | ||
1146 | 148 | } | ||
1147 | 149 | } | ||
1148 | 150 | delete [] pThrPriv; | ||
1149 | 151 | epicsThreadPrivateSet ( pCtx->m_threadPrivateId, 0 ); | ||
1150 | 152 | } | ||
1151 | 153 | } | ||
1152 | 154 | |||
1153 | 155 | AllocCounter < true > :: AllocCounter () | ||
1154 | 156 | { | ||
1155 | 157 | atomic :: set ( m_nRacks, 0u ); | ||
1156 | 158 | atomic :: set ( m_bytes, 0u ); | ||
1157 | 159 | atomic :: set ( m_nRacksTrace, 8 ); | ||
1158 | 160 | } | ||
1159 | 161 | |||
1160 | 162 | static size_t m_nRacksTotal = 0u; | ||
1161 | 163 | static size_t m_bytesTotal = 0u; | ||
1162 | 164 | |||
1163 | 165 | void AllocCounter < true > :: increment ( size_t nBytesThisTime, | ||
1164 | 166 | const type_info & ti ) | ||
1165 | 167 | { | ||
1166 | 168 | const size_t newNRacksVal = atomic :: increment ( m_nRacks ); | ||
1167 | 169 | atomic :: increment ( m_nRacksTotal ); | ||
1168 | 170 | atomic :: add ( m_bytes, nBytesThisTime ); | ||
1169 | 171 | atomic :: add ( m_bytesTotal, nBytesThisTime ); | ||
1170 | 172 | if ( newNRacksVal >= m_nRacksTrace ) { | ||
1171 | 173 | atomic :: add ( m_nRacksTrace, m_nRacksTrace ); | ||
1172 | 174 | this->show ( ti ); | ||
1173 | 175 | } | ||
1174 | 176 | } | ||
1175 | 177 | |||
1176 | 178 | void AllocCounter < true > :: decrement ( size_t nBytesThisTime ) | ||
1177 | 179 | { | ||
1178 | 180 | atomic :: decrement ( m_nRacks ); | ||
1179 | 181 | atomic :: decrement ( m_nRacksTotal ); | ||
1180 | 182 | atomic :: subtract ( m_bytes, nBytesThisTime ); | ||
1181 | 183 | atomic :: subtract ( m_bytesTotal, nBytesThisTime ); | ||
1182 | 184 | } | ||
1183 | 185 | |||
1184 | 186 | void AllocCounter < true > :: show ( const type_info & ti ) const | ||
1185 | 187 | { | ||
1186 | 188 | const std :: string name = epicsDemangleTypeName ( ti ); | ||
1187 | 189 | errlogPrintf ( | ||
1188 | 190 | "AA C=%08lu SZ=%08lu CT=%08lu SZT=%08lu \"%s\"\n", | ||
1189 | 191 | ( unsigned long ) m_nRacks, | ||
1190 | 192 | ( unsigned long ) m_bytes, | ||
1191 | 193 | ( unsigned long ) m_nRacksTotal, | ||
1192 | 194 | ( unsigned long ) m_bytesTotal, | ||
1193 | 195 | name.c_str () ); | ||
1194 | 196 | } | ||
1195 | 197 | |||
1196 | 198 | } // end of name space _impl | ||
1197 | 199 | } // end of name space epics | ||
1198 | diff --git a/modules/libcom/src/misc/Makefile b/modules/libcom/src/misc/Makefile | |||
1199 | index 3d5412d..fb94ec9 100644 | |||
1200 | --- a/modules/libcom/src/misc/Makefile | |||
1201 | +++ b/modules/libcom/src/misc/Makefile | |||
1202 | @@ -27,6 +27,7 @@ INC += ipAddrToAsciiAsynchronous.h | |||
1203 | 27 | INC += compilerDependencies.h | 27 | INC += compilerDependencies.h |
1204 | 28 | INC += epicsUnitTest.h | 28 | INC += epicsUnitTest.h |
1205 | 29 | INC += testMain.h | 29 | INC += testMain.h |
1206 | 30 | INC += AllocatorArena.h | ||
1207 | 30 | 31 | ||
1208 | 31 | # epicsVersion.h is created by this Makefile | 32 | # epicsVersion.h is created by this Makefile |
1209 | 32 | INC += epicsVersion.h | 33 | INC += epicsVersion.h |
1210 | @@ -42,3 +43,5 @@ Com_SRCS += epicsString.c | |||
1211 | 42 | Com_SRCS += truncateFile.c | 43 | Com_SRCS += truncateFile.c |
1212 | 43 | Com_SRCS += ipAddrToAsciiAsynchronous.cpp | 44 | Com_SRCS += ipAddrToAsciiAsynchronous.cpp |
1213 | 44 | Com_SRCS += epicsUnitTest.c | 45 | Com_SRCS += epicsUnitTest.c |
1214 | 46 | Com_SRCS += AllocatorArenaUntyped.cpp | ||
1215 | 47 | |||
1216 | diff --git a/modules/libcom/src/osi/Makefile b/modules/libcom/src/osi/Makefile | |||
1217 | index f0108e6..9e1dcee 100644 | |||
1218 | --- a/modules/libcom/src/osi/Makefile | |||
1219 | +++ b/modules/libcom/src/osi/Makefile | |||
1220 | @@ -9,8 +9,16 @@ | |||
1221 | 9 | 9 | ||
1222 | 10 | SRC_DIRS += $(LIBCOM)/osi | 10 | SRC_DIRS += $(LIBCOM)/osi |
1223 | 11 | 11 | ||
1224 | 12 | # compiler specific | ||
1225 | 12 | INC += compilerDependencies.h | 13 | INC += compilerDependencies.h |
1226 | 13 | INC += compilerSpecific.h | 14 | INC += compilerSpecific.h |
1227 | 15 | INC += epicsStaticInstance.h | ||
1228 | 16 | INC += epicsStaticInstanceCD.h | ||
1229 | 17 | INC += epicsStaticInstanceSaneCmplr.h | ||
1230 | 18 | INC += epicsStaticInstanceSketchyCmplr.h | ||
1231 | 19 | |||
1232 | 20 | Com_SRCS += epicsStaticInstanceSketchyCmplr.cpp | ||
1233 | 21 | Com_SRCS += compilerDependentDemangle.cpp | ||
1234 | 14 | 22 | ||
1235 | 15 | INC += osiFileName.h | 23 | INC += osiFileName.h |
1236 | 16 | INC += osiSock.h | 24 | INC += osiSock.h |
1237 | @@ -57,6 +65,7 @@ INC += epicsStdioRedirect.h | |||
1238 | 57 | INC += epicsTempFile.h | 65 | INC += epicsTempFile.h |
1239 | 58 | INC += epicsGetopt.h | 66 | INC += epicsGetopt.h |
1240 | 59 | INC += epicsStackTrace.h | 67 | INC += epicsStackTrace.h |
1241 | 68 | INC += epicsDemangle.h | ||
1242 | 60 | 69 | ||
1243 | 61 | INC += devLib.h | 70 | INC += devLib.h |
1244 | 62 | INC += devLibVME.h | 71 | INC += devLibVME.h |
1245 | @@ -157,3 +166,4 @@ Com_SRCS_WIN32 += forceBadAllocException.cpp | |||
1246 | 157 | Com_SRCS += epicsStackTrace.c | 166 | Com_SRCS += epicsStackTrace.c |
1247 | 158 | Com_SRCS += osdBackTrace.cpp | 167 | Com_SRCS += osdBackTrace.cpp |
1248 | 159 | Com_SRCS += osdFindAddr.c | 168 | Com_SRCS += osdFindAddr.c |
1249 | 169 | |||
1250 | diff --git a/modules/libcom/src/osi/compiler/clang/compilerSpecific.h b/modules/libcom/src/osi/compiler/clang/compilerSpecific.h | |||
1251 | index 0498f6e..c06272f 100644 | |||
1252 | --- a/modules/libcom/src/osi/compiler/clang/compilerSpecific.h | |||
1253 | +++ b/modules/libcom/src/osi/compiler/clang/compilerSpecific.h | |||
1254 | @@ -1,4 +1,6 @@ | |||
1255 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
1256 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1257 | 3 | * National Laboratory | ||
1258 | 2 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne | 4 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1259 | 3 | * National Laboratory. | 5 | * National Laboratory. |
1260 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
1261 | @@ -29,6 +31,8 @@ | |||
1262 | 29 | /* Expands to a 'const char*' which describes the name of the current function scope */ | 31 | /* Expands to a 'const char*' which describes the name of the current function scope */ |
1263 | 30 | #define EPICS_FUNCTION __PRETTY_FUNCTION__ | 32 | #define EPICS_FUNCTION __PRETTY_FUNCTION__ |
1264 | 31 | 33 | ||
1265 | 34 | #define NO_RETURN __attribute__((noreturn)) | ||
1266 | 35 | |||
1267 | 32 | #ifdef __cplusplus | 36 | #ifdef __cplusplus |
1268 | 33 | 37 | ||
1269 | 34 | /* | 38 | /* |
1270 | @@ -36,6 +40,8 @@ | |||
1271 | 36 | */ | 40 | */ |
1272 | 37 | #define CXX_PLACEMENT_DELETE | 41 | #define CXX_PLACEMENT_DELETE |
1273 | 38 | 42 | ||
1274 | 43 | #define USING_BASE_TYPE(B,T) using typename B :: T; | ||
1275 | 44 | |||
1276 | 39 | #endif /* __cplusplus */ | 45 | #endif /* __cplusplus */ |
1277 | 40 | 46 | ||
1278 | 41 | /* | 47 | /* |
1279 | diff --git a/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h | |||
1280 | index 763cb05..515129b 100644 | |||
1281 | --- a/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h | |||
1282 | +++ b/modules/libcom/src/osi/compiler/clang/epicsAtomicCD.h | |||
1283 | @@ -1,7 +1,7 @@ | |||
1284 | 1 | 1 | ||
1285 | 2 | /*************************************************************************\ | 2 | /*************************************************************************\ |
1288 | 3 | * Copyright (c) 2011 LANS LLC, as Operator of | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1289 | 4 | * Los Alamos National Laboratory. | 4 | * National Laboratory |
1290 | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1291 | 6 | * National Laboratory. | 6 | * National Laboratory. |
1292 | 7 | * EPICS BASE is distributed subject to a Software License Agreement found | 7 | * EPICS BASE is distributed subject to a Software License Agreement found |
1293 | diff --git a/modules/libcom/src/osi/compiler/default/compilerDependentDemangle.cpp b/modules/libcom/src/osi/compiler/default/compilerDependentDemangle.cpp | |||
1294 | 8 | new file mode 100644 | 8 | new file mode 100644 |
1295 | index 0000000..4a1f404 | |||
1296 | --- /dev/null | |||
1297 | +++ b/modules/libcom/src/osi/compiler/default/compilerDependentDemangle.cpp | |||
1298 | @@ -0,0 +1,28 @@ | |||
1299 | 1 | |||
1300 | 2 | /*************************************************************************\ | ||
1301 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1302 | 4 | * National Laboratory | ||
1303 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1304 | 6 | * in file LICENSE that is included with this distribution. | ||
1305 | 7 | \*************************************************************************/ | ||
1306 | 8 | |||
1307 | 9 | /* | ||
1308 | 10 | * Author: Jeffrey O. Hill | ||
1309 | 11 | */ | ||
1310 | 12 | #include <exception> | ||
1311 | 13 | #include <cstdlib> | ||
1312 | 14 | |||
1313 | 15 | #define epicsExportSharedSymbols | ||
1314 | 16 | #include "epicsDemangle.h" | ||
1315 | 17 | |||
1316 | 18 | using std :: string; | ||
1317 | 19 | |||
1318 | 20 | std :: string epicsDemangle ( const char * const pMangledName ) | ||
1319 | 21 | { | ||
1320 | 22 | return std :: string ( pMangledName ); | ||
1321 | 23 | } | ||
1322 | 24 | |||
1323 | 25 | std :: string epicsDemangleTypeName ( const std :: type_info & ti ) | ||
1324 | 26 | { | ||
1325 | 27 | return epicsDemangle ( ti.name () ); | ||
1326 | 28 | } | ||
1327 | diff --git a/modules/libcom/src/osi/compiler/default/compilerSpecific.h b/modules/libcom/src/osi/compiler/default/compilerSpecific.h | |||
1328 | index 8af1727..3c64c39 100644 | |||
1329 | --- a/modules/libcom/src/osi/compiler/default/compilerSpecific.h | |||
1330 | +++ b/modules/libcom/src/osi/compiler/default/compilerSpecific.h | |||
1331 | @@ -1,4 +1,6 @@ | |||
1332 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
1333 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1334 | 3 | * National Laboratory | ||
1335 | 2 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne | 4 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1336 | 3 | * National Laboratory. | 5 | * National Laboratory. |
1337 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
1338 | @@ -26,15 +28,18 @@ | |||
1339 | 26 | 28 | ||
1340 | 27 | #ifdef __cplusplus | 29 | #ifdef __cplusplus |
1341 | 28 | 30 | ||
1342 | 31 | #define NO_RETURN | ||
1343 | 32 | |||
1344 | 33 | |||
1345 | 29 | /* | 34 | /* |
1346 | 30 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete | 35 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete |
1347 | 31 | * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification | ||
1348 | 32 | * | 36 | * |
1349 | 33 | * (our default guess is that the compiler implements the C++ 97 standard) | 37 | * (our default guess is that the compiler implements the C++ 97 standard) |
1350 | 34 | */ | 38 | */ |
1351 | 35 | #define CXX_THROW_SPECIFICATION | ||
1352 | 36 | #define CXX_PLACEMENT_DELETE | 39 | #define CXX_PLACEMENT_DELETE |
1353 | 37 | 40 | ||
1354 | 41 | #define USING_BASE_TYPE(B,T) using typename B :: T; | ||
1355 | 42 | |||
1356 | 38 | #endif /* __cplusplus */ | 43 | #endif /* __cplusplus */ |
1357 | 39 | 44 | ||
1358 | 40 | 45 | ||
1359 | diff --git a/modules/libcom/src/osi/compiler/default/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/default/epicsAtomicCD.h | |||
1360 | index 585a39c..55e4ab7 100644 | |||
1361 | --- a/modules/libcom/src/osi/compiler/default/epicsAtomicCD.h | |||
1362 | +++ b/modules/libcom/src/osi/compiler/default/epicsAtomicCD.h | |||
1363 | @@ -1,7 +1,7 @@ | |||
1364 | 1 | 1 | ||
1365 | 2 | /*************************************************************************\ | 2 | /*************************************************************************\ |
1368 | 3 | * Copyright (c) 2011 LANS LLC, as Operator of | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1369 | 4 | * Los Alamos National Laboratory. | 4 | * National Laboratory |
1370 | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1371 | 6 | * National Laboratory. | 6 | * National Laboratory. |
1372 | 7 | * EPICS BASE is distributed subject to a Software License Agreement found | 7 | * EPICS BASE is distributed subject to a Software License Agreement found |
1373 | diff --git a/modules/libcom/src/osi/compiler/default/epicsStaticInstanceCD.h b/modules/libcom/src/osi/compiler/default/epicsStaticInstanceCD.h | |||
1374 | 8 | new file mode 100644 | 8 | new file mode 100644 |
1375 | index 0000000..094e7a9 | |||
1376 | --- /dev/null | |||
1377 | +++ b/modules/libcom/src/osi/compiler/default/epicsStaticInstanceCD.h | |||
1378 | @@ -0,0 +1,20 @@ | |||
1379 | 1 | |||
1380 | 2 | /*************************************************************************\ | ||
1381 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1382 | 4 | * National Laboratory | ||
1383 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1384 | 6 | * in file LICENSE that is included with this distribution. | ||
1385 | 7 | \*************************************************************************/ | ||
1386 | 8 | |||
1387 | 9 | /* | ||
1388 | 10 | * Author Jeffrey O. Hill | ||
1389 | 11 | * johill@lanl.gov | ||
1390 | 12 | */ | ||
1391 | 13 | |||
1392 | 14 | #ifndef epicsStaticInstanceCD_h | ||
1393 | 15 | #define epicsStaticInstanceCD_h | ||
1394 | 16 | |||
1395 | 17 | // we dont trust compilers we dont know | ||
1396 | 18 | # include "epicsStaticInstanceSketchyCmplr.h" | ||
1397 | 19 | |||
1398 | 20 | #endif /* epicsStaticInstanceCD_h */ | ||
1399 | diff --git a/modules/libcom/src/osi/compiler/gcc/compilerDependentDemangle.cpp b/modules/libcom/src/osi/compiler/gcc/compilerDependentDemangle.cpp | |||
1400 | 0 | new file mode 100644 | 21 | new file mode 100644 |
1401 | index 0000000..a0f0429 | |||
1402 | --- /dev/null | |||
1403 | +++ b/modules/libcom/src/osi/compiler/gcc/compilerDependentDemangle.cpp | |||
1404 | @@ -0,0 +1,168 @@ | |||
1405 | 1 | |||
1406 | 2 | /*************************************************************************\ | ||
1407 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1408 | 4 | * National Laboratory | ||
1409 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1410 | 6 | * in file LICENSE that is included with this distribution. | ||
1411 | 7 | \*************************************************************************/ | ||
1412 | 8 | |||
1413 | 9 | /* | ||
1414 | 10 | * Author: Jeffrey O. Hill | ||
1415 | 11 | */ | ||
1416 | 12 | #include <exception> | ||
1417 | 13 | #include <cstdlib> | ||
1418 | 14 | |||
1419 | 15 | #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 300 | ||
1420 | 16 | # include <cxxabi.h> | ||
1421 | 17 | #endif | ||
1422 | 18 | |||
1423 | 19 | #include "epicsThread.h" | ||
1424 | 20 | #include "epicsAtomic.h" | ||
1425 | 21 | #include "epicsExit.h" | ||
1426 | 22 | #include "epicsAssert.h" | ||
1427 | 23 | |||
1428 | 24 | #include "epicsDemangle.h" | ||
1429 | 25 | |||
1430 | 26 | using std :: string; | ||
1431 | 27 | |||
1432 | 28 | union ThreadPrivateId { | ||
1433 | 29 | ThreadPrivateId () : m_epics ( 0 ) {} | ||
1434 | 30 | epicsThreadPrivateId m_epics; | ||
1435 | 31 | void * m_cmpAndSwap; | ||
1436 | 32 | }; | ||
1437 | 33 | |||
1438 | 34 | class ThreadPrivate { | ||
1439 | 35 | public: | ||
1440 | 36 | ThreadPrivate (); | ||
1441 | 37 | ~ThreadPrivate (); | ||
1442 | 38 | string demangle ( const char * pMangledName ); | ||
1443 | 39 | static ThreadPrivate * get (); | ||
1444 | 40 | static void epicsExit ( void * /* arg */ ); | ||
1445 | 41 | private: | ||
1446 | 42 | char * m_pBuf; | ||
1447 | 43 | size_t m_bufSize; | ||
1448 | 44 | static const size_t m_bufSizeInit; | ||
1449 | 45 | static ThreadPrivateId m_privateId; | ||
1450 | 46 | }; | ||
1451 | 47 | |||
1452 | 48 | const size_t ThreadPrivate :: m_bufSizeInit = 256u; | ||
1453 | 49 | ThreadPrivateId ThreadPrivate :: m_privateId; | ||
1454 | 50 | |||
1455 | 51 | // | ||
1456 | 52 | // __cxa_demangle doc indicates that buffer passed must be allocated | ||
1457 | 53 | // with malloc | ||
1458 | 54 | // | ||
1459 | 55 | ThreadPrivate :: ThreadPrivate () : | ||
1460 | 56 | m_pBuf ( static_cast < char * > ( malloc ( m_bufSizeInit ) ) ), | ||
1461 | 57 | m_bufSize ( m_pBuf ? m_bufSizeInit : 0u ) | ||
1462 | 58 | { | ||
1463 | 59 | } | ||
1464 | 60 | |||
1465 | 61 | ThreadPrivate :: ~ThreadPrivate () | ||
1466 | 62 | { | ||
1467 | 63 | // | ||
1468 | 64 | // __cxa_demangle doc indicates that buffer passed must be allocated | ||
1469 | 65 | // with malloc | ||
1470 | 66 | // | ||
1471 | 67 | free ( m_pBuf ); | ||
1472 | 68 | } | ||
1473 | 69 | |||
1474 | 70 | string ThreadPrivate :: demangle ( const char * const pMangledName ) | ||
1475 | 71 | { | ||
1476 | 72 | if ( ! m_pBuf ) { | ||
1477 | 73 | // | ||
1478 | 74 | // __cxa_demangle doc indicates that buffer passed must be | ||
1479 | 75 | // allocated with malloc | ||
1480 | 76 | // | ||
1481 | 77 | m_pBuf = static_cast < char * > ( malloc ( m_bufSizeInit ) ); | ||
1482 | 78 | if ( m_pBuf ) { | ||
1483 | 79 | m_bufSize = m_bufSizeInit; | ||
1484 | 80 | } | ||
1485 | 81 | else { | ||
1486 | 82 | m_bufSize = 0u; | ||
1487 | 83 | return pMangledName; | ||
1488 | 84 | } | ||
1489 | 85 | } | ||
1490 | 86 | int status = -1000; | ||
1491 | 87 | size_t sz = m_bufSize; | ||
1492 | 88 | char * const pBufResult = | ||
1493 | 89 | abi :: __cxa_demangle ( pMangledName, m_pBuf, | ||
1494 | 90 | & sz, & status ); | ||
1495 | 91 | if ( pBufResult ) { | ||
1496 | 92 | m_pBuf = pBufResult; | ||
1497 | 93 | m_bufSize = sz; | ||
1498 | 94 | if ( status == 0 ) { | ||
1499 | 95 | return string ( pBufResult ); | ||
1500 | 96 | } | ||
1501 | 97 | else { | ||
1502 | 98 | return string ( pMangledName ); | ||
1503 | 99 | } | ||
1504 | 100 | } | ||
1505 | 101 | return string ( pMangledName ); | ||
1506 | 102 | } | ||
1507 | 103 | |||
1508 | 104 | void ThreadPrivate :: epicsExit ( void * p ) | ||
1509 | 105 | { | ||
1510 | 106 | if ( m_privateId.m_epics ) { | ||
1511 | 107 | epicsThreadPrivateSet ( m_privateId.m_epics, 0 ); | ||
1512 | 108 | } | ||
1513 | 109 | ThreadPrivate * const pPriv = static_cast < ThreadPrivate * > ( p ); | ||
1514 | 110 | delete pPriv; | ||
1515 | 111 | } | ||
1516 | 112 | |||
1517 | 113 | inline ThreadPrivate * ThreadPrivate :: get () | ||
1518 | 114 | { | ||
1519 | 115 | STATIC_ASSERT ( sizeof ( m_privateId.m_cmpAndSwap ) == | ||
1520 | 116 | sizeof ( m_privateId.m_epics ) ); | ||
1521 | 117 | if ( ! m_privateId.m_epics ) { | ||
1522 | 118 | ThreadPrivateId newId; | ||
1523 | 119 | newId.m_epics = epicsThreadPrivateCreate (); | ||
1524 | 120 | if ( ! newId.m_epics ) { | ||
1525 | 121 | return 0; | ||
1526 | 122 | } | ||
1527 | 123 | const EpicsAtomicPtrT pBefore = | ||
1528 | 124 | epicsAtomicCmpAndSwapPtrT ( & m_privateId.m_cmpAndSwap, | ||
1529 | 125 | 0, newId.m_cmpAndSwap ); | ||
1530 | 126 | if ( pBefore ) { | ||
1531 | 127 | epicsThreadPrivateDelete ( newId.m_epics ); | ||
1532 | 128 | } | ||
1533 | 129 | } | ||
1534 | 130 | void * const p = epicsThreadPrivateGet ( m_privateId.m_epics ); | ||
1535 | 131 | ThreadPrivate * pPriv = static_cast < ThreadPrivate * > ( p ); | ||
1536 | 132 | if ( ! pPriv ) { | ||
1537 | 133 | pPriv = new ( std :: nothrow ) ThreadPrivate (); | ||
1538 | 134 | if ( pPriv ) { | ||
1539 | 135 | const int status = | ||
1540 | 136 | epicsAtThreadExit ( ThreadPrivate :: epicsExit, pPriv ); | ||
1541 | 137 | if ( status < 0 ) { | ||
1542 | 138 | delete pPriv; | ||
1543 | 139 | pPriv = 0; | ||
1544 | 140 | } | ||
1545 | 141 | epicsThreadPrivateSet ( m_privateId.m_epics, pPriv ); | ||
1546 | 142 | } | ||
1547 | 143 | } | ||
1548 | 144 | return pPriv; | ||
1549 | 145 | } | ||
1550 | 146 | |||
1551 | 147 | std :: string epicsDemangle ( const char * const pMangledName ) | ||
1552 | 148 | { | ||
1553 | 149 | if ( pMangledName ) { | ||
1554 | 150 | # if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 300 | ||
1555 | 151 | ThreadPrivate * const pPriv = ThreadPrivate :: get (); | ||
1556 | 152 | if ( ! pPriv ) { | ||
1557 | 153 | return pMangledName; | ||
1558 | 154 | } | ||
1559 | 155 | return pPriv->demangle ( pMangledName ); | ||
1560 | 156 | # else | ||
1561 | 157 | return pMangledName; | ||
1562 | 158 | # endif | ||
1563 | 159 | } | ||
1564 | 160 | else { | ||
1565 | 161 | return "<nil type name>"; | ||
1566 | 162 | } | ||
1567 | 163 | } | ||
1568 | 164 | |||
1569 | 165 | std :: string epicsDemangleTypeName ( const std :: type_info & ti ) | ||
1570 | 166 | { | ||
1571 | 167 | return epicsDemangle ( ti.name () ); | ||
1572 | 168 | } | ||
1573 | diff --git a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h | |||
1574 | index 3342308..3a75150 100644 | |||
1575 | --- a/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h | |||
1576 | +++ b/modules/libcom/src/osi/compiler/gcc/compilerSpecific.h | |||
1577 | @@ -1,4 +1,6 @@ | |||
1578 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
1579 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1580 | 3 | * National Laboratory | ||
1581 | 2 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne | 4 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1582 | 3 | * National Laboratory. | 5 | * National Laboratory. |
1583 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
1584 | @@ -29,6 +31,8 @@ | |||
1585 | 29 | /* Expands to a 'const char*' which describes the name of the current function scope */ | 31 | /* Expands to a 'const char*' which describes the name of the current function scope */ |
1586 | 30 | #define EPICS_FUNCTION __PRETTY_FUNCTION__ | 32 | #define EPICS_FUNCTION __PRETTY_FUNCTION__ |
1587 | 31 | 33 | ||
1588 | 34 | #define NO_RETURN __attribute__((noreturn)) | ||
1589 | 35 | |||
1590 | 32 | #ifdef __cplusplus | 36 | #ifdef __cplusplus |
1591 | 33 | 37 | ||
1592 | 34 | /* | 38 | /* |
1593 | @@ -50,6 +54,12 @@ | |||
1594 | 50 | */ | 54 | */ |
1595 | 51 | #define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) | 55 | #define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) |
1596 | 52 | 56 | ||
1597 | 57 | #if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 8 ) | ||
1598 | 58 | # define USING_BASE_TYPE(B,T) using typename B :: T; | ||
1599 | 59 | #else | ||
1600 | 60 | # define USING_BASE_TYPE(B,T) typedef typename B :: T T; | ||
1601 | 61 | #endif | ||
1602 | 62 | |||
1603 | 53 | /* | 63 | /* |
1604 | 54 | * Deprecation marker | 64 | * Deprecation marker |
1605 | 55 | */ | 65 | */ |
1606 | diff --git a/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h | |||
1607 | index 15b9a6c..d268803 100644 | |||
1608 | --- a/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h | |||
1609 | +++ b/modules/libcom/src/osi/compiler/gcc/epicsAtomicCD.h | |||
1610 | @@ -1,7 +1,7 @@ | |||
1611 | 1 | 1 | ||
1612 | 2 | /*************************************************************************\ | 2 | /*************************************************************************\ |
1615 | 3 | * Copyright (c) 2011 LANS LLC, as Operator of | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1616 | 4 | * Los Alamos National Laboratory. | 4 | * National Laboratory |
1617 | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1618 | 6 | * National Laboratory. | 6 | * National Laboratory. |
1619 | 7 | * EPICS BASE is distributed subject to a Software License Agreement found | 7 | * EPICS BASE is distributed subject to a Software License Agreement found |
1620 | diff --git a/modules/libcom/src/osi/compiler/gcc/epicsStaticInstanceCD.h b/modules/libcom/src/osi/compiler/gcc/epicsStaticInstanceCD.h | |||
1621 | 8 | new file mode 100644 | 8 | new file mode 100644 |
1622 | index 0000000..d7d974e | |||
1623 | --- /dev/null | |||
1624 | +++ b/modules/libcom/src/osi/compiler/gcc/epicsStaticInstanceCD.h | |||
1625 | @@ -0,0 +1,26 @@ | |||
1626 | 1 | |||
1627 | 2 | /*************************************************************************\ | ||
1628 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1629 | 4 | * National Laboratory | ||
1630 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1631 | 6 | * in file LICENSE that is included with this distribution. | ||
1632 | 7 | \*************************************************************************/ | ||
1633 | 8 | |||
1634 | 9 | /* | ||
1635 | 10 | * Author Jeffrey O. Hill | ||
1636 | 11 | * johill@lanl.gov | ||
1637 | 12 | */ | ||
1638 | 13 | |||
1639 | 14 | #ifndef epicsStaticInstanceCD_h | ||
1640 | 15 | #define epicsStaticInstanceCD_h | ||
1641 | 16 | |||
1642 | 17 | #if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 | ||
1643 | 18 | // any gcc version that has -fno-threadsafe-statics | ||
1644 | 19 | // should be ok | ||
1645 | 20 | # include "epicsStaticInstanceSaneCmplr.h" | ||
1646 | 21 | #else // ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 | ||
1647 | 22 | // other gcc maybe cant be trusted without more testing | ||
1648 | 23 | # include "epicsStaticInstanceSketchyCmplr.h" | ||
1649 | 24 | #endif // ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 | ||
1650 | 25 | |||
1651 | 26 | #endif // epicsStaticInstanceCD_h | ||
1652 | diff --git a/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h b/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h | |||
1653 | index 4e282db..ab4e134 100644 | |||
1654 | --- a/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h | |||
1655 | +++ b/modules/libcom/src/osi/compiler/msvc/compilerSpecific.h | |||
1656 | @@ -1,4 +1,6 @@ | |||
1657 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
1658 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1659 | 3 | * National Laboratory | ||
1660 | 2 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne | 4 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1661 | 3 | * National Laboratory. | 5 | * National Laboratory. |
1662 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
1663 | @@ -21,6 +23,8 @@ | |||
1664 | 21 | #endif | 23 | #endif |
1665 | 22 | 24 | ||
1666 | 23 | #define EPICS_ALWAYS_INLINE __forceinline | 25 | #define EPICS_ALWAYS_INLINE __forceinline |
1667 | 26 | #define NO_RETURN __declspec(noreturn) | ||
1668 | 27 | |||
1669 | 24 | 28 | ||
1670 | 25 | /* Expands to a 'const char*' which describes the name of the current function scope */ | 29 | /* Expands to a 'const char*' which describes the name of the current function scope */ |
1671 | 26 | #define EPICS_FUNCTION __FUNCTION__ | 30 | #define EPICS_FUNCTION __FUNCTION__ |
1672 | @@ -36,10 +40,10 @@ | |||
1673 | 36 | 40 | ||
1674 | 37 | /* | 41 | /* |
1675 | 38 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete | 42 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete |
1676 | 39 | * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification | ||
1677 | 40 | */ | 43 | */ |
1678 | 41 | #define CXX_PLACEMENT_DELETE | 44 | #define CXX_PLACEMENT_DELETE |
1680 | 42 | #define CXX_THROW_SPECIFICATION | 45 | |
1681 | 46 | #define USING_BASE_TYPE(B,T) using typename B :: T; | ||
1682 | 43 | 47 | ||
1683 | 44 | #endif /* __cplusplus */ | 48 | #endif /* __cplusplus */ |
1684 | 45 | 49 | ||
1685 | diff --git a/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h | |||
1686 | index e7d1c4b..f9ddc79 100644 | |||
1687 | --- a/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h | |||
1688 | +++ b/modules/libcom/src/osi/compiler/msvc/epicsAtomicCD.h | |||
1689 | @@ -1,7 +1,7 @@ | |||
1690 | 1 | 1 | ||
1691 | 2 | /*************************************************************************\ | 2 | /*************************************************************************\ |
1694 | 3 | * Copyright (c) 2011 LANS LLC, as Operator of | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1695 | 4 | * Los Alamos National Laboratory. | 4 | * National Laboratory |
1696 | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1697 | 6 | * National Laboratory. | 6 | * National Laboratory. |
1698 | 7 | * EPICS BASE is distributed subject to a Software License Agreement found | 7 | * EPICS BASE is distributed subject to a Software License Agreement found |
1699 | diff --git a/modules/libcom/src/osi/compiler/msvc/epicsStaticInstanceCD.h b/modules/libcom/src/osi/compiler/msvc/epicsStaticInstanceCD.h | |||
1700 | 8 | new file mode 100644 | 8 | new file mode 100644 |
1701 | index 0000000..1859595 | |||
1702 | --- /dev/null | |||
1703 | +++ b/modules/libcom/src/osi/compiler/msvc/epicsStaticInstanceCD.h | |||
1704 | @@ -0,0 +1,32 @@ | |||
1705 | 1 | |||
1706 | 2 | /*************************************************************************\ | ||
1707 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1708 | 4 | * National Laboratory | ||
1709 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1710 | 6 | * in file LICENSE that is included with this distribution. | ||
1711 | 7 | \*************************************************************************/ | ||
1712 | 8 | |||
1713 | 9 | /* | ||
1714 | 10 | * Author Jeffrey O. Hill | ||
1715 | 11 | * johill@lanl.gov | ||
1716 | 12 | */ | ||
1717 | 13 | |||
1718 | 14 | #ifndef epicsStaticInstanceCD_h | ||
1719 | 15 | #define epicsStaticInstanceCD_h | ||
1720 | 16 | |||
1721 | 17 | // visual c++ has some issues | ||
1722 | 18 | // -------------------------- | ||
1723 | 19 | // | ||
1724 | 20 | // 1) it lacks mutual exclusion protecting concurrent on demand | ||
1725 | 21 | // initialization of block scope static variables | ||
1726 | 22 | // | ||
1727 | 23 | // 2) it silently adds the path to the .cpp file to the very beginning | ||
1728 | 24 | // of the include search list so if a msvc specific version of this | ||
1729 | 25 | // file does not exist and we use the default version then it will | ||
1730 | 26 | // also use compiler/default/ version, despite the existence | ||
1731 | 27 | // of a more specialized version, and even if not told to do so on | ||
1732 | 28 | // the command line | ||
1733 | 29 | // | ||
1734 | 30 | # include "epicsStaticInstanceSketchyCmplr.h" | ||
1735 | 31 | |||
1736 | 32 | #endif epicsStaticInstanceCD_h | ||
1737 | diff --git a/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h b/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h | |||
1738 | index a9d59a5..20065e2 100644 | |||
1739 | --- a/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h | |||
1740 | +++ b/modules/libcom/src/osi/compiler/solStudio/compilerSpecific.h | |||
1741 | @@ -1,4 +1,6 @@ | |||
1742 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
1743 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1744 | 3 | * National Laboratory | ||
1745 | 2 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne | 4 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1746 | 3 | * National Laboratory. | 5 | * National Laboratory. |
1747 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
1748 | @@ -31,11 +33,9 @@ | |||
1749 | 31 | 33 | ||
1750 | 32 | /* | 34 | /* |
1751 | 33 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete | 35 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete |
1752 | 34 | * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification | ||
1753 | 35 | * | 36 | * |
1754 | 36 | * (our default guess is that the compiler implements the C++ 97 standard) | 37 | * (our default guess is that the compiler implements the C++ 97 standard) |
1755 | 37 | */ | 38 | */ |
1756 | 38 | #define CXX_THROW_SPECIFICATION | ||
1757 | 39 | #define CXX_PLACEMENT_DELETE | 39 | #define CXX_PLACEMENT_DELETE |
1758 | 40 | 40 | ||
1759 | 41 | #endif /* __cplusplus */ | 41 | #endif /* __cplusplus */ |
1760 | diff --git a/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h b/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h | |||
1761 | index 8a733a5..b92e36a 100644 | |||
1762 | --- a/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h | |||
1763 | +++ b/modules/libcom/src/osi/compiler/solStudio/epicsAtomicCD.h | |||
1764 | @@ -1,7 +1,7 @@ | |||
1765 | 1 | 1 | ||
1766 | 2 | /*************************************************************************\ | 2 | /*************************************************************************\ |
1769 | 3 | * Copyright (c) 2011 LANS LLC, as Operator of | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1770 | 4 | * Los Alamos National Laboratory. | 4 | * National Laboratory |
1771 | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne | 5 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1772 | 6 | * National Laboratory. | 6 | * National Laboratory. |
1773 | 7 | * EPICS BASE is distributed subject to a Software License Agreement found | 7 | * EPICS BASE is distributed subject to a Software License Agreement found |
1774 | diff --git a/modules/libcom/src/osi/compilerDependencies.h b/modules/libcom/src/osi/compilerDependencies.h | |||
1775 | index 0b333d7..509687a 100644 | |||
1776 | --- a/modules/libcom/src/osi/compilerDependencies.h | |||
1777 | +++ b/modules/libcom/src/osi/compilerDependencies.h | |||
1778 | @@ -1,4 +1,6 @@ | |||
1779 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
1780 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1781 | 3 | * National Laboratory | ||
1782 | 2 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne | 4 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1783 | 3 | * National Laboratory. | 5 | * National Laboratory. |
1784 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
1785 | @@ -20,6 +22,46 @@ | |||
1786 | 20 | 22 | ||
1787 | 21 | #ifdef __cplusplus | 23 | #ifdef __cplusplus |
1788 | 22 | 24 | ||
1789 | 25 | #if __cplusplus >= 201103L | ||
1790 | 26 | #define epicsOverride override | ||
1791 | 27 | #else | ||
1792 | 28 | #define epicsOverride | ||
1793 | 29 | #endif | ||
1794 | 30 | |||
1795 | 31 | #if __cplusplus >= 201103L | ||
1796 | 32 | #define epicsFinal final | ||
1797 | 33 | #else | ||
1798 | 34 | #define epicsFinal | ||
1799 | 35 | #endif | ||
1800 | 36 | |||
1801 | 37 | #if __cplusplus >= 201103L | ||
1802 | 38 | #define epicsNoexcept noexcept | ||
1803 | 39 | #else | ||
1804 | 40 | #define epicsNoexcept throw() | ||
1805 | 41 | #endif | ||
1806 | 42 | |||
1807 | 43 | #if __cplusplus >= 201103L | ||
1808 | 44 | #define epicsConstexpr constexpr | ||
1809 | 45 | #else | ||
1810 | 46 | #define epicsConstexpr | ||
1811 | 47 | #endif | ||
1812 | 48 | |||
1813 | 49 | #if __cplusplus >= 201103L | ||
1814 | 50 | #define epicsMove(A) std::move(A) | ||
1815 | 51 | #else | ||
1816 | 52 | #define epicsMove(A) (A) | ||
1817 | 53 | #endif | ||
1818 | 54 | |||
1819 | 55 | /* | ||
1820 | 56 | * deleted method should also be declared private as per convention | ||
1821 | 57 | * with old compilers | ||
1822 | 58 | */ | ||
1823 | 59 | #if __cplusplus >= 201103L | ||
1824 | 60 | #define epicsDeleteMethod =delete | ||
1825 | 61 | #else | ||
1826 | 62 | #define epicsDeleteMethod | ||
1827 | 63 | #endif | ||
1828 | 64 | |||
1829 | 23 | /* | 65 | /* |
1830 | 24 | * usage: epicsPlacementDeleteOperator (( void *, myMemoryManager & )) | 66 | * usage: epicsPlacementDeleteOperator (( void *, myMemoryManager & )) |
1831 | 25 | */ | 67 | */ |
1832 | diff --git a/modules/libcom/src/osi/epicsDemangle.h b/modules/libcom/src/osi/epicsDemangle.h | |||
1833 | 26 | new file mode 100644 | 68 | new file mode 100644 |
1834 | index 0000000..07bd299 | |||
1835 | --- /dev/null | |||
1836 | +++ b/modules/libcom/src/osi/epicsDemangle.h | |||
1837 | @@ -0,0 +1,26 @@ | |||
1838 | 1 | |||
1839 | 2 | /*************************************************************************\ | ||
1840 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1841 | 4 | * National Laboratory | ||
1842 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1843 | 6 | * in file LICENSE that is included with this distribution. | ||
1844 | 7 | \*************************************************************************/ | ||
1845 | 8 | |||
1846 | 9 | /* | ||
1847 | 10 | * Author: Jeffrey O. Hill | ||
1848 | 11 | */ | ||
1849 | 12 | |||
1850 | 13 | #ifndef epicsDemangleh | ||
1851 | 14 | #define epicsDemangleh | ||
1852 | 15 | |||
1853 | 16 | #include <string> | ||
1854 | 17 | #include <typeinfo> | ||
1855 | 18 | |||
1856 | 19 | #include "shareLib.h" | ||
1857 | 20 | |||
1858 | 21 | epicsShareFunc std :: string | ||
1859 | 22 | epicsDemangle ( const char * const pMangledName ); | ||
1860 | 23 | epicsShareFunc std :: string | ||
1861 | 24 | epicsDemangleTypeName ( const std :: type_info & ); | ||
1862 | 25 | |||
1863 | 26 | #endif /* epicsDemangleh */ | ||
1864 | diff --git a/modules/libcom/src/osi/epicsStaticInstance.h b/modules/libcom/src/osi/epicsStaticInstance.h | |||
1865 | 0 | new file mode 100644 | 27 | new file mode 100644 |
1866 | index 0000000..8e8d469 | |||
1867 | --- /dev/null | |||
1868 | +++ b/modules/libcom/src/osi/epicsStaticInstance.h | |||
1869 | @@ -0,0 +1,52 @@ | |||
1870 | 1 | /*************************************************************************\ | ||
1871 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1872 | 3 | * National Laboratory | ||
1873 | 4 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1874 | 5 | * in file LICENSE that is included with this distribution. | ||
1875 | 6 | \*************************************************************************/ | ||
1876 | 7 | |||
1877 | 8 | /* | ||
1878 | 9 | * Author Jeffrey O. Hill | ||
1879 | 10 | * johill@lanl.gov | ||
1880 | 11 | */ | ||
1881 | 12 | |||
1882 | 13 | #ifndef epicsStaticInstance_h | ||
1883 | 14 | #define epicsStaticInstance_h | ||
1884 | 15 | |||
1885 | 16 | namespace epics { | ||
1886 | 17 | |||
1887 | 18 | // | ||
1888 | 19 | // c++ 11 specifies the behavior for concurrent | ||
1889 | 20 | // access to block scope statics but some compiler | ||
1890 | 21 | // writers, lacking clear guidance in the earlier | ||
1891 | 22 | // c++ standards, curiously implement thread unsafe | ||
1892 | 23 | // block static variables despite ensuring for | ||
1893 | 24 | // proper multi-threaded behavior for many other | ||
1894 | 25 | // aspects of the compiler runtime infrastructure | ||
1895 | 26 | // such as runtime support for exception handling | ||
1896 | 27 | // | ||
1897 | 28 | // T - the type of the initialized once only static | ||
1898 | 29 | // | ||
1899 | 30 | template < typename T > T & staticInstance (); | ||
1900 | 31 | |||
1901 | 32 | // | ||
1902 | 33 | // !!!! perhaps we need versions here that pass parameters | ||
1903 | 34 | // !!!! to the constructor using templates also | ||
1904 | 35 | // | ||
1905 | 36 | |||
1906 | 37 | } // end of name space epics | ||
1907 | 38 | |||
1908 | 39 | // | ||
1909 | 40 | // if the compiler claims its c++ 11 then we are optimistic, ref: | ||
1910 | 41 | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf | ||
1911 | 42 | // (see section 6.7) | ||
1912 | 43 | // | ||
1913 | 44 | #if __cplusplus >= 201103L | ||
1914 | 45 | # include "epicsStaticInstanceSaneCmplr.h" | ||
1915 | 46 | #else | ||
1916 | 47 | // otherwise we can implement compiler specific behavior | ||
1917 | 48 | # include "epicsStaticInstanceCD.h" | ||
1918 | 49 | #endif | ||
1919 | 50 | |||
1920 | 51 | #endif // ifndef epicsStaticInstance_h | ||
1921 | 52 | |||
1922 | diff --git a/modules/libcom/src/osi/epicsStaticInstanceSaneCmplr.h b/modules/libcom/src/osi/epicsStaticInstanceSaneCmplr.h | |||
1923 | 0 | new file mode 100644 | 53 | new file mode 100644 |
1924 | index 0000000..523e509 | |||
1925 | --- /dev/null | |||
1926 | +++ b/modules/libcom/src/osi/epicsStaticInstanceSaneCmplr.h | |||
1927 | @@ -0,0 +1,45 @@ | |||
1928 | 1 | |||
1929 | 2 | /*************************************************************************\ | ||
1930 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1931 | 4 | * National Laboratory | ||
1932 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1933 | 6 | * in file LICENSE that is included with this distribution. | ||
1934 | 7 | \*************************************************************************/ | ||
1935 | 8 | |||
1936 | 9 | /* | ||
1937 | 10 | * Author Jeffrey O. Hill | ||
1938 | 11 | * johill@lanl.gov | ||
1939 | 12 | */ | ||
1940 | 13 | |||
1941 | 14 | #ifndef epicsStaticInstanceSaneCmplr_h | ||
1942 | 15 | #define epicsStaticInstanceSaneCmplr_h | ||
1943 | 16 | |||
1944 | 17 | namespace epics { | ||
1945 | 18 | |||
1946 | 19 | // c++ 11 specifies the behavior for concurrent | ||
1947 | 20 | // access to block scope statics but some compiler | ||
1948 | 21 | // writers, lacking clear guidance in the earlier | ||
1949 | 22 | // c++ standards, curiously implement thread unsafe | ||
1950 | 23 | // block static variables despite ensuring for | ||
1951 | 24 | // proper multi-threaded behavior for many other | ||
1952 | 25 | // aspects of the compiler infrastructure such as | ||
1953 | 26 | // runtime support for exception handling | ||
1954 | 27 | // | ||
1955 | 28 | // if the compiler claims its c++ 11 then we are optimistic, ref: | ||
1956 | 29 | // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf | ||
1957 | 30 | // (see section 6.7) | ||
1958 | 31 | // "If control enters the (block-scope variable with static storage duration) | ||
1959 | 32 | // declaration concurrently while the variable is being initialized, the | ||
1960 | 33 | // concurrent execution shall wait for completion of the initialization." | ||
1961 | 34 | // | ||
1962 | 35 | // also see c++ FAQ, how do I prevent the | ||
1963 | 36 | // "static initialization order fiasco"? | ||
1964 | 37 | template < typename T > T & staticInstance () | ||
1965 | 38 | { | ||
1966 | 39 | static T * const m_pInstance = new T; | ||
1967 | 40 | return * m_pInstance; | ||
1968 | 41 | } | ||
1969 | 42 | |||
1970 | 43 | } // end of name space epics | ||
1971 | 44 | |||
1972 | 45 | #endif // ifndef epicsStaticInstanceSaneCmplr_h | ||
1973 | diff --git a/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.cpp b/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.cpp | |||
1974 | 0 | new file mode 100644 | 46 | new file mode 100644 |
1975 | index 0000000..9581618 | |||
1976 | --- /dev/null | |||
1977 | +++ b/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.cpp | |||
1978 | @@ -0,0 +1,88 @@ | |||
1979 | 1 | |||
1980 | 2 | /*************************************************************************\ | ||
1981 | 3 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
1982 | 4 | * National Laboratory | ||
1983 | 5 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
1984 | 6 | * in file LICENSE that is included with this distribution. | ||
1985 | 7 | \*************************************************************************/ | ||
1986 | 8 | |||
1987 | 9 | /* | ||
1988 | 10 | * Author Jeffrey O. Hill | ||
1989 | 11 | * johill@lanl.gov | ||
1990 | 12 | */ | ||
1991 | 13 | |||
1992 | 14 | #include <cstddef> | ||
1993 | 15 | |||
1994 | 16 | #define epicsExportSharedSymbols | ||
1995 | 17 | #include "errlog.h" | ||
1996 | 18 | #include "epicsThread.h" | ||
1997 | 19 | #include "epicsAssert.h" | ||
1998 | 20 | #include "epicsStaticInstanceSketchyCmplr.h" | ||
1999 | 21 | |||
2000 | 22 | namespace epics { | ||
2001 | 23 | |||
2002 | 24 | epicsShareDef char staticInstanceBusy; | ||
2003 | 25 | |||
2004 | 26 | // | ||
2005 | 27 | // c++ 0x specifies the behavior for concurrent | ||
2006 | 28 | // access to block scope statics but some compiler | ||
2007 | 29 | // writers, lacking clear guidance in the earlier | ||
2008 | 30 | // c++ standards, curiously implement thread unsafe | ||
2009 | 31 | // block static variables despite ensuring for | ||
2010 | 32 | // proper multi-threaded behavior for many other | ||
2011 | 33 | // aspects of the compiler infrastructure such as | ||
2012 | 34 | // runtime support for exception handling | ||
2013 | 35 | // | ||
2014 | 36 | // see also c++ faq, static initialization order fiasco | ||
2015 | 37 | // | ||
2016 | 38 | // This implementation is active if we don't | ||
2017 | 39 | // know for certain that we can trust the compiler. | ||
2018 | 40 | // This implementation is substantially more efficient | ||
2019 | 41 | // at runtime than epicsThreadOnce | ||
2020 | 42 | // | ||
2021 | 43 | // we are careful to create T no more than once here | ||
2022 | 44 | // | ||
2023 | 45 | EpicsAtomicPtrT staticInstanceInit ( EpicsAtomicPtrT & target, | ||
2024 | 46 | const PStaticInstanceFactory pFactory ) | ||
2025 | 47 | { | ||
2026 | 48 | static const std :: size_t spinDownInit = 1000u; | ||
2027 | 49 | static const std :: size_t spinCount = 10u; | ||
2028 | 50 | STATIC_ASSERT ( spinDownInit > spinCount ); | ||
2029 | 51 | static const std :: size_t spinThresh = spinDownInit - spinCount; | ||
2030 | 52 | std :: size_t spinDown = spinDownInit; | ||
2031 | 53 | EpicsAtomicPtrT pCur = 0; | ||
2032 | 54 | while ( true ) { | ||
2033 | 55 | pCur = epics :: atomic :: compareAndSwap ( target, | ||
2034 | 56 | pStaticInstanceInit, | ||
2035 | 57 | & staticInstanceBusy ); | ||
2036 | 58 | if ( pCur == pStaticInstanceInit ) { | ||
2037 | 59 | try { | ||
2038 | 60 | pCur = ( * pFactory ) (); | ||
2039 | 61 | epics :: atomic :: set ( target, pCur ); | ||
2040 | 62 | break; | ||
2041 | 63 | } | ||
2042 | 64 | catch ( ... ) { | ||
2043 | 65 | epics :: atomic :: set ( target, | ||
2044 | 66 | pStaticInstanceInit ); | ||
2045 | 67 | throw; | ||
2046 | 68 | } | ||
2047 | 69 | } | ||
2048 | 70 | else if ( pCur != & staticInstanceBusy ) { | ||
2049 | 71 | break; | ||
2050 | 72 | } | ||
2051 | 73 | if ( spinDown <= spinThresh ) { | ||
2052 | 74 | epicsThreadSleep ( epicsThreadSleepQuantum () ); | ||
2053 | 75 | } | ||
2054 | 76 | if ( spinDown > 0u ) { | ||
2055 | 77 | spinDown--; | ||
2056 | 78 | } | ||
2057 | 79 | else { | ||
2058 | 80 | errlogPrintf ( "staticInstanceInit: waiting for another " | ||
2059 | 81 | "thread to finish creating the static instance\n" ); | ||
2060 | 82 | spinDown = spinThresh; | ||
2061 | 83 | } | ||
2062 | 84 | } | ||
2063 | 85 | return pCur; | ||
2064 | 86 | } | ||
2065 | 87 | |||
2066 | 88 | } // end of name space epics | ||
2067 | diff --git a/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.h b/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.h | |||
2068 | 0 | new file mode 100644 | 89 | new file mode 100644 |
2069 | index 0000000..33765d9 | |||
2070 | --- /dev/null | |||
2071 | +++ b/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.h | |||
2072 | @@ -0,0 +1,53 @@ | |||
2073 | 1 | /*************************************************************************\ | ||
2074 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
2075 | 3 | * National Laboratory | ||
2076 | 4 | * EPICS BASE is distributed subject to a Software License Agreement found | ||
2077 | 5 | * in file LICENSE that is included with this distribution. | ||
2078 | 6 | \*************************************************************************/ | ||
2079 | 7 | |||
2080 | 8 | /* | ||
2081 | 9 | * Author Jeffrey O. Hill | ||
2082 | 10 | * johill@lanl.gov | ||
2083 | 11 | */ | ||
2084 | 12 | |||
2085 | 13 | #ifndef epicsStaticInstanceSketchyCmplr_h | ||
2086 | 14 | #define epicsStaticInstanceSketchyCmplr_h | ||
2087 | 15 | |||
2088 | 16 | #include "epicsAtomic.h" | ||
2089 | 17 | |||
2090 | 18 | #include "shareLib.h" | ||
2091 | 19 | |||
2092 | 20 | namespace epics { | ||
2093 | 21 | |||
2094 | 22 | typedef EpicsAtomicPtrT ( * PStaticInstanceFactory ) (); | ||
2095 | 23 | |||
2096 | 24 | epicsShareFunc EpicsAtomicPtrT staticInstanceInit ( EpicsAtomicPtrT & target, | ||
2097 | 25 | const PStaticInstanceFactory pFactory ); | ||
2098 | 26 | |||
2099 | 27 | epicsShareExtern char staticInstanceBusy; | ||
2100 | 28 | |||
2101 | 29 | namespace { | ||
2102 | 30 | template < typename T > | ||
2103 | 31 | EpicsAtomicPtrT staticInstanceFactory () | ||
2104 | 32 | { | ||
2105 | 33 | return new T (); | ||
2106 | 34 | } | ||
2107 | 35 | static const EpicsAtomicPtrT pStaticInstanceInit = 0; | ||
2108 | 36 | } | ||
2109 | 37 | |||
2110 | 38 | // this should _not_ be an in line function so that | ||
2111 | 39 | // we guarantee that only one instance of type T per | ||
2112 | 40 | // executable is created | ||
2113 | 41 | template < typename T > T & staticInstance () | ||
2114 | 42 | { | ||
2115 | 43 | static EpicsAtomicPtrT pInstance = pStaticInstanceInit; | ||
2116 | 44 | EpicsAtomicPtrT pCur = epics :: atomic :: get ( pInstance ); | ||
2117 | 45 | if ( pCur == pStaticInstanceInit || pCur == & staticInstanceBusy ) { | ||
2118 | 46 | pCur = staticInstanceInit ( pInstance, staticInstanceFactory < T > ); | ||
2119 | 47 | } | ||
2120 | 48 | return * reinterpret_cast < T * > ( pCur ); | ||
2121 | 49 | } | ||
2122 | 50 | |||
2123 | 51 | } // end of name space epics | ||
2124 | 52 | |||
2125 | 53 | #endif // ifndef epicsStaticInstanceSketchyCmplr_h | ||
2126 | diff --git a/modules/libcom/src/timer/epicsTimer.cpp b/modules/libcom/src/timer/epicsTimer.cpp | |||
2127 | index e55280e..96bfa31 100644 | |||
2128 | --- a/modules/libcom/src/timer/epicsTimer.cpp | |||
2129 | +++ b/modules/libcom/src/timer/epicsTimer.cpp | |||
2130 | @@ -1,4 +1,6 @@ | |||
2131 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
2132 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
2133 | 3 | * National Laboratory | ||
2134 | 2 | * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne | 4 | * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne |
2135 | 3 | * National Laboratory. | 5 | * National Laboratory. |
2136 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
2137 | @@ -12,151 +14,144 @@ | |||
2138 | 12 | * 505 665 1831 | 14 | * 505 665 1831 |
2139 | 13 | */ | 15 | */ |
2140 | 14 | 16 | ||
2141 | 17 | #include <cstdio> | ||
2142 | 15 | #include <string> | 18 | #include <string> |
2143 | 16 | #include <stdexcept> | 19 | #include <stdexcept> |
2144 | 17 | 20 | ||
2145 | 18 | #define epicsExportSharedSymbols | 21 | #define epicsExportSharedSymbols |
2146 | 19 | #include "epicsMath.h" | 22 | #include "epicsMath.h" |
2147 | 20 | #include "epicsTimer.h" | 23 | #include "epicsTimer.h" |
2148 | 21 | #include "epicsGuard.h" | ||
2149 | 22 | #include "timerPrivate.h" | 24 | #include "timerPrivate.h" |
2150 | 23 | 25 | ||
2151 | 24 | #ifdef _MSC_VER | ||
2152 | 25 | # pragma warning ( push ) | ||
2153 | 26 | # pragma warning ( disable:4660 ) | ||
2154 | 27 | #endif | ||
2155 | 28 | |||
2156 | 29 | #ifdef _MSC_VER | ||
2157 | 30 | # pragma warning ( pop ) | ||
2158 | 31 | #endif | ||
2159 | 32 | |||
2160 | 33 | template class tsFreeList < epicsTimerForC, 0x20 >; | ||
2161 | 34 | |||
2162 | 35 | epicsTimer::~epicsTimer () {} | 26 | epicsTimer::~epicsTimer () {} |
2163 | 36 | 27 | ||
2165 | 37 | epicsTimerQueueNotify::~epicsTimerQueueNotify () {} | 28 | void epicsTimerNotify :: show ( unsigned /* level */ ) const {} |
2166 | 38 | 29 | ||
2173 | 39 | epicsTimerNotify::~epicsTimerNotify () {} | 30 | TimerForC :: TimerForC ( timerQueue & queue, epicsTimerCallback const pCB, |
2174 | 40 | 31 | void * const pPrivate ) : | |
2175 | 41 | void epicsTimerNotify::show ( unsigned /* level */ ) const {} | 32 | m_pTimer ( & queue.createTimerImpl () ), |
2176 | 42 | 33 | m_pCallBack ( pCB ), | |
2177 | 43 | epicsTimerForC::epicsTimerForC ( timerQueue &queue, epicsTimerCallback pCBIn, void *pPrivateIn ) : | 34 | m_pPrivate ( pPrivate ) |
2172 | 44 | timer ( queue ), pCallBack ( pCBIn ), pPrivate ( pPrivateIn ) | ||
2178 | 45 | { | 35 | { |
2179 | 46 | } | 36 | } |
2180 | 47 | 37 | ||
2182 | 48 | epicsTimerForC::~epicsTimerForC () | 38 | TimerForC :: ~TimerForC () |
2183 | 49 | { | 39 | { |
2184 | 40 | m_pTimer->destroy (); | ||
2185 | 50 | } | 41 | } |
2186 | 51 | 42 | ||
2188 | 52 | void epicsTimerForC::destroy () | 43 | void TimerForC :: show ( unsigned level ) const |
2189 | 53 | { | 44 | { |
2193 | 54 | timerQueue & queueTmp = this->queue; | 45 | printf ( "TimerForC: callback ptr %p private ptr %p\n", |
2194 | 55 | this->~epicsTimerForC (); | 46 | m_pCallBack, m_pPrivate ); |
2195 | 56 | queueTmp.timerForCFreeList.release ( this ); | 47 | if ( level > 1 && m_pTimer ) { |
2196 | 48 | m_pTimer->show ( level - 1 ); | ||
2197 | 49 | } | ||
2198 | 57 | } | 50 | } |
2199 | 58 | 51 | ||
2201 | 59 | epicsTimerNotify::expireStatus epicsTimerForC::expire ( const epicsTime & ) | 52 | epicsTimerNotify :: expireStatus |
2202 | 53 | TimerForC :: expire ( const epicsTime & ) | ||
2203 | 60 | { | 54 | { |
2205 | 61 | ( *this->pCallBack ) ( this->pPrivate ); | 55 | ( *m_pCallBack ) ( m_pPrivate ); |
2206 | 62 | return noRestart; | 56 | return noRestart; |
2207 | 63 | } | 57 | } |
2208 | 64 | 58 | ||
2209 | 65 | epicsTimerQueueActiveForC :: | 59 | epicsTimerQueueActiveForC :: |
2213 | 66 | epicsTimerQueueActiveForC ( RefMgr & refMgr, | 60 | epicsTimerQueueActiveForC ( bool okToShare, unsigned priority ) : |
2214 | 67 | bool okToShare, unsigned priority ) : | 61 | timerQueueActive ( okToShare, priority ) |
2212 | 68 | timerQueueActive ( refMgr, okToShare, priority ) | ||
2215 | 69 | { | 62 | { |
2216 | 70 | timerQueueActive::start(); | ||
2217 | 71 | } | 63 | } |
2218 | 72 | 64 | ||
2220 | 73 | epicsTimerQueueActiveForC::~epicsTimerQueueActiveForC () | 65 | epicsTimerQueueActiveForC :: ~epicsTimerQueueActiveForC () |
2221 | 74 | { | 66 | { |
2222 | 75 | } | 67 | } |
2223 | 76 | 68 | ||
2225 | 77 | void epicsTimerQueueActiveForC::release () | 69 | void epicsTimerQueueActiveForC :: release () |
2226 | 78 | { | 70 | { |
2228 | 79 | _refMgr->release ( *this ); | 71 | timerQueueActiveMgr :: master ().release ( *this ); |
2229 | 80 | } | 72 | } |
2230 | 81 | 73 | ||
2239 | 82 | epicsTimerQueuePassiveForC::epicsTimerQueuePassiveForC ( | 74 | epicsTimerQueuePassiveForC :: epicsTimerQueuePassiveForC ( |
2240 | 83 | epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, | 75 | epicsTimerQueueNotifyReschedule pRescheduleCallback, |
2241 | 84 | epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, | 76 | epicsTimerQueueNotifyQuantum pSleepQuantumCallback, |
2242 | 85 | void * pPrivateIn ) : | 77 | void * pPrivate ) : |
2243 | 86 | timerQueuePassive ( * static_cast < epicsTimerQueueNotify * > ( this ) ), | 78 | timerQueuePassive ( |
2244 | 87 | pRescheduleCallback ( pRescheduleCallbackIn ), | 79 | * static_cast < epicsTimerQueueNotify * > ( this ) ), |
2245 | 88 | pSleepQuantumCallback ( pSleepQuantumCallbackIn ), | 80 | m_pRescheduleCallback ( pRescheduleCallback ), |
2246 | 89 | pPrivate ( pPrivateIn ) | 81 | m_pSleepQuantumCallback ( pSleepQuantumCallback ), |
2247 | 82 | m_pPrivate ( pPrivate ) | ||
2248 | 90 | { | 83 | { |
2249 | 91 | } | 84 | } |
2250 | 92 | 85 | ||
2252 | 93 | epicsTimerQueuePassiveForC::~epicsTimerQueuePassiveForC () | 86 | epicsTimerQueuePassiveForC :: ~epicsTimerQueuePassiveForC () |
2253 | 94 | { | 87 | { |
2254 | 95 | } | 88 | } |
2255 | 96 | 89 | ||
2257 | 97 | void epicsTimerQueuePassiveForC::reschedule () | 90 | void epicsTimerQueuePassiveForC :: reschedule () |
2258 | 98 | { | 91 | { |
2260 | 99 | (*this->pRescheduleCallback) ( this->pPrivate ); | 92 | ( *m_pRescheduleCallback ) ( m_pPrivate ); |
2261 | 100 | } | 93 | } |
2262 | 101 | 94 | ||
2269 | 102 | double epicsTimerQueuePassiveForC::quantum () | 95 | void epicsTimerQueuePassiveForC :: destroy () |
2264 | 103 | { | ||
2265 | 104 | return (*this->pSleepQuantumCallback) ( this->pPrivate ); | ||
2266 | 105 | } | ||
2267 | 106 | |||
2268 | 107 | void epicsTimerQueuePassiveForC::destroy () | ||
2270 | 108 | { | 96 | { |
2271 | 109 | delete this; | 97 | delete this; |
2272 | 110 | } | 98 | } |
2273 | 111 | 99 | ||
2276 | 112 | epicsShareFunc epicsTimerNotify::expireStatus::expireStatus ( restart_t restart ) : | 100 | epicsShareFunc epicsTimerNotify :: expireStatus :: |
2277 | 113 | delay ( - DBL_MAX ) | 101 | expireStatus ( restart_t restart ) : |
2278 | 102 | m_delay ( -DBL_MAX ) | ||
2279 | 114 | { | 103 | { |
2280 | 115 | if ( restart != noRestart ) { | 104 | if ( restart != noRestart ) { |
2281 | 116 | throw std::logic_error | 105 | throw std::logic_error |
2283 | 117 | ( "timer restart was requested without specifying a delay?" ); | 106 | ( "timer restart was requested " |
2284 | 107 | "without specifying a delay?" ); | ||
2285 | 118 | } | 108 | } |
2286 | 119 | } | 109 | } |
2287 | 120 | 110 | ||
2291 | 121 | epicsShareFunc epicsTimerNotify::expireStatus::expireStatus | 111 | epicsShareFunc epicsTimerNotify :: expireStatus :: expireStatus |
2292 | 122 | ( restart_t restartIn, const double & expireDelaySec ) : | 112 | ( restart_t restartIn, const double & expireDelaySec ) : |
2293 | 123 | delay ( expireDelaySec ) | 113 | m_delay ( expireDelaySec ) |
2294 | 124 | { | 114 | { |
2296 | 125 | if ( restartIn != epicsTimerNotify::restart ) { | 115 | if ( restartIn != epicsTimerNotify :: restart ) { |
2297 | 126 | throw std::logic_error | 116 | throw std::logic_error |
2299 | 127 | ( "no timer restart was requested, but a delay was specified?" ); | 117 | ( "no timer restart was requested, " |
2300 | 118 | "but a delay was specified?" ); | ||
2301 | 128 | } | 119 | } |
2303 | 129 | if ( this->delay < 0.0 || !finite(this->delay) ) { | 120 | if ( m_delay < 0.0 || ! finite ( m_delay ) ) { |
2304 | 130 | throw std::logic_error | 121 | throw std::logic_error |
2306 | 131 | ( "timer restart was requested, but a negative delay was specified?" ); | 122 | ( "timer restart was requested, but a " |
2307 | 123 | "negative delay was specified?" ); | ||
2308 | 132 | } | 124 | } |
2309 | 133 | } | 125 | } |
2310 | 134 | 126 | ||
2312 | 135 | epicsShareFunc bool epicsTimerNotify::expireStatus::restart () const | 127 | epicsShareFunc bool |
2313 | 128 | epicsTimerNotify :: expireStatus :: restart () const | ||
2314 | 136 | { | 129 | { |
2316 | 137 | return this->delay >= 0.0 && finite(this->delay); | 130 | return m_delay >= 0.0 && finite ( m_delay ); |
2317 | 138 | } | 131 | } |
2318 | 139 | 132 | ||
2320 | 140 | epicsShareFunc double epicsTimerNotify::expireStatus::expirationDelay () const | 133 | epicsShareFunc double |
2321 | 134 | epicsTimerNotify :: expireStatus :: expirationDelay () const | ||
2322 | 141 | { | 135 | { |
2324 | 142 | if ( this->delay < 0.0 || !finite(this->delay) ) { | 136 | if ( m_delay < 0.0 || ! finite ( m_delay ) ) { |
2325 | 143 | throw std::logic_error | 137 | throw std::logic_error |
2327 | 144 | ( "no timer restart was requested, but you are asking for a restart delay?" ); | 138 | ( "no timer restart was requested, " |
2328 | 139 | "but you are asking for a restart delay?" ); | ||
2329 | 145 | } | 140 | } |
2331 | 146 | return this->delay; | 141 | return m_delay; |
2332 | 147 | } | 142 | } |
2333 | 148 | 143 | ||
2334 | 149 | extern "C" epicsTimerQueuePassiveId epicsShareAPI | 144 | extern "C" epicsTimerQueuePassiveId epicsShareAPI |
2335 | 150 | epicsTimerQueuePassiveCreate ( | 145 | epicsTimerQueuePassiveCreate ( |
2339 | 151 | epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, | 146 | const epicsTimerQueueNotifyReschedule pRescheduleCallback, |
2340 | 152 | epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, | 147 | const epicsTimerQueueNotifyQuantum pSleepQuantumCallback, |
2341 | 153 | void * pPrivateIn ) | 148 | void * const pPrivate ) |
2342 | 154 | { | 149 | { |
2343 | 155 | try { | 150 | try { |
2344 | 156 | return new epicsTimerQueuePassiveForC ( | 151 | return new epicsTimerQueuePassiveForC ( |
2348 | 157 | pRescheduleCallbackIn, | 152 | pRescheduleCallback, |
2349 | 158 | pSleepQuantumCallbackIn, | 153 | pSleepQuantumCallback, |
2350 | 159 | pPrivateIn ); | 154 | pPrivate ); |
2351 | 160 | } | 155 | } |
2352 | 161 | catch ( ... ) { | 156 | catch ( ... ) { |
2353 | 162 | return 0; | 157 | return 0; |
2354 | @@ -173,28 +168,31 @@ extern "C" double epicsShareAPI | |||
2355 | 173 | epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue ) | 168 | epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue ) |
2356 | 174 | { | 169 | { |
2357 | 175 | try { | 170 | try { |
2359 | 176 | return pQueue->process ( epicsTime::getCurrent() ); | 171 | return pQueue->process ( epicsTime :: getCurrent () ); |
2360 | 177 | } | 172 | } |
2361 | 178 | catch ( ... ) { | 173 | catch ( ... ) { |
2362 | 179 | return 1.0; | 174 | return 1.0; |
2363 | 180 | } | 175 | } |
2364 | 181 | } | 176 | } |
2365 | 182 | 177 | ||
2368 | 183 | extern "C" epicsTimerId epicsShareAPI epicsTimerQueuePassiveCreateTimer ( | 178 | extern "C" epicsTimerId epicsShareAPI |
2369 | 184 | epicsTimerQueuePassiveId pQueue, epicsTimerCallback pCallback, void *pArg ) | 179 | epicsTimerQueuePassiveCreateTimer ( |
2370 | 180 | epicsTimerQueuePassiveId pQueue, | ||
2371 | 181 | epicsTimerCallback pCallback, void *pArg ) | ||
2372 | 185 | { | 182 | { |
2373 | 186 | try { | 183 | try { |
2375 | 187 | return & pQueue->createTimerForC ( pCallback, pArg ); | 184 | return & pQueue->createTimerForC ( pCallback, pArg ); |
2376 | 188 | } | 185 | } |
2377 | 189 | catch ( ... ) { | 186 | catch ( ... ) { |
2378 | 190 | return 0; | 187 | return 0; |
2379 | 191 | } | 188 | } |
2380 | 192 | } | 189 | } |
2381 | 193 | 190 | ||
2384 | 194 | extern "C" epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroyTimer ( | 191 | extern "C" epicsShareFunc void epicsShareAPI |
2385 | 195 | epicsTimerQueuePassiveId /* pQueue */, epicsTimerId pTmr ) | 192 | epicsTimerQueuePassiveDestroyTimer ( |
2386 | 193 | epicsTimerQueuePassiveId pQueue, epicsTimerId pTmr ) | ||
2387 | 196 | { | 194 | { |
2389 | 197 | pTmr->destroy (); | 195 | delete pTmr; |
2390 | 198 | } | 196 | } |
2391 | 199 | 197 | ||
2392 | 200 | extern "C" void epicsShareAPI epicsTimerQueuePassiveShow ( | 198 | extern "C" void epicsShareAPI epicsTimerQueuePassiveShow ( |
2393 | @@ -207,11 +205,10 @@ extern "C" epicsTimerQueueId epicsShareAPI | |||
2394 | 207 | epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ) | 205 | epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ) |
2395 | 208 | { | 206 | { |
2396 | 209 | try { | 207 | try { |
2397 | 210 | epicsSingleton < timerQueueActiveMgr > :: reference ref = | ||
2398 | 211 | timerQueueMgrEPICS.getReference (); | ||
2399 | 212 | epicsTimerQueueActiveForC & tmr = | 208 | epicsTimerQueueActiveForC & tmr = |
2402 | 213 | ref->allocate ( ref, okToShare ? true : false, threadPriority ); | 209 | timerQueueActiveMgr :: master (). |
2403 | 214 | return &tmr; | 210 | allocate ( okToShare ? true : false, threadPriority ); |
2404 | 211 | return & tmr; | ||
2405 | 215 | } | 212 | } |
2406 | 216 | catch ( ... ) { | 213 | catch ( ... ) { |
2407 | 217 | return 0; | 214 | return 0; |
2408 | @@ -223,8 +220,10 @@ extern "C" void epicsShareAPI epicsTimerQueueRelease ( epicsTimerQueueId pQueue | |||
2409 | 223 | pQueue->release (); | 220 | pQueue->release (); |
2410 | 224 | } | 221 | } |
2411 | 225 | 222 | ||
2414 | 226 | extern "C" epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( | 223 | extern "C" epicsTimerId epicsShareAPI |
2415 | 227 | epicsTimerQueueId pQueue, epicsTimerCallback pCallback, void *pArg ) | 224 | epicsTimerQueueCreateTimer ( |
2416 | 225 | epicsTimerQueueId pQueue, | ||
2417 | 226 | epicsTimerCallback pCallback, void *pArg ) | ||
2418 | 228 | { | 227 | { |
2419 | 229 | try { | 228 | try { |
2420 | 230 | return & pQueue->createTimerForC ( pCallback, pArg ); | 229 | return & pQueue->createTimerForC ( pCallback, pArg ); |
2421 | @@ -234,43 +233,49 @@ extern "C" epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( | |||
2422 | 234 | } | 233 | } |
2423 | 235 | } | 234 | } |
2424 | 236 | 235 | ||
2427 | 237 | extern "C" void epicsShareAPI epicsTimerQueueShow ( | 236 | extern "C" void epicsShareAPI |
2428 | 238 | epicsTimerQueueId pQueue, unsigned int level ) | 237 | epicsTimerQueueShow ( |
2429 | 238 | epicsTimerQueueId pQueue, unsigned int level ) | ||
2430 | 239 | { | 239 | { |
2431 | 240 | pQueue->show ( level ); | 240 | pQueue->show ( level ); |
2432 | 241 | } | 241 | } |
2433 | 242 | 242 | ||
2436 | 243 | extern "C" void epicsShareAPI epicsTimerQueueDestroyTimer ( | 243 | extern "C" void epicsShareAPI |
2437 | 244 | epicsTimerQueueId /* pQueue */, epicsTimerId pTmr ) | 244 | epicsTimerQueueDestroyTimer ( |
2438 | 245 | epicsTimerQueueId pQueue, epicsTimerId pTmr ) | ||
2439 | 245 | { | 246 | { |
2441 | 246 | pTmr->destroy (); | 247 | delete pTmr; |
2442 | 247 | } | 248 | } |
2443 | 248 | 249 | ||
2446 | 249 | extern "C" void epicsShareAPI epicsTimerStartTime ( | 250 | extern "C" unsigned epicsShareAPI |
2447 | 250 | epicsTimerId pTmr, const epicsTimeStamp *pTime ) | 251 | epicsTimerStartTime ( |
2448 | 252 | epicsTimerId pTmr, const epicsTimeStamp *pTime ) | ||
2449 | 251 | { | 253 | { |
2451 | 252 | pTmr->start ( *pTmr, *pTime ); | 254 | return pTmr->start ( *pTime ); |
2452 | 253 | } | 255 | } |
2453 | 254 | 256 | ||
2456 | 255 | extern "C" void epicsShareAPI epicsTimerStartDelay ( | 257 | extern "C" unsigned epicsShareAPI |
2457 | 256 | epicsTimerId pTmr, double delaySeconds ) | 258 | epicsTimerStartDelay ( |
2458 | 259 | epicsTimerId pTmr, double delaySeconds ) | ||
2459 | 257 | { | 260 | { |
2461 | 258 | pTmr->start ( *pTmr, delaySeconds ); | 261 | return pTmr->start ( delaySeconds ); |
2462 | 259 | } | 262 | } |
2463 | 260 | 263 | ||
2465 | 261 | extern "C" void epicsShareAPI epicsTimerCancel ( epicsTimerId pTmr ) | 264 | extern "C" int epicsShareAPI |
2466 | 265 | epicsTimerCancel ( epicsTimerId pTmr ) | ||
2467 | 262 | { | 266 | { |
2469 | 263 | pTmr->cancel (); | 267 | return pTmr->cancel (); |
2470 | 264 | } | 268 | } |
2471 | 265 | 269 | ||
2473 | 266 | extern "C" double epicsShareAPI epicsTimerGetExpireDelay ( epicsTimerId pTmr ) | 270 | extern "C" double epicsShareAPI |
2474 | 271 | epicsTimerGetExpireDelay ( epicsTimerId pTmr ) | ||
2475 | 267 | { | 272 | { |
2476 | 268 | return pTmr->getExpireDelay (); | 273 | return pTmr->getExpireDelay (); |
2477 | 269 | } | 274 | } |
2478 | 270 | 275 | ||
2481 | 271 | extern "C" void epicsShareAPI epicsTimerShow ( | 276 | extern "C" void epicsShareAPI |
2482 | 272 | epicsTimerId pTmr, unsigned int level ) | 277 | epicsTimerShow ( epicsTimerId pTmr, unsigned int level ) |
2483 | 273 | { | 278 | { |
2485 | 274 | pTmr->timer::show ( level ); | 279 | pTmr->show ( level ); |
2486 | 275 | } | 280 | } |
2487 | 276 | 281 | ||
2488 | diff --git a/modules/libcom/src/timer/epicsTimer.h b/modules/libcom/src/timer/epicsTimer.h | |||
2489 | index 72270f2..45c6b29 100644 | |||
2490 | --- a/modules/libcom/src/timer/epicsTimer.h | |||
2491 | +++ b/modules/libcom/src/timer/epicsTimer.h | |||
2492 | @@ -1,11 +1,12 @@ | |||
2493 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
2494 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
2495 | 3 | * National Laboratory | ||
2496 | 2 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne | 4 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
2497 | 3 | * National Laboratory. | 5 | * National Laboratory. |
2498 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
2499 | 5 | * Operator of Los Alamos National Laboratory. | 7 | * Operator of Los Alamos National Laboratory. |
2503 | 6 | * EPICS BASE Versions 3.13.7 | 8 | * EPICS BASE is distributed subject to a Software License Agreement found |
2504 | 7 | * and higher are distributed subject to a Software License Agreement found | 9 | * in file LICENSE that is included with this distribution. |
2502 | 8 | * in file LICENSE that is included with this distribution. | ||
2505 | 9 | \*************************************************************************/ | 10 | \*************************************************************************/ |
2506 | 10 | /* epicsTimer.h */ | 11 | /* epicsTimer.h */ |
2507 | 11 | 12 | ||
2508 | @@ -14,50 +15,80 @@ | |||
2509 | 14 | #ifndef epicsTimerH | 15 | #ifndef epicsTimerH |
2510 | 15 | #define epicsTimerH | 16 | #define epicsTimerH |
2511 | 16 | 17 | ||
2512 | 17 | #include <float.h> | ||
2513 | 18 | |||
2514 | 19 | #include "shareLib.h" | ||
2515 | 20 | #include "epicsTime.h" | 18 | #include "epicsTime.h" |
2516 | 21 | #include "epicsThread.h" | 19 | #include "epicsThread.h" |
2517 | 20 | #include "shareLib.h" | ||
2518 | 22 | 21 | ||
2519 | 23 | #ifdef __cplusplus | 22 | #ifdef __cplusplus |
2520 | 24 | 23 | ||
2521 | 24 | #include <cfloat> | ||
2522 | 25 | |||
2523 | 25 | /* | 26 | /* |
2524 | 26 | * Notes: | 27 | * Notes: |
2526 | 27 | * 1) epicsTimer does not hold its lock when calling callbacks. | 28 | * 1) The timer queue process method does not hold the timer |
2527 | 29 | * queue lock when calling callbacks, to avoid deadlocks. | ||
2528 | 30 | * | ||
2529 | 31 | * 2) The timer start method has three different possible outcomes | ||
2530 | 32 | * | ||
2531 | 33 | * 2a) If start is called and the timer isnt pending in the timer | ||
2532 | 34 | * queue, and the timer callback isnt currently being orchestrated, | ||
2533 | 35 | * then the timer is schedualed in the queue and start returns | ||
2534 | 36 | * 1u, indicating that the timer callback will run once as a | ||
2535 | 37 | * direct consequence of this invocation of start. | ||
2536 | 38 | * | ||
2537 | 39 | * 2b) If start is called and the timer is already pending in | ||
2538 | 40 | * the timer queue, then the timer is reschedualed back into | ||
2539 | 41 | * a new positon in the queue and start returns 0u. The | ||
2540 | 42 | * timer callback will run once as a reschedualed (postponed) | ||
2541 | 43 | * consequence of a previous invocation of start, but zero | ||
2542 | 44 | * times as a direct consequence of this call to start. | ||
2543 | 45 | * | ||
2544 | 46 | * 2c) If start is called and the timer isnt pending in the timer | ||
2545 | 47 | * queue, but the timer callback _is_ currently being orchestrated | ||
2546 | 48 | * then it is reschedualed in the queue for a new expiration | ||
2547 | 49 | * time, and start returns 1u. The timer callback will run twice. | ||
2548 | 50 | * Once as a consequence of this invocation of start, and once | ||
2549 | 51 | * as a consequence of a previous call to start. | ||
2550 | 52 | * | ||
2551 | 53 | * 3) Cancel returns true if the timer was pending in the queue | ||
2552 | 54 | * when cancel was called, and false otherwise. | ||
2553 | 28 | */ | 55 | */ |
2554 | 29 | 56 | ||
2555 | 30 | /* code using a timer must implement epicsTimerNotify */ | 57 | /* code using a timer must implement epicsTimerNotify */ |
2556 | 31 | class epicsShareClass epicsTimerNotify { | 58 | class epicsShareClass epicsTimerNotify { |
2557 | 32 | public: | 59 | public: |
2558 | 33 | enum restart_t { noRestart, restart }; | 60 | enum restart_t { noRestart, restart }; |
2560 | 34 | class expireStatus { | 61 | class epicsShareClass expireStatus { |
2561 | 35 | public: | 62 | public: |
2566 | 36 | epicsShareFunc expireStatus ( restart_t ); | 63 | expireStatus ( restart_t ); |
2567 | 37 | epicsShareFunc expireStatus ( restart_t, const double & expireDelaySec ); | 64 | expireStatus ( restart_t, const double & expireDelaySec ); |
2568 | 38 | epicsShareFunc bool restart () const; | 65 | bool restart () const; |
2569 | 39 | epicsShareFunc double expirationDelay () const; | 66 | double expirationDelay () const; |
2570 | 40 | private: | 67 | private: |
2572 | 41 | double delay; | 68 | double m_delay; |
2573 | 42 | }; | 69 | }; |
2574 | 43 | 70 | ||
2575 | 44 | virtual ~epicsTimerNotify () = 0; | ||
2576 | 45 | /* return "noRestart" or "expireStatus ( restart, 30.0 )" */ | 71 | /* return "noRestart" or "expireStatus ( restart, 30.0 )" */ |
2577 | 46 | virtual expireStatus expire ( const epicsTime & currentTime ) = 0; | 72 | virtual expireStatus expire ( const epicsTime & currentTime ) = 0; |
2578 | 47 | virtual void show ( unsigned int level ) const; | 73 | virtual void show ( unsigned int level ) const; |
2579 | 74 | protected: | ||
2580 | 75 | virtual ~epicsTimerNotify () {} // protected disables delete through intf | ||
2581 | 48 | }; | 76 | }; |
2582 | 49 | 77 | ||
2583 | 50 | class epicsShareClass epicsTimer { | 78 | class epicsShareClass epicsTimer { |
2584 | 51 | public: | 79 | public: |
2585 | 52 | /* calls cancel (see warning below) and then destroys the timer */ | 80 | /* calls cancel (see warning below) and then destroys the timer */ |
2586 | 53 | virtual void destroy () = 0; | 81 | virtual void destroy () = 0; |
2590 | 54 | virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0; | 82 | /* see note 2 above for the significance of the return value */ |
2591 | 55 | virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0; | 83 | virtual unsigned start ( epicsTimerNotify &, const epicsTime & ) = 0; |
2592 | 56 | /* WARNING: A deadlock will occur if you hold a lock while | 84 | virtual unsigned start ( epicsTimerNotify &, double delaySeconds ) = 0; |
2593 | 85 | /* see note 3 for the significance of the return value | ||
2594 | 86 | * | ||
2595 | 87 | * WARNING: A deadlock will occur if you hold a lock while | ||
2596 | 57 | * calling this function that you also take within the timer | 88 | * calling this function that you also take within the timer |
2597 | 58 | * expiration callback. | 89 | * expiration callback. |
2598 | 59 | */ | 90 | */ |
2600 | 60 | virtual void cancel () = 0; | 91 | virtual bool cancel () = 0; |
2601 | 61 | struct expireInfo { | 92 | struct expireInfo { |
2602 | 62 | expireInfo ( bool active, const epicsTime & expireTime ); | 93 | expireInfo ( bool active, const epicsTime & expireTime ); |
2603 | 63 | bool active; | 94 | bool active; |
2604 | @@ -67,7 +98,7 @@ public: | |||
2605 | 67 | double getExpireDelay (); | 98 | double getExpireDelay (); |
2606 | 68 | virtual void show ( unsigned int level ) const = 0; | 99 | virtual void show ( unsigned int level ) const = 0; |
2607 | 69 | protected: | 100 | protected: |
2609 | 70 | virtual ~epicsTimer () = 0; /* protected => delete() must not be called */ | 101 | virtual ~epicsTimer () = 0; /* disable delete */ |
2610 | 71 | }; | 102 | }; |
2611 | 72 | 103 | ||
2612 | 73 | class epicsTimerQueue { | 104 | class epicsTimerQueue { |
2613 | @@ -75,7 +106,7 @@ public: | |||
2614 | 75 | virtual epicsTimer & createTimer () = 0; | 106 | virtual epicsTimer & createTimer () = 0; |
2615 | 76 | virtual void show ( unsigned int level ) const = 0; | 107 | virtual void show ( unsigned int level ) const = 0; |
2616 | 77 | protected: | 108 | protected: |
2618 | 78 | epicsShareFunc virtual ~epicsTimerQueue () = 0; | 109 | virtual ~epicsTimerQueue () {} /* disable delete */ |
2619 | 79 | }; | 110 | }; |
2620 | 80 | 111 | ||
2621 | 81 | class epicsTimerQueueActive | 112 | class epicsTimerQueueActive |
2622 | @@ -85,7 +116,7 @@ public: | |||
2623 | 85 | bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 ); | 116 | bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 ); |
2624 | 86 | virtual void release () = 0; | 117 | virtual void release () = 0; |
2625 | 87 | protected: | 118 | protected: |
2627 | 88 | epicsShareFunc virtual ~epicsTimerQueueActive () = 0; | 119 | virtual ~epicsTimerQueueActive () {} /* disable delete */ |
2628 | 89 | }; | 120 | }; |
2629 | 90 | 121 | ||
2630 | 91 | class epicsTimerQueueNotify { | 122 | class epicsTimerQueueNotify { |
2631 | @@ -93,18 +124,14 @@ public: | |||
2632 | 93 | /* called when a new timer is inserted into the queue and the */ | 124 | /* called when a new timer is inserted into the queue and the */ |
2633 | 94 | /* delay to the next expire has changed */ | 125 | /* delay to the next expire has changed */ |
2634 | 95 | virtual void reschedule () = 0; | 126 | virtual void reschedule () = 0; |
2635 | 96 | /* if there is a quantum in the scheduling of timer intervals */ | ||
2636 | 97 | /* return this quantum in seconds. If unknown then return zero. */ | ||
2637 | 98 | virtual double quantum () = 0; | ||
2638 | 99 | protected: | 127 | protected: |
2640 | 100 | epicsShareFunc virtual ~epicsTimerQueueNotify () = 0; | 128 | virtual ~epicsTimerQueueNotify () {} /* disable delete */ |
2641 | 101 | }; | 129 | }; |
2642 | 102 | 130 | ||
2645 | 103 | class epicsTimerQueuePassive | 131 | class epicsTimerQueuePassive : public epicsTimerQueue { |
2644 | 104 | : public epicsTimerQueue { | ||
2646 | 105 | public: | 132 | public: |
2647 | 106 | static epicsShareFunc epicsTimerQueuePassive & create ( epicsTimerQueueNotify & ); | 133 | static epicsShareFunc epicsTimerQueuePassive & create ( epicsTimerQueueNotify & ); |
2649 | 107 | epicsShareFunc virtual ~epicsTimerQueuePassive () = 0; /* ok to call delete */ | 134 | virtual ~epicsTimerQueuePassive () {} /* ok to call delete */ |
2650 | 108 | virtual double process ( const epicsTime & currentTime ) = 0; /* returns delay to next expire */ | 135 | virtual double process ( const epicsTime & currentTime ) = 0; /* returns delay to next expire */ |
2651 | 109 | }; | 136 | }; |
2652 | 110 | 137 | ||
2653 | @@ -114,11 +141,11 @@ inline epicsTimer::expireInfo::expireInfo ( bool activeIn, | |||
2654 | 114 | { | 141 | { |
2655 | 115 | } | 142 | } |
2656 | 116 | 143 | ||
2658 | 117 | inline double epicsTimer::getExpireDelay () | 144 | inline double epicsTimer :: getExpireDelay () |
2659 | 118 | { | 145 | { |
2660 | 119 | epicsTimer::expireInfo info = this->getExpireInfo (); | 146 | epicsTimer::expireInfo info = this->getExpireInfo (); |
2661 | 120 | if ( info.active ) { | 147 | if ( info.active ) { |
2663 | 121 | double delay = info.expireTime - epicsTime::getCurrent (); | 148 | double delay = info.expireTime - epicsTime :: getCurrent (); |
2664 | 122 | if ( delay < 0.0 ) { | 149 | if ( delay < 0.0 ) { |
2665 | 123 | delay = 0.0; | 150 | delay = 0.0; |
2666 | 124 | } | 151 | } |
2667 | @@ -130,7 +157,7 @@ inline double epicsTimer::getExpireDelay () | |||
2668 | 130 | extern "C" { | 157 | extern "C" { |
2669 | 131 | #endif /* __cplusplus */ | 158 | #endif /* __cplusplus */ |
2670 | 132 | 159 | ||
2672 | 133 | typedef struct epicsTimerForC * epicsTimerId; | 160 | typedef struct TimerForC * epicsTimerId; |
2673 | 134 | typedef void ( *epicsTimerCallback ) ( void *pPrivate ); | 161 | typedef void ( *epicsTimerCallback ) ( void *pPrivate ); |
2674 | 135 | 162 | ||
2675 | 136 | /* thread managed timer queue */ | 163 | /* thread managed timer queue */ |
2676 | @@ -167,11 +194,11 @@ epicsShareFunc void epicsShareAPI | |||
2677 | 167 | epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId id, unsigned int level ); | 194 | epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId id, unsigned int level ); |
2678 | 168 | 195 | ||
2679 | 169 | /* timer */ | 196 | /* timer */ |
2681 | 170 | epicsShareFunc void epicsShareAPI | 197 | epicsShareFunc unsigned epicsShareAPI |
2682 | 171 | epicsTimerStartTime ( epicsTimerId id, const epicsTimeStamp *pTime ); | 198 | epicsTimerStartTime ( epicsTimerId id, const epicsTimeStamp *pTime ); |
2684 | 172 | epicsShareFunc void epicsShareAPI | 199 | epicsShareFunc unsigned epicsShareAPI |
2685 | 173 | epicsTimerStartDelay ( epicsTimerId id, double delaySeconds ); | 200 | epicsTimerStartDelay ( epicsTimerId id, double delaySeconds ); |
2687 | 174 | epicsShareFunc void epicsShareAPI | 201 | epicsShareFunc int epicsShareAPI |
2688 | 175 | epicsTimerCancel ( epicsTimerId id ); | 202 | epicsTimerCancel ( epicsTimerId id ); |
2689 | 176 | epicsShareFunc double epicsShareAPI | 203 | epicsShareFunc double epicsShareAPI |
2690 | 177 | epicsTimerGetExpireDelay ( epicsTimerId id ); | 204 | epicsTimerGetExpireDelay ( epicsTimerId id ); |
2691 | diff --git a/modules/libcom/src/timer/timer.cpp b/modules/libcom/src/timer/timer.cpp | |||
2692 | index 35d6e47..2c49f7e 100644 | |||
2693 | --- a/modules/libcom/src/timer/timer.cpp | |||
2694 | +++ b/modules/libcom/src/timer/timer.cpp | |||
2695 | @@ -1,11 +1,12 @@ | |||
2696 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
2697 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
2698 | 3 | * National Laboratory | ||
2699 | 2 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne | 4 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
2700 | 3 | * National Laboratory. | 5 | * National Laboratory. |
2701 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
2702 | 5 | * Operator of Los Alamos National Laboratory. | 7 | * Operator of Los Alamos National Laboratory. |
2706 | 6 | * EPICS BASE Versions 3.13.7 | 8 | * EPICS BASE is distributed subject to a Software License Agreement found |
2707 | 7 | * and higher are distributed subject to a Software License Agreement found | 9 | * in file LICENSE that is included with this distribution. |
2705 | 8 | * in file LICENSE that is included with this distribution. | ||
2708 | 9 | \*************************************************************************/ | 10 | \*************************************************************************/ |
2709 | 10 | 11 | ||
2710 | 11 | /* | 12 | /* |
2711 | @@ -17,227 +18,226 @@ | |||
2712 | 17 | #include <typeinfo> | 18 | #include <typeinfo> |
2713 | 18 | #include <string> | 19 | #include <string> |
2714 | 19 | #include <stdexcept> | 20 | #include <stdexcept> |
2716 | 20 | #include <stdio.h> | 21 | #include <cstdio> |
2717 | 21 | 22 | ||
2718 | 22 | #define epicsExportSharedSymbols | 23 | #define epicsExportSharedSymbols |
2719 | 23 | #include "epicsGuard.h" | ||
2720 | 24 | #include "timerPrivate.h" | 24 | #include "timerPrivate.h" |
2721 | 25 | #include "errlog.h" | 25 | #include "errlog.h" |
2722 | 26 | 26 | ||
2736 | 27 | #ifdef _MSC_VER | 27 | Timer :: Timer ( timerQueue & queueIn ) : |
2737 | 28 | # pragma warning ( push ) | 28 | m_queue ( queueIn ), |
2738 | 29 | # pragma warning ( disable:4660 ) | 29 | m_curState ( stateLimbo ), |
2739 | 30 | #endif | 30 | m_pNotify ( 0 ), |
2740 | 31 | 31 | m_index ( m_invalidIndex ) | |
2728 | 32 | template class tsFreeList < timer, 0x20 >; | ||
2729 | 33 | |||
2730 | 34 | #ifdef _MSC_VER | ||
2731 | 35 | # pragma warning ( pop ) | ||
2732 | 36 | #endif | ||
2733 | 37 | |||
2734 | 38 | timer::timer ( timerQueue & queueIn ) : | ||
2735 | 39 | queue ( queueIn ), curState ( stateLimbo ), pNotify ( 0 ) | ||
2741 | 40 | { | 32 | { |
2742 | 41 | } | 33 | } |
2743 | 42 | 34 | ||
2745 | 43 | timer::~timer () | 35 | Timer :: ~Timer () |
2746 | 44 | { | 36 | { |
2748 | 45 | this->cancel (); | 37 | M_CancelStatus cs = { false, false }; |
2749 | 38 | { | ||
2750 | 39 | Guard guard ( m_queue ); | ||
2751 | 40 | cs = m_cancelPvt ( guard ); | ||
2752 | 41 | m_queue.m_numTimers--; | ||
2753 | 42 | } | ||
2754 | 43 | // we are careful to wakeup the timer queue thread after | ||
2755 | 44 | // we nolonger hold the lock | ||
2756 | 45 | if ( cs.reschedule ) { | ||
2757 | 46 | m_queue.m_notify.reschedule (); | ||
2758 | 47 | } | ||
2759 | 46 | } | 48 | } |
2760 | 47 | 49 | ||
2762 | 48 | void timer::destroy () | 50 | void Timer :: destroy () |
2763 | 49 | { | 51 | { |
2767 | 50 | timerQueue & queueTmp = this->queue; | 52 | delete this; |
2765 | 51 | this->~timer (); | ||
2766 | 52 | queueTmp.timerFreeList.release ( this ); | ||
2768 | 53 | } | 53 | } |
2769 | 54 | 54 | ||
2771 | 55 | void timer::start ( epicsTimerNotify & notify, double delaySeconds ) | 55 | unsigned Timer :: start ( epicsTimerNotify & notify, double delaySeconds ) |
2772 | 56 | { | 56 | { |
2774 | 57 | this->start ( notify, epicsTime::getCurrent () + delaySeconds ); | 57 | const epicsTime current = epicsTime :: getCurrent (); |
2775 | 58 | const epicsTime exp = current + delaySeconds; | ||
2776 | 59 | const Timer :: M_StartReturn sr = m_privateStart ( notify, exp ); | ||
2777 | 60 | // we are careful to wakeup the timer queue thread after | ||
2778 | 61 | // we nolonger hold the lock | ||
2779 | 62 | if ( sr.resched ) { | ||
2780 | 63 | m_queue.m_notify.reschedule (); | ||
2781 | 64 | } | ||
2782 | 65 | return sr.numNew; | ||
2783 | 58 | } | 66 | } |
2784 | 59 | 67 | ||
2786 | 60 | void timer::start ( epicsTimerNotify & notify, const epicsTime & expire ) | 68 | unsigned Timer :: start ( epicsTimerNotify & notify, |
2787 | 69 | const epicsTime & expire ) | ||
2788 | 61 | { | 70 | { |
2791 | 62 | epicsGuard < epicsMutex > locker ( this->queue.mutex ); | 71 | Timer :: M_StartReturn sr = m_privateStart ( notify, expire ); |
2792 | 63 | this->privateStart ( notify, expire ); | 72 | // we are careful to wakeup the timer queue thread after |
2793 | 73 | // we nolonger hold the lock | ||
2794 | 74 | if ( sr.resched ) { | ||
2795 | 75 | m_queue.m_notify.reschedule (); | ||
2796 | 76 | } | ||
2797 | 77 | return sr.numNew; | ||
2798 | 64 | } | 78 | } |
2799 | 65 | 79 | ||
2801 | 66 | void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire ) | 80 | Timer :: M_StartReturn |
2802 | 81 | Timer :: m_privateStart ( epicsTimerNotify & notify, | ||
2803 | 82 | const epicsTime & expire ) | ||
2804 | 67 | { | 83 | { |
2819 | 68 | this->pNotify = & notify; | 84 | Timer :: M_StartReturn sr; |
2820 | 69 | this->exp = expire - ( this->queue.notify.quantum () / 2.0 ); | 85 | Guard locker ( m_queue ); |
2821 | 70 | 86 | m_pNotify = & notify; | |
2822 | 71 | bool reschedualNeeded = false; | 87 | if ( m_curState == statePending ) { |
2823 | 72 | if ( this->curState == stateActive ) { | 88 | const epicsTime oldExp = m_queue.m_heap.front ()->m_exp; |
2824 | 73 | // above expire time and notify will override any restart parameters | 89 | m_exp = expire; |
2825 | 74 | // that may be returned from the timer expire callback | 90 | if ( ! m_queue.m_fixParent ( m_index ) ) { |
2826 | 75 | return; | 91 | m_queue.m_fixChildren ( m_index ); |
2827 | 76 | } | 92 | } |
2828 | 77 | else if ( this->curState == statePending ) { | 93 | if ( m_queue.m_pExpTmr == this ) { |
2829 | 78 | this->queue.timerList.remove ( *this ); | 94 | // new expire time and notify will override |
2830 | 79 | if ( this->queue.timerList.first() == this && | 95 | // any restart parameters that may be returned |
2831 | 80 | this->queue.timerList.count() > 0 ) { | 96 | // from the timer expire callback |
2832 | 81 | reschedualNeeded = true; | 97 | sr.numNew = 1u; |
2833 | 98 | sr.resched = false; | ||
2834 | 99 | } | ||
2835 | 100 | else { | ||
2836 | 101 | sr.numNew = 0u; | ||
2837 | 102 | sr.resched = ( oldExp > m_queue.m_heap.front ()->m_exp ); | ||
2838 | 82 | } | 103 | } |
2839 | 83 | } | 104 | } |
2861 | 84 | 105 | else { | |
2862 | 85 | # ifdef DEBUG | 106 | sr.numNew = 1u; |
2863 | 86 | unsigned preemptCount=0u; | 107 | m_curState = Timer :: statePending; |
2864 | 87 | # endif | 108 | m_index = m_queue.m_heap.size (); |
2865 | 88 | 109 | if ( m_index > 0u ) { | |
2866 | 89 | // | 110 | const epicsTime oldExp = m_queue.m_heap.front ()->m_exp; |
2867 | 90 | // insert into the pending queue | 111 | m_exp = expire; |
2868 | 91 | // | 112 | m_queue.m_heap.push_back ( this ); |
2869 | 92 | // Finds proper time sorted location using a linear search. | 113 | m_queue.m_fixParent ( m_index ); |
2870 | 93 | // | 114 | sr.resched = ( oldExp > m_queue.m_heap.front ()->m_exp ); |
2850 | 94 | // **** this should use a binary tree ???? | ||
2851 | 95 | // | ||
2852 | 96 | tsDLIter < timer > pTmr = this->queue.timerList.lastIter (); | ||
2853 | 97 | while ( true ) { | ||
2854 | 98 | if ( ! pTmr.valid () ) { | ||
2855 | 99 | // | ||
2856 | 100 | // add to the beginning of the list | ||
2857 | 101 | // | ||
2858 | 102 | this->queue.timerList.push ( *this ); | ||
2859 | 103 | reschedualNeeded = true; | ||
2860 | 104 | break; | ||
2871 | 105 | } | 115 | } |
2878 | 106 | if ( pTmr->exp <= this->exp ) { | 116 | else { |
2879 | 107 | // | 117 | m_exp = expire; |
2880 | 108 | // add after the item found that expires earlier | 118 | m_queue.m_heap.push_back ( this ); |
2881 | 109 | // | 119 | sr.resched = true; |
2876 | 110 | this->queue.timerList.insertAfter ( *this, *pTmr ); | ||
2877 | 111 | break; | ||
2882 | 112 | } | 120 | } |
2883 | 113 | # ifdef DEBUG | ||
2884 | 114 | preemptCount++; | ||
2885 | 115 | # endif | ||
2886 | 116 | --pTmr; | ||
2887 | 117 | } | 121 | } |
2888 | 122 | debugPrintf ( ("Start of \"%s\" with delay %f at %p\n", | ||
2889 | 123 | m_pNotify ? | ||
2890 | 124 | typeid ( *m_pNotify ).name () : | ||
2891 | 125 | typeid ( m_pNotify ).name (), | ||
2892 | 126 | m_exp - epicsTime :: getCurrent (), | ||
2893 | 127 | this ) ); | ||
2894 | 128 | return sr; | ||
2895 | 129 | } | ||
2896 | 118 | 130 | ||
2912 | 119 | this->curState = timer::statePending; | 131 | void Timer :: m_remove ( Guard & guard ) |
2913 | 120 | 132 | { | |
2914 | 121 | if ( reschedualNeeded ) { | 133 | Timer * const pMoved = m_queue.m_heap.back (); |
2915 | 122 | this->queue.notify.reschedule (); | 134 | m_queue.m_heap.pop_back (); |
2916 | 123 | } | 135 | if ( m_index != m_queue.m_heap.size () ) { |
2917 | 124 | 136 | const size_t oldIndex = m_index; | |
2918 | 125 | # if defined(DEBUG) && 0 | 137 | m_queue.m_heap[oldIndex] = pMoved; |
2919 | 126 | this->show ( 10u ); | 138 | pMoved->m_index = oldIndex; |
2920 | 127 | this->queue.show ( 10u ); | 139 | if ( ! m_queue.m_fixParent ( oldIndex ) ) { |
2921 | 128 | # endif | 140 | m_queue.m_fixChildren ( oldIndex ); |
2922 | 129 | 141 | } | |
2923 | 130 | debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n", | 142 | } |
2924 | 131 | typeid ( this->notify ).name (), | 143 | m_index = m_invalidIndex; |
2925 | 132 | expire - epicsTime::getCurrent (), | 144 | m_curState = stateLimbo; |
2911 | 133 | this, preemptCount ) ); | ||
2926 | 134 | } | 145 | } |
2927 | 135 | 146 | ||
2929 | 136 | void timer::cancel () | 147 | bool Timer :: cancel () |
2930 | 137 | { | 148 | { |
2933 | 138 | bool reschedual = false; | 149 | M_CancelStatus cs = { false, false }; |
2932 | 139 | bool wakeupCancelBlockingThreads = false; | ||
2934 | 140 | { | 150 | { |
2955 | 141 | epicsGuard < epicsMutex > locker ( this->queue.mutex ); | 151 | Guard guard ( m_queue ); |
2956 | 142 | this->pNotify = 0; | 152 | cs = m_cancelPvt ( guard ); |
2957 | 143 | if ( this->curState == statePending ) { | 153 | } |
2958 | 144 | this->queue.timerList.remove ( *this ); | 154 | // we are careful to wakeup the timer queue thread after |
2959 | 145 | this->curState = stateLimbo; | 155 | // we nolonger hold the lock |
2960 | 146 | if ( this->queue.timerList.first() == this && | 156 | if ( cs.reschedule ) { |
2961 | 147 | this->queue.timerList.count() > 0 ) { | 157 | m_queue.m_notify.reschedule (); |
2962 | 148 | reschedual = true; | 158 | } |
2963 | 149 | } | 159 | return cs.wasPending; |
2964 | 150 | } | 160 | } |
2965 | 151 | else if ( this->curState == stateActive ) { | 161 | |
2966 | 152 | this->queue.cancelPending = true; | 162 | Timer :: M_CancelStatus Timer :: m_cancelPvt ( Guard & gd ) |
2967 | 153 | this->curState = timer::stateLimbo; | 163 | { |
2968 | 154 | if ( this->queue.processThread != epicsThreadGetIdSelf() ) { | 164 | gd.assertIdenticalMutex ( m_queue ); |
2969 | 155 | // make certain timer expire() does not run after cancel () returns, | 165 | M_CancelStatus cs = { false, false }; |
2970 | 156 | // but dont require that lock is applied while calling expire() | 166 | Guard guard ( m_queue ); |
2971 | 157 | while ( this->queue.cancelPending && | 167 | if ( m_curState == statePending ) { |
2972 | 158 | this->queue.pExpireTmr == this ) { | 168 | const epicsTime oldExp = m_queue.m_heap.front ()->m_exp; |
2973 | 159 | epicsGuardRelease < epicsMutex > autoRelease ( locker ); | 169 | m_remove ( guard ); |
2974 | 160 | this->queue.cancelBlockingEvent.wait (); | 170 | m_queue.m_cancelPending = ( m_queue.m_pExpTmr == this ); |
2975 | 171 | if ( m_queue.m_cancelPending ) { | ||
2976 | 172 | if ( m_queue.m_processThread != epicsThreadGetIdSelf() ) { | ||
2977 | 173 | // 1) make certain timer expire cllback does not run | ||
2978 | 174 | // after this cancel method returns | ||
2979 | 175 | // 2) dont require that lock is applied while calling | ||
2980 | 176 | // expire callback | ||
2981 | 177 | // 3) assume that timer could be deleted in its | ||
2982 | 178 | // expire callback so we dont touch this after lock | ||
2983 | 179 | // is released | ||
2984 | 180 | timerQueue & queue = m_queue; | ||
2985 | 181 | while ( queue.m_cancelPending && | ||
2986 | 182 | queue.m_pExpTmr == this ) { | ||
2987 | 183 | GuardRelease unguard ( guard ); | ||
2988 | 184 | queue.m_cancelBlockingEvent.wait (); | ||
2989 | 161 | } | 185 | } |
2990 | 162 | // in case other threads are waiting | 186 | // in case other threads are waiting |
2992 | 163 | wakeupCancelBlockingThreads = true; | 187 | queue.m_cancelBlockingEvent.signal (); |
2993 | 188 | } | ||
2994 | 189 | } | ||
2995 | 190 | else { | ||
2996 | 191 | cs.wasPending = true; | ||
2997 | 192 | if ( oldExp > m_queue.m_heap.front ()->m_exp ) { | ||
2998 | 193 | cs.reschedule = true; | ||
2999 | 164 | } | 194 | } |
3000 | 165 | } | 195 | } |
3001 | 166 | } | 196 | } |
3008 | 167 | if ( reschedual ) { | 197 | return cs; |
3003 | 168 | this->queue.notify.reschedule (); | ||
3004 | 169 | } | ||
3005 | 170 | if ( wakeupCancelBlockingThreads ) { | ||
3006 | 171 | this->queue.cancelBlockingEvent.signal (); | ||
3007 | 172 | } | ||
3009 | 173 | } | 198 | } |
3010 | 174 | 199 | ||
3012 | 175 | epicsTimer::expireInfo timer::getExpireInfo () const | 200 | epicsTimer :: expireInfo Timer :: getExpireInfo () const |
3013 | 176 | { | 201 | { |
3014 | 177 | // taking a lock here guarantees that users will not | 202 | // taking a lock here guarantees that users will not |
3015 | 178 | // see brief intervals when a timer isnt active because | 203 | // see brief intervals when a timer isnt active because |
3016 | 179 | // it is is canceled when start is called | 204 | // it is is canceled when start is called |
3020 | 180 | epicsGuard < epicsMutex > locker ( this->queue.mutex ); | 205 | Guard locker ( m_queue ); |
3021 | 181 | if ( this->curState == statePending || this->curState == stateActive ) { | 206 | if ( m_curState == statePending ) { |
3022 | 182 | return expireInfo ( true, this->exp ); | 207 | return expireInfo ( true, m_exp ); |
3023 | 183 | } | 208 | } |
3024 | 184 | return expireInfo ( false, epicsTime() ); | 209 | return expireInfo ( false, epicsTime() ); |
3025 | 185 | } | 210 | } |
3026 | 186 | 211 | ||
3028 | 187 | void timer::show ( unsigned int level ) const | 212 | void Timer :: show ( unsigned int level ) const |
3029 | 188 | { | 213 | { |
3031 | 189 | epicsGuard < epicsMutex > locker ( this->queue.mutex ); | 214 | Guard locker ( m_queue ); |
3032 | 190 | double delay; | 215 | double delay; |
3034 | 191 | if ( this->curState == statePending || this->curState == stateActive ) { | 216 | if ( m_curState == statePending ) { |
3035 | 192 | try { | 217 | try { |
3037 | 193 | delay = this->exp - epicsTime::getCurrent(); | 218 | delay = m_exp - epicsTime :: getCurrent (); |
3038 | 194 | } | 219 | } |
3039 | 195 | catch ( ... ) { | 220 | catch ( ... ) { |
3041 | 196 | delay = - DBL_MAX; | 221 | delay = -DBL_MAX; |
3042 | 197 | } | 222 | } |
3043 | 198 | } | 223 | } |
3044 | 199 | else { | 224 | else { |
3045 | 200 | delay = -DBL_MAX; | 225 | delay = -DBL_MAX; |
3046 | 201 | } | 226 | } |
3047 | 202 | const char *pStateName; | 227 | const char *pStateName; |
3049 | 203 | if ( this->curState == statePending ) { | 228 | if ( m_curState == statePending ) { |
3050 | 204 | pStateName = "pending"; | 229 | pStateName = "pending"; |
3051 | 205 | } | 230 | } |
3056 | 206 | else if ( this->curState == stateActive ) { | 231 | else if ( m_curState == stateLimbo ) { |
3053 | 207 | pStateName = "active"; | ||
3054 | 208 | } | ||
3055 | 209 | else if ( this->curState == stateLimbo ) { | ||
3057 | 210 | pStateName = "limbo"; | 232 | pStateName = "limbo"; |
3058 | 211 | } | 233 | } |
3059 | 212 | else { | 234 | else { |
3060 | 213 | pStateName = "corrupt"; | 235 | pStateName = "corrupt"; |
3061 | 214 | } | 236 | } |
3066 | 215 | printf ( "timer, state = %s, delay = %f\n", | 237 | printf ( "Timer, state = %s, index = %lu, delay = %f\n", |
3067 | 216 | pStateName, delay ); | 238 | pStateName, (unsigned long ) m_index, delay ); |
3068 | 217 | if ( level >= 1u && this->pNotify ) { | 239 | if ( level >= 1u && m_pNotify ) { |
3069 | 218 | this->pNotify->show ( level - 1u ); | 240 | m_pNotify->show ( level - 1u ); |
3070 | 219 | } | 241 | } |
3071 | 220 | } | 242 | } |
3072 | 221 | 243 | ||
3073 | 222 | void timer::operator delete ( void * ) | ||
3074 | 223 | { | ||
3075 | 224 | // Visual C++ .net appears to require operator delete if | ||
3076 | 225 | // placement operator delete is defined? I smell a ms rat | ||
3077 | 226 | // because if I declare placement new and delete, but | ||
3078 | 227 | // comment out the placement delete definition there are | ||
3079 | 228 | // no undefined symbols. | ||
3080 | 229 | errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", | ||
3081 | 230 | __FILE__, __LINE__ ); | ||
3082 | 231 | } | ||
3083 | 232 | |||
3084 | 233 | void epicsTimerForC::operator delete ( void * ) | ||
3085 | 234 | { | ||
3086 | 235 | // Visual C++ .net appears to require operator delete if | ||
3087 | 236 | // placement operator delete is defined? I smell a ms rat | ||
3088 | 237 | // because if I declare placement new and delete, but | ||
3089 | 238 | // comment out the placement delete definition there are | ||
3090 | 239 | // no undefined symbols. | ||
3091 | 240 | errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", | ||
3092 | 241 | __FILE__, __LINE__ ); | ||
3093 | 242 | } | ||
3094 | 243 | |||
3095 | diff --git a/modules/libcom/src/timer/timerPrivate.h b/modules/libcom/src/timer/timerPrivate.h | |||
3096 | index 259afae..8ca0e67 100644 | |||
3097 | --- a/modules/libcom/src/timer/timerPrivate.h | |||
3098 | +++ b/modules/libcom/src/timer/timerPrivate.h | |||
3099 | @@ -1,5 +1,7 @@ | |||
3100 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
3102 | 2 | * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
3103 | 3 | * National Laboratory | ||
3104 | 4 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne | ||
3105 | 3 | * National Laboratory. | 5 | * National Laboratory. |
3106 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
3107 | 5 | * Operator of Los Alamos National Laboratory. | 7 | * Operator of Los Alamos National Laboratory. |
3108 | @@ -7,6 +9,7 @@ | |||
3109 | 7 | * in file LICENSE that is included with this distribution. | 9 | * in file LICENSE that is included with this distribution. |
3110 | 8 | \*************************************************************************/ | 10 | \*************************************************************************/ |
3111 | 9 | /* | 11 | /* |
3112 | 12 | * | ||
3113 | 10 | * Author Jeffrey O. Hill | 13 | * Author Jeffrey O. Hill |
3114 | 11 | * johill@lanl.gov | 14 | * johill@lanl.gov |
3115 | 12 | * 505 665 1831 | 15 | * 505 665 1831 |
3116 | @@ -16,12 +19,15 @@ | |||
3117 | 16 | #define epicsTimerPrivate_h | 19 | #define epicsTimerPrivate_h |
3118 | 17 | 20 | ||
3119 | 18 | #include <typeinfo> | 21 | #include <typeinfo> |
3120 | 22 | #include <vector> | ||
3121 | 19 | 23 | ||
3122 | 20 | #include "tsFreeList.h" | ||
3123 | 21 | #include "epicsSingleton.h" | ||
3124 | 22 | #include "tsDLList.h" | 24 | #include "tsDLList.h" |
3125 | 23 | #include "epicsTimer.h" | 25 | #include "epicsTimer.h" |
3126 | 26 | #include "epicsMutex.h" | ||
3127 | 27 | #include "epicsGuard.h" | ||
3128 | 24 | #include "compilerDependencies.h" | 28 | #include "compilerDependencies.h" |
3129 | 29 | #include "epicsStaticInstance.h" | ||
3130 | 30 | #include "AllocatorArena.h" | ||
3131 | 25 | 31 | ||
3132 | 26 | #ifdef DEBUG | 32 | #ifdef DEBUG |
3133 | 27 | # define debugPrintf(ARGSINPAREN) printf ARGSINPAREN | 33 | # define debugPrintf(ARGSINPAREN) printf ARGSINPAREN |
3134 | @@ -29,90 +35,130 @@ | |||
3135 | 29 | # define debugPrintf(ARGSINPAREN) | 35 | # define debugPrintf(ARGSINPAREN) |
3136 | 30 | #endif | 36 | #endif |
3137 | 31 | 37 | ||
3139 | 32 | template < class T > class epicsGuard; | 38 | using std :: type_info; |
3140 | 39 | |||
3141 | 40 | class Timer; | ||
3142 | 41 | class timerQueue; | ||
3143 | 33 | 42 | ||
3145 | 34 | class timer : public epicsTimer, public tsDLNode < timer > { | 43 | bool operator < ( const Timer &, const Timer & ); |
3146 | 44 | bool operator > ( const Timer &, const Timer & ); | ||
3147 | 45 | bool operator <= ( const Timer &, const Timer & ); | ||
3148 | 46 | bool operator >= ( const Timer &, const Timer & ); | ||
3149 | 47 | |||
3150 | 48 | class Timer : public epicsTimer { | ||
3151 | 35 | public: | 49 | public: |
3152 | 50 | typedef epicsMutex Mutex; | ||
3153 | 51 | typedef epicsGuard < Mutex > Guard; | ||
3154 | 52 | typedef epicsGuardRelease < Mutex > GuardRelease; | ||
3155 | 53 | Timer ( timerQueue & ); | ||
3156 | 54 | ~Timer (); | ||
3157 | 36 | void destroy (); | 55 | void destroy (); |
3161 | 37 | void start ( class epicsTimerNotify &, const epicsTime & ); | 56 | unsigned start ( class epicsTimerNotify &, const epicsTime & ); |
3162 | 38 | void start ( class epicsTimerNotify &, double delaySeconds ); | 57 | unsigned start ( class epicsTimerNotify &, double delaySeconds ); |
3163 | 39 | void cancel (); | 58 | bool cancel (); |
3164 | 40 | expireInfo getExpireInfo () const; | 59 | expireInfo getExpireInfo () const; |
3165 | 60 | double getExpireDelay ( const epicsTime & currentTime ); | ||
3166 | 41 | void show ( unsigned int level ) const; | 61 | void show ( unsigned int level ) const; |
3169 | 42 | void * operator new ( size_t size, tsFreeList < timer, 0x20 > & ); | 62 | static void * operator new ( size_t sz ); |
3170 | 43 | epicsPlacementDeleteOperator (( void *, tsFreeList < timer, 0x20 > & )) | 63 | static void operator delete ( void * ptr, size_t sz ); |
3171 | 44 | protected: | 64 | protected: |
3175 | 45 | timer ( class timerQueue & ); | 65 | timerQueue & m_queue; |
3173 | 46 | ~timer (); | ||
3174 | 47 | timerQueue & queue; | ||
3176 | 48 | private: | 66 | private: |
3189 | 49 | enum state { statePending = 45, stateActive = 56, stateLimbo = 78 }; | 67 | typedef epics :: AllocatorArena < Timer, timerQueue, 16u > M_Allocator; |
3190 | 50 | epicsTime exp; // experation time | 68 | enum state { |
3191 | 51 | state curState; // current state | 69 | statePending = 45, |
3192 | 52 | epicsTimerNotify * pNotify; // callback | 70 | stateLimbo = 78 }; |
3193 | 53 | void privateStart ( epicsTimerNotify & notify, const epicsTime & ); | 71 | epicsTime m_exp; // experation time |
3194 | 54 | timer & operator = ( const timer & ); | 72 | state m_curState; // current state |
3195 | 55 | // Visual C++ .net appears to require operator delete if | 73 | epicsTimerNotify * m_pNotify; // callback |
3196 | 56 | // placement operator delete is defined? I smell a ms rat | 74 | size_t m_index; |
3197 | 57 | // because if I declare placement new and delete, but | 75 | static const size_t m_invalidIndex = |
3198 | 58 | // comment out the placement delete definition there are | 76 | ~ static_cast < size_t > ( 0u ); |
3199 | 59 | // no undefined symbols. | 77 | struct M_StartReturn { |
3200 | 60 | void operator delete ( void * ); | 78 | unsigned numNew; |
3201 | 79 | bool resched; | ||
3202 | 80 | }; | ||
3203 | 81 | M_StartReturn m_privateStart ( epicsTimerNotify & notify, | ||
3204 | 82 | const epicsTime & expire ); | ||
3205 | 83 | struct M_CancelStatus { | ||
3206 | 84 | bool reschedule; | ||
3207 | 85 | bool wasPending; | ||
3208 | 86 | }; | ||
3209 | 87 | M_CancelStatus m_cancelPvt ( Guard & ); | ||
3210 | 88 | void m_remove ( Guard & guard ); | ||
3211 | 89 | Timer & operator = ( const Timer & ); | ||
3212 | 61 | friend class timerQueue; | 90 | friend class timerQueue; |
3213 | 91 | friend bool operator < ( const Timer &, const Timer & ); | ||
3214 | 92 | friend bool operator > ( const Timer &, const Timer & ); | ||
3215 | 93 | friend bool operator <= ( const Timer &, const Timer & ); | ||
3216 | 94 | friend bool operator >= ( const Timer &, const Timer & ); | ||
3217 | 62 | }; | 95 | }; |
3218 | 63 | 96 | ||
3220 | 64 | struct epicsTimerForC : public epicsTimerNotify, public timer { | 97 | struct TimerForC : public epicsTimerNotify { |
3221 | 65 | public: | 98 | public: |
3231 | 66 | void destroy (); | 99 | typedef epicsMutex Mutex; |
3232 | 67 | protected: | 100 | typedef epicsGuard < Mutex > Guard; |
3233 | 68 | epicsTimerForC ( timerQueue &, epicsTimerCallback, void *pPrivateIn ); | 101 | TimerForC ( class timerQueue &, epicsTimerCallback, void * pPrivateIn ); |
3234 | 69 | ~epicsTimerForC (); | 102 | ~TimerForC (); |
3235 | 70 | void * operator new ( size_t size, tsFreeList < epicsTimerForC, 0x20 > & ); | 103 | unsigned start ( const epicsTime & ); |
3236 | 71 | epicsPlacementDeleteOperator (( void *, tsFreeList < epicsTimerForC, 0x20 > & )) | 104 | unsigned start ( double delaySeconds ); |
3237 | 72 | private: | 105 | bool cancel (); |
3238 | 73 | epicsTimerCallback pCallBack; | 106 | void show ( unsigned level ) const; |
3230 | 74 | void * pPrivate; | ||
3239 | 75 | expireStatus expire ( const epicsTime & currentTime ); | 107 | expireStatus expire ( const epicsTime & currentTime ); |
3251 | 76 | epicsTimerForC & operator = ( const epicsTimerForC & ); | 108 | double getExpireDelay (); |
3252 | 77 | // Visual C++ .net appears to require operator delete if | 109 | static void * operator new ( size_t sz ); |
3253 | 78 | // placement operator delete is defined? I smell a ms rat | 110 | static void operator delete ( void * ptr, size_t sz ); |
3254 | 79 | // because if I declare placement new and delete, but | 111 | private: |
3255 | 80 | // comment out the placement delete definition there are | 112 | typedef epics :: AllocatorArena < TimerForC, timerQueue, 16u > M_Allocator; |
3256 | 81 | // no undefined symbols. | 113 | Timer * m_pTimer; |
3257 | 82 | void operator delete ( void * ); | 114 | epicsTimerCallback m_pCallBack; |
3258 | 83 | friend class timerQueue; | 115 | void * m_pPrivate; |
3259 | 84 | }; | 116 | TimerForC & operator = ( const TimerForC & ); |
3260 | 85 | 117 | }; | |
3250 | 86 | using std :: type_info; | ||
3261 | 87 | 118 | ||
3263 | 88 | class timerQueue : public epicsTimerQueue { | 119 | class timerQueue : |
3264 | 120 | public epicsTimerQueue, | ||
3265 | 121 | public epicsMutex { | ||
3266 | 89 | public: | 122 | public: |
3267 | 123 | typedef epicsMutex Mutex; | ||
3268 | 124 | typedef epicsGuard < Mutex > Guard; | ||
3269 | 125 | typedef epicsGuardRelease < Mutex > GuardRelease; | ||
3270 | 90 | timerQueue ( epicsTimerQueueNotify ¬ify ); | 126 | timerQueue ( epicsTimerQueueNotify ¬ify ); |
3271 | 91 | virtual ~timerQueue (); | 127 | virtual ~timerQueue (); |
3272 | 92 | epicsTimer & createTimer (); | 128 | epicsTimer & createTimer (); |
3274 | 93 | epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); | 129 | Timer & createTimerImpl (); |
3275 | 130 | TimerForC & createTimerForC ( | ||
3276 | 131 | epicsTimerCallback pCallback, void *pArg ); | ||
3277 | 94 | double process ( const epicsTime & currentTime ); | 132 | double process ( const epicsTime & currentTime ); |
3278 | 133 | double process ( Guard &, const epicsTime & currentTime ); | ||
3279 | 95 | void show ( unsigned int level ) const; | 134 | void show ( unsigned int level ) const; |
3280 | 135 | void show ( Guard &, unsigned int level ) const; | ||
3281 | 96 | private: | 136 | private: |
3295 | 97 | tsFreeList < timer, 0x20 > timerFreeList; | 137 | epicsEvent m_cancelBlockingEvent; |
3296 | 98 | tsFreeList < epicsTimerForC, 0x20 > timerForCFreeList; | 138 | std :: vector < Timer * > m_heap; |
3297 | 99 | mutable epicsMutex mutex; | 139 | epicsTime m_exceptMsgTimeStamp; |
3298 | 100 | epicsEvent cancelBlockingEvent; | 140 | epicsTimerQueueNotify & m_notify; |
3299 | 101 | tsDLList < timer > timerList; | 141 | Timer * m_pExpTmr; |
3300 | 102 | epicsTimerQueueNotify & notify; | 142 | epicsThreadId m_processThread; |
3301 | 103 | timer * pExpireTmr; | 143 | size_t m_numTimers; |
3302 | 104 | epicsThreadId processThread; | 144 | bool m_cancelPending; |
3303 | 105 | epicsTime exceptMsgTimeStamp; | 145 | static const double m_exceptMsgMinPeriod; |
3291 | 106 | bool cancelPending; | ||
3292 | 107 | static const double exceptMsgMinPeriod; | ||
3293 | 108 | void printExceptMsg ( const char * pName, | ||
3294 | 109 | const type_info & type ); | ||
3304 | 110 | timerQueue ( const timerQueue & ); | 146 | timerQueue ( const timerQueue & ); |
3305 | 111 | timerQueue & operator = ( const timerQueue & ); | 147 | timerQueue & operator = ( const timerQueue & ); |
3308 | 112 | friend class timer; | 148 | void m_printExceptMsg ( const char * pName, const type_info & type ); |
3309 | 113 | friend struct epicsTimerForC; | 149 | bool m_fixParent ( size_t childIdx ); |
3310 | 150 | void m_fixChildren ( size_t parentIdx ); | ||
3311 | 151 | void m_swapEntries ( size_t idx0, size_t idx1 ); | ||
3312 | 152 | double m_expDelay ( const epicsTime & currentTime ); | ||
3313 | 153 | static size_t m_parent ( size_t childIdx ); | ||
3314 | 154 | static size_t m_leftChild ( size_t parentIdx ); | ||
3315 | 155 | static size_t m_rightChild ( size_t parentIdx ); | ||
3316 | 156 | friend class Timer; | ||
3317 | 157 | friend struct TimerForC; | ||
3318 | 114 | }; | 158 | }; |
3319 | 115 | 159 | ||
3320 | 160 | class timerQueueActiveMgr; | ||
3321 | 161 | |||
3322 | 116 | class timerQueueActiveMgrPrivate { | 162 | class timerQueueActiveMgrPrivate { |
3323 | 117 | public: | 163 | public: |
3324 | 118 | timerQueueActiveMgrPrivate (); | 164 | timerQueueActiveMgrPrivate (); |
3325 | @@ -123,101 +169,95 @@ private: | |||
3326 | 123 | friend class timerQueueActiveMgr; | 169 | friend class timerQueueActiveMgr; |
3327 | 124 | }; | 170 | }; |
3328 | 125 | 171 | ||
3333 | 126 | class timerQueueActiveMgr; | 172 | class timerQueueActive : |
3334 | 127 | 173 | public epicsTimerQueueActive, | |
3335 | 128 | class timerQueueActive : public epicsTimerQueueActive, | 174 | public epicsThreadRunable, |
3336 | 129 | public epicsThreadRunable, public epicsTimerQueueNotify, | 175 | public epicsTimerQueueNotify, |
3337 | 130 | public timerQueueActiveMgrPrivate { | 176 | public timerQueueActiveMgrPrivate { |
3338 | 131 | public: | 177 | public: |
3342 | 132 | typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr; | 178 | timerQueueActive ( bool okToShare, unsigned priority ); |
3340 | 133 | timerQueueActive ( RefMgr &, bool okToShare, unsigned priority ); | ||
3341 | 134 | void start (); | ||
3343 | 135 | epicsTimer & createTimer (); | 179 | epicsTimer & createTimer (); |
3345 | 136 | epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); | 180 | TimerForC & createTimerForC ( |
3346 | 181 | epicsTimerCallback pCallback, void *pArg ); | ||
3347 | 182 | void run (); | ||
3348 | 183 | void reschedule (); | ||
3349 | 184 | epicsTimerQueue & getEpicsTimerQueue (); | ||
3350 | 137 | void show ( unsigned int level ) const; | 185 | void show ( unsigned int level ) const; |
3351 | 138 | bool sharingOK () const; | 186 | bool sharingOK () const; |
3352 | 139 | unsigned threadPriority () const; | 187 | unsigned threadPriority () const; |
3353 | 140 | protected: | 188 | protected: |
3354 | 141 | ~timerQueueActive (); | 189 | ~timerQueueActive (); |
3355 | 142 | RefMgr _refMgr; | ||
3356 | 143 | private: | 190 | private: |
3372 | 144 | timerQueue queue; | 191 | typedef epicsMutex Mutex; |
3373 | 145 | epicsEvent rescheduleEvent; | 192 | typedef epicsGuard < Mutex > Guard; |
3374 | 146 | epicsEvent exitEvent; | 193 | typedef epicsGuardRelease < Mutex > GuardRelease; |
3375 | 147 | epicsThread thread; | 194 | timerQueue m_queue; |
3376 | 148 | const double sleepQuantum; | 195 | epicsEvent m_rescheduleEvent; |
3377 | 149 | bool okToShare; | 196 | epicsEvent m_exitEvent; |
3378 | 150 | int exitFlag; // use atomic ops | 197 | epicsThread m_thread; |
3379 | 151 | bool terminateFlag; | 198 | bool m_okToShare; |
3380 | 152 | void run (); | 199 | bool m_exitFlag; |
3381 | 153 | void reschedule (); | 200 | bool m_terminateFlag; |
3367 | 154 | double quantum (); | ||
3368 | 155 | void _printLastChanceExceptionMessage ( | ||
3369 | 156 | const char * pExceptionTypeName, | ||
3370 | 157 | const char * pExceptionContext ); | ||
3371 | 158 | epicsTimerQueue & getEpicsTimerQueue (); | ||
3382 | 159 | timerQueueActive ( const timerQueueActive & ); | 201 | timerQueueActive ( const timerQueueActive & ); |
3383 | 160 | timerQueueActive & operator = ( const timerQueueActive & ); | 202 | timerQueueActive & operator = ( const timerQueueActive & ); |
3384 | 161 | }; | 203 | }; |
3385 | 162 | 204 | ||
3386 | 163 | class timerQueueActiveMgr { | 205 | class timerQueueActiveMgr { |
3387 | 164 | public: | 206 | public: |
3389 | 165 | typedef epicsSingleton < timerQueueActiveMgr > :: reference RefThis; | 207 | static timerQueueActiveMgr & master (); |
3390 | 166 | timerQueueActiveMgr (); | 208 | timerQueueActiveMgr (); |
3391 | 167 | ~timerQueueActiveMgr (); | 209 | ~timerQueueActiveMgr (); |
3393 | 168 | epicsTimerQueueActiveForC & allocate ( RefThis &, bool okToShare, | 210 | epicsTimerQueueActiveForC & allocate ( bool okToShare, |
3394 | 169 | unsigned threadPriority = epicsThreadPriorityMin + 10 ); | 211 | unsigned threadPriority = epicsThreadPriorityMin + 10 ); |
3395 | 170 | void release ( epicsTimerQueueActiveForC & ); | 212 | void release ( epicsTimerQueueActiveForC & ); |
3396 | 171 | private: | 213 | private: |
3399 | 172 | epicsMutex mutex; | 214 | typedef epicsMutex Mutex; |
3400 | 173 | tsDLList < epicsTimerQueueActiveForC > sharedQueueList; | 215 | typedef epicsGuard < Mutex > Guard; |
3401 | 216 | Mutex m_mutex; | ||
3402 | 217 | tsDLList < epicsTimerQueueActiveForC > m_sharedQueueList; | ||
3403 | 174 | timerQueueActiveMgr ( const timerQueueActiveMgr & ); | 218 | timerQueueActiveMgr ( const timerQueueActiveMgr & ); |
3404 | 175 | timerQueueActiveMgr & operator = ( const timerQueueActiveMgr & ); | 219 | timerQueueActiveMgr & operator = ( const timerQueueActiveMgr & ); |
3405 | 176 | }; | 220 | }; |
3406 | 177 | 221 | ||
3407 | 178 | extern epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; | ||
3408 | 179 | |||
3409 | 180 | class timerQueuePassive : public epicsTimerQueuePassive { | 222 | class timerQueuePassive : public epicsTimerQueuePassive { |
3410 | 181 | public: | 223 | public: |
3411 | 182 | timerQueuePassive ( epicsTimerQueueNotify & ); | 224 | timerQueuePassive ( epicsTimerQueueNotify & ); |
3412 | 183 | epicsTimer & createTimer (); | 225 | epicsTimer & createTimer (); |
3414 | 184 | epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); | 226 | TimerForC & createTimerForC ( |
3415 | 227 | epicsTimerCallback pCallback, void *pArg ); | ||
3416 | 185 | void show ( unsigned int level ) const; | 228 | void show ( unsigned int level ) const; |
3417 | 186 | double process ( const epicsTime & currentTime ); | 229 | double process ( const epicsTime & currentTime ); |
3418 | 230 | epicsTimerQueue & getEpicsTimerQueue (); | ||
3419 | 187 | protected: | 231 | protected: |
3421 | 188 | timerQueue queue; | 232 | timerQueue m_queue; |
3422 | 189 | ~timerQueuePassive (); | 233 | ~timerQueuePassive (); |
3423 | 190 | epicsTimerQueue & getEpicsTimerQueue (); | ||
3424 | 191 | timerQueuePassive ( const timerQueuePassive & ); | 234 | timerQueuePassive ( const timerQueuePassive & ); |
3425 | 192 | timerQueuePassive & operator = ( const timerQueuePassive & ); | 235 | timerQueuePassive & operator = ( const timerQueuePassive & ); |
3426 | 193 | }; | 236 | }; |
3427 | 194 | 237 | ||
3430 | 195 | struct epicsTimerQueuePassiveForC : | 238 | struct epicsTimerQueuePassiveForC : |
3431 | 196 | public epicsTimerQueueNotify, public timerQueuePassive { | 239 | public epicsTimerQueueNotify, |
3432 | 240 | public timerQueuePassive { | ||
3433 | 197 | public: | 241 | public: |
3436 | 198 | epicsTimerQueuePassiveForC ( | 242 | epicsTimerQueuePassiveForC ( |
3437 | 199 | epicsTimerQueueNotifyReschedule, | 243 | epicsTimerQueueNotifyReschedule, |
3438 | 200 | epicsTimerQueueNotifyQuantum, | 244 | epicsTimerQueueNotifyQuantum, |
3439 | 201 | void * pPrivate ); | 245 | void * pPrivate ); |
3440 | 202 | void destroy (); | 246 | void destroy (); |
3441 | 247 | void reschedule (); | ||
3442 | 203 | protected: | 248 | protected: |
3443 | 204 | ~epicsTimerQueuePassiveForC (); | 249 | ~epicsTimerQueuePassiveForC (); |
3444 | 205 | private: | 250 | private: |
3451 | 206 | epicsTimerQueueNotifyReschedule pRescheduleCallback; | 251 | epicsTimerQueueNotifyReschedule m_pRescheduleCallback; |
3452 | 207 | epicsTimerQueueNotifyQuantum pSleepQuantumCallback; | 252 | epicsTimerQueueNotifyQuantum m_pSleepQuantumCallback; |
3453 | 208 | void * pPrivate; | 253 | void * m_pPrivate; |
3448 | 209 | static epicsSingleton < tsFreeList < epicsTimerQueuePassiveForC, 0x10 > > pFreeList; | ||
3449 | 210 | void reschedule (); | ||
3450 | 211 | double quantum (); | ||
3454 | 212 | }; | 254 | }; |
3455 | 213 | 255 | ||
3457 | 214 | struct epicsTimerQueueActiveForC : public timerQueueActive, | 256 | struct epicsTimerQueueActiveForC : public timerQueueActive, |
3458 | 215 | public tsDLNode < epicsTimerQueueActiveForC > { | 257 | public tsDLNode < epicsTimerQueueActiveForC > { |
3459 | 216 | public: | 258 | public: |
3461 | 217 | epicsTimerQueueActiveForC ( RefMgr &, bool okToShare, unsigned priority ); | 259 | epicsTimerQueueActiveForC ( bool okToShare, unsigned priority ); |
3462 | 218 | void release (); | 260 | void release (); |
3463 | 219 | void * operator new ( size_t ); | ||
3464 | 220 | void operator delete ( void * ); | ||
3465 | 221 | protected: | 261 | protected: |
3466 | 222 | virtual ~epicsTimerQueueActiveForC (); | 262 | virtual ~epicsTimerQueueActiveForC (); |
3467 | 223 | private: | 263 | private: |
3468 | @@ -225,52 +265,106 @@ private: | |||
3469 | 225 | epicsTimerQueueActiveForC & operator = ( const epicsTimerQueueActiveForC & ); | 265 | epicsTimerQueueActiveForC & operator = ( const epicsTimerQueueActiveForC & ); |
3470 | 226 | }; | 266 | }; |
3471 | 227 | 267 | ||
3473 | 228 | inline bool timerQueueActive::sharingOK () const | 268 | inline double Timer :: getExpireDelay ( const epicsTime & currentTime ) |
3474 | 269 | { | ||
3475 | 270 | return m_exp - currentTime; | ||
3476 | 271 | } | ||
3477 | 272 | |||
3478 | 273 | inline void * Timer :: operator new ( size_t sz ) | ||
3479 | 274 | { | ||
3480 | 275 | return M_Allocator :: allocateOctets ( sz ); | ||
3481 | 276 | } | ||
3482 | 277 | |||
3483 | 278 | inline void Timer :: operator delete ( void * p, size_t sz ) | ||
3484 | 229 | { | 279 | { |
3486 | 230 | return this->okToShare; | 280 | M_Allocator :: deallocateOctets ( p, sz ); |
3487 | 281 | } | ||
3488 | 282 | |||
3489 | 283 | inline bool operator < ( const Timer & lhs, const Timer & rhs ) | ||
3490 | 284 | { | ||
3491 | 285 | return lhs.m_exp < rhs.m_exp; | ||
3492 | 286 | } | ||
3493 | 287 | |||
3494 | 288 | inline bool operator > ( const Timer & lhs, const Timer & rhs ) | ||
3495 | 289 | { | ||
3496 | 290 | return rhs < lhs; | ||
3497 | 291 | } | ||
3498 | 292 | |||
3499 | 293 | inline bool operator <= ( const Timer & lhs, const Timer & rhs ) | ||
3500 | 294 | { | ||
3501 | 295 | return ! ( lhs > rhs ); | ||
3502 | 231 | } | 296 | } |
3503 | 232 | 297 | ||
3505 | 233 | inline unsigned timerQueueActive::threadPriority () const | 298 | inline bool operator >= ( const Timer & lhs, const Timer & rhs ) |
3506 | 299 | { | ||
3507 | 300 | return ! ( lhs < rhs ); | ||
3508 | 301 | } | ||
3509 | 302 | |||
3510 | 303 | inline size_t timerQueue :: m_parent ( const size_t childIdx ) | ||
3511 | 234 | { | 304 | { |
3513 | 235 | return thread.getPriority (); | 305 | return ( childIdx + ( childIdx & 1u ) ) / 2u - 1u; |
3514 | 236 | } | 306 | } |
3515 | 237 | 307 | ||
3518 | 238 | inline void * timer::operator new ( size_t size, | 308 | inline size_t timerQueue :: m_leftChild ( const size_t parentIdx ) |
3517 | 239 | tsFreeList < timer, 0x20 > & freeList ) | ||
3519 | 240 | { | 309 | { |
3521 | 241 | return freeList.allocate ( size ); | 310 | return ( parentIdx + 1u ) * 2u - 1u; |
3522 | 242 | } | 311 | } |
3523 | 243 | 312 | ||
3527 | 244 | #ifdef CXX_PLACEMENT_DELETE | 313 | inline size_t timerQueue :: m_rightChild ( const size_t parentIdx ) |
3525 | 245 | inline void timer::operator delete ( void * pCadaver, | ||
3526 | 246 | tsFreeList < timer, 0x20 > & freeList ) | ||
3528 | 247 | { | 314 | { |
3530 | 248 | freeList.release ( pCadaver ); | 315 | return ( parentIdx + 1u ) * 2u; |
3531 | 249 | } | 316 | } |
3532 | 250 | #endif | ||
3533 | 251 | 317 | ||
3536 | 252 | inline void * epicsTimerForC::operator new ( size_t size, | 318 | inline void timerQueue :: m_swapEntries ( size_t idx0, size_t idx1 ) |
3535 | 253 | tsFreeList < epicsTimerForC, 0x20 > & freeList ) | ||
3537 | 254 | { | 319 | { |
3539 | 255 | return freeList.allocate ( size ); | 320 | std :: swap ( m_heap[idx0], m_heap[idx1] ); |
3540 | 321 | m_heap[idx0]->m_index = idx0; | ||
3541 | 322 | m_heap[idx1]->m_index = idx1; | ||
3542 | 256 | } | 323 | } |
3543 | 257 | 324 | ||
3547 | 258 | #ifdef CXX_PLACEMENT_DELETE | 325 | inline bool timerQueueActive :: sharingOK () const |
3545 | 259 | inline void epicsTimerForC::operator delete ( void * pCadaver, | ||
3546 | 260 | tsFreeList < epicsTimerForC, 0x20 > & freeList ) | ||
3548 | 261 | { | 326 | { |
3550 | 262 | freeList.release ( pCadaver ); | 327 | return m_okToShare; |
3551 | 328 | } | ||
3552 | 329 | |||
3553 | 330 | inline unsigned timerQueueActive :: threadPriority () const | ||
3554 | 331 | { | ||
3555 | 332 | return m_thread.getPriority (); | ||
3556 | 333 | } | ||
3557 | 334 | |||
3558 | 335 | inline unsigned TimerForC :: start ( const epicsTime & expTime ) | ||
3559 | 336 | { | ||
3560 | 337 | return m_pTimer->start ( *this, expTime ); | ||
3561 | 338 | } | ||
3562 | 339 | |||
3563 | 340 | inline unsigned TimerForC :: start ( double delaySeconds ) | ||
3564 | 341 | { | ||
3565 | 342 | return m_pTimer->start ( *this, delaySeconds ); | ||
3566 | 343 | } | ||
3567 | 344 | |||
3568 | 345 | inline bool TimerForC :: cancel () | ||
3569 | 346 | { | ||
3570 | 347 | return m_pTimer->cancel (); | ||
3571 | 348 | } | ||
3572 | 349 | |||
3573 | 350 | inline double TimerForC :: getExpireDelay () | ||
3574 | 351 | { | ||
3575 | 352 | return m_pTimer->getExpireDelay ( epicsTime :: getCurrent () ); | ||
3576 | 353 | } | ||
3577 | 354 | |||
3578 | 355 | inline void * TimerForC :: operator new ( size_t sz ) | ||
3579 | 356 | { | ||
3580 | 357 | return M_Allocator :: allocateOctets ( sz ); | ||
3581 | 263 | } | 358 | } |
3582 | 264 | #endif | ||
3583 | 265 | 359 | ||
3585 | 266 | inline void * epicsTimerQueueActiveForC::operator new ( size_t size ) | 360 | inline void TimerForC :: operator delete ( void * p, size_t sz ) |
3586 | 267 | { | 361 | { |
3588 | 268 | return ::operator new ( size ); | 362 | M_Allocator :: deallocateOctets ( p, sz ); |
3589 | 269 | } | 363 | } |
3590 | 270 | 364 | ||
3592 | 271 | inline void epicsTimerQueueActiveForC::operator delete ( void * pCadaver ) | 365 | inline timerQueueActiveMgr & timerQueueActiveMgr :: master () |
3593 | 272 | { | 366 | { |
3595 | 273 | ::operator delete ( pCadaver ); | 367 | return epics :: staticInstance < timerQueueActiveMgr > (); |
3596 | 274 | } | 368 | } |
3597 | 275 | 369 | ||
3598 | 276 | #endif // epicsTimerPrivate_h | 370 | #endif // epicsTimerPrivate_h |
3599 | diff --git a/modules/libcom/src/timer/timerQueue.cpp b/modules/libcom/src/timer/timerQueue.cpp | |||
3600 | index 8f7f98e..5948941 100644 | |||
3601 | --- a/modules/libcom/src/timer/timerQueue.cpp | |||
3602 | +++ b/modules/libcom/src/timer/timerQueue.cpp | |||
3603 | @@ -1,4 +1,6 @@ | |||
3604 | 1 | /*************************************************************************\ | 1 | /*************************************************************************\ |
3605 | 2 | * Copyright (c) 2020 Triad National Security, as operator of Los Alamos | ||
3606 | 3 | * National Laboratory | ||
3607 | 2 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne | 4 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
3608 | 3 | * National Laboratory. | 5 | * National Laboratory. |
3609 | 4 | * Copyright (c) 2002 The Regents of the University of California, as | 6 | * Copyright (c) 2002 The Regents of the University of California, as |
3610 | @@ -12,56 +14,45 @@ | |||
3611 | 12 | * 505 665 1831 | 14 | * 505 665 1831 |
3612 | 13 | */ | 15 | */ |
3613 | 14 | 16 | ||
3616 | 15 | #include <stdio.h> | 17 | #include <cstdio> |
3615 | 16 | #include <float.h> | ||
3617 | 17 | 18 | ||
3618 | 18 | #define epicsExportSharedSymbols | 19 | #define epicsExportSharedSymbols |
3619 | 19 | #include "epicsGuard.h" | ||
3620 | 20 | #include "timerPrivate.h" | ||
3621 | 21 | #include "errlog.h" | 20 | #include "errlog.h" |
3622 | 21 | #include "timerPrivate.h" | ||
3623 | 22 | 22 | ||
3627 | 23 | const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds | 23 | const double timerQueue :: m_exceptMsgMinPeriod = 60.0 * 5.0; // seconds |
3625 | 24 | |||
3626 | 25 | epicsTimerQueue::~epicsTimerQueue () {} | ||
3628 | 26 | 24 | ||
3637 | 27 | timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) : | 25 | timerQueue :: timerQueue ( epicsTimerQueueNotify & notifyIn ) : |
3638 | 28 | mutex(__FILE__, __LINE__), | 26 | Mutex ( __FILE__, __LINE__ ), |
3639 | 29 | notify ( notifyIn ), | 27 | m_exceptMsgTimeStamp ( epicsTime :: getCurrent () |
3640 | 30 | pExpireTmr ( 0 ), | 28 | - m_exceptMsgMinPeriod ), |
3641 | 31 | processThread ( 0 ), | 29 | m_notify ( notifyIn ), |
3642 | 32 | exceptMsgTimeStamp ( | 30 | m_pExpTmr ( 0 ), |
3643 | 33 | epicsTime :: getCurrent () - exceptMsgMinPeriod ), | 31 | m_processThread ( 0 ), |
3644 | 34 | cancelPending ( false ) | 32 | m_numTimers ( 0u ), |
3645 | 33 | m_cancelPending ( false ) | ||
3646 | 35 | { | 34 | { |
3647 | 36 | } | 35 | } |
3648 | 37 | 36 | ||
3650 | 38 | timerQueue::~timerQueue () | 37 | timerQueue :: ~timerQueue () |
3651 | 39 | { | 38 | { |
3655 | 40 | timer *pTmr; | 39 | if ( m_heap.size () ) { |
3656 | 41 | while ( ( pTmr = this->timerList.get () ) ) { | 40 | while ( Timer * const pTmr = m_heap.back () ) { |
3657 | 42 | pTmr->curState = timer::stateLimbo; | 41 | pTmr->m_curState = Timer :: stateLimbo; |
3658 | 42 | m_heap.pop_back (); | ||
3659 | 43 | } | ||
3660 | 43 | } | 44 | } |
3661 | 44 | } | 45 | } |
3662 | 45 | 46 | ||
3663 | 46 | void timerQueue :: | 47 | void timerQueue :: |
3665 | 47 | printExceptMsg ( const char * pName, const type_info & type ) | 48 | m_printExceptMsg ( const char * pName, const type_info & type ) |
3666 | 48 | { | 49 | { |
3683 | 49 | char date[64]; | 50 | const epicsTime cur = epicsTime :: getCurrent (); |
3684 | 50 | double delay; | 51 | const double delay = cur - m_exceptMsgTimeStamp; |
3685 | 51 | try { | 52 | if ( delay >= m_exceptMsgMinPeriod ) { |
3686 | 52 | epicsTime cur = epicsTime :: getCurrent (); | 53 | m_exceptMsgTimeStamp = cur; |
3687 | 53 | delay = cur - this->exceptMsgTimeStamp; | 54 | char date[64]; |
3688 | 54 | cur.strftime ( date, sizeof ( date ), | 55 | cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f" ); |
3673 | 55 | "%a %b %d %Y %H:%M:%S.%f" ); | ||
3674 | 56 | if ( delay >= exceptMsgMinPeriod ) { | ||
3675 | 57 | this->exceptMsgTimeStamp = cur; | ||
3676 | 58 | } | ||
3677 | 59 | } | ||
3678 | 60 | catch ( ... ) { | ||
3679 | 61 | delay = DBL_MAX; | ||
3680 | 62 | strcpy ( date, "UKN DATE" ); | ||
3681 | 63 | } | ||
3682 | 64 | if ( delay >= exceptMsgMinPeriod ) { | ||
3689 | 65 | // we dont touch the typeid for the timer expiration | 56 | // we dont touch the typeid for the timer expiration |
3690 | 66 | // notify interface here because they might have | 57 | // notify interface here because they might have |
3691 | 67 | // destroyed the timer during its callback | 58 | // destroyed the timer during its callback |
3692 | @@ -72,155 +63,212 @@ void timerQueue :: | |||
3693 | 72 | pName, | 63 | pName, |
3694 | 73 | type.name (), | 64 | type.name (), |
3695 | 74 | date ); | 65 | date ); |
3696 | 66 | errlogPrintf ( "!!!! WARNING - PERIODIC TIMER MAY NOT RESTART !!!!\n" ); | ||
3697 | 75 | errlogFlush (); | 67 | errlogFlush (); |
3698 | 76 | } | 68 | } |
3699 | 77 | } | 69 | } |
3700 | 78 | 70 | ||
3704 | 79 | double timerQueue::process ( const epicsTime & currentTime ) | 71 | inline double timerQueue :: m_expDelay ( const epicsTime & currentTime ) |
3705 | 80 | { | 72 | { |
3706 | 81 | epicsGuard < epicsMutex > guard ( this->mutex ); | 73 | double delay = DBL_MAX; |
3707 | 74 | if ( m_heap.size () > 0u ) { | ||
3708 | 75 | delay = m_heap.front ()->m_exp - currentTime; | ||
3709 | 76 | } | ||
3710 | 77 | return delay; | ||
3711 | 78 | } | ||
3712 | 79 | |||
3713 | 80 | double timerQueue :: process ( const epicsTime & currentTime ) | ||
3714 | 81 | { | ||
3715 | 82 | Guard guard ( *this ); | ||
3716 | 83 | return this->process ( guard, currentTime ); | ||
3717 | 84 | } | ||
3718 | 82 | 85 | ||
3720 | 83 | if ( this->pExpireTmr ) { | 86 | double timerQueue :: process ( Guard & guard, |
3721 | 87 | const epicsTime & currentTime ) | ||
3722 | 88 | { | ||
3723 | 89 | guard.assertIdenticalMutex ( *this ); | ||
3724 | 90 | if ( m_processThread ) { | ||
3725 | 84 | // if some other thread is processing the queue | 91 | // if some other thread is processing the queue |
3726 | 85 | // (or if this is a recursive call) | 92 | // (or if this is a recursive call) |
3737 | 86 | timer * pTmr = this->timerList.first (); | 93 | double delay = m_expDelay ( currentTime ); |
3738 | 87 | if ( pTmr ) { | 94 | if ( delay <= 0.0 ) { |
3739 | 88 | double delay = pTmr->exp - currentTime; | 95 | delay = 0.0; |
3730 | 89 | if ( delay < 0.0 ) { | ||
3731 | 90 | delay = 0.0; | ||
3732 | 91 | } | ||
3733 | 92 | return delay; | ||
3734 | 93 | } | ||
3735 | 94 | else { | ||
3736 | 95 | return DBL_MAX; | ||
3740 | 96 | } | 96 | } |
3765 | 97 | } | 97 | return delay; |
3742 | 98 | |||
3743 | 99 | // | ||
3744 | 100 | // Tag current epired tmr so that we can detect if call back | ||
3745 | 101 | // is in progress when canceling the timer. | ||
3746 | 102 | // | ||
3747 | 103 | if ( this->timerList.first () ) { | ||
3748 | 104 | if ( currentTime >= this->timerList.first ()->exp ) { | ||
3749 | 105 | this->pExpireTmr = this->timerList.first (); | ||
3750 | 106 | this->timerList.remove ( *this->pExpireTmr ); | ||
3751 | 107 | this->pExpireTmr->curState = timer::stateActive; | ||
3752 | 108 | this->processThread = epicsThreadGetIdSelf (); | ||
3753 | 109 | # ifdef DEBUG | ||
3754 | 110 | this->pExpireTmr->show ( 0u ); | ||
3755 | 111 | # endif | ||
3756 | 112 | } | ||
3757 | 113 | else { | ||
3758 | 114 | double delay = this->timerList.first ()->exp - currentTime; | ||
3759 | 115 | debugPrintf ( ( "no activity process %f to next\n", delay ) ); | ||
3760 | 116 | return delay; | ||
3761 | 117 | } | ||
3762 | 118 | } | ||
3763 | 119 | else { | ||
3764 | 120 | return DBL_MAX; | ||
3766 | 121 | } | 98 | } |
3767 | 122 | 99 | ||
3768 | 123 | # ifdef DEBUG | 100 | # ifdef DEBUG |
3769 | 124 | unsigned N = 0u; | 101 | unsigned N = 0u; |
3770 | 125 | # endif | 102 | # endif |
3771 | 126 | 103 | ||
3781 | 127 | double delay = DBL_MAX; | 104 | m_processThread = epicsThreadGetIdSelf (); |
3782 | 128 | while ( true ) { | 105 | double delay = m_expDelay ( currentTime ); |
3783 | 129 | epicsTimerNotify *pTmpNotify = this->pExpireTmr->pNotify; | 106 | while ( delay <= 0.0 ) { |
3784 | 130 | this->pExpireTmr->pNotify = 0; | 107 | // |
3785 | 131 | epicsTimerNotify::expireStatus expStat ( epicsTimerNotify::noRestart ); | 108 | // if delay is zero or less we know at least one timer is on |
3786 | 132 | 109 | // the queue | |
3787 | 133 | { | 110 | // |
3788 | 134 | epicsGuardRelease < epicsMutex > unguard ( guard ); | 111 | // tag current expired tmr so that we can detect if call back |
3789 | 135 | 112 | // is in progress when canceling the timer | |
3790 | 113 | // | ||
3791 | 114 | delay = 0.0; | ||
3792 | 115 | m_pExpTmr = m_heap.front (); | ||
3793 | 116 | epicsTimerNotify * const pTmpNotify = m_pExpTmr->m_pNotify; | ||
3794 | 117 | m_pExpTmr->m_pNotify = 0; | ||
3795 | 118 | epicsTimerNotify :: expireStatus | ||
3796 | 119 | expStat ( epicsTimerNotify :: noRestart ); | ||
3797 | 120 | if ( pTmpNotify ) { | ||
3798 | 121 | GuardRelease unguard ( guard ); | ||
3799 | 136 | debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", | 122 | debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", |
3802 | 137 | N++, typeid ( this->pExpireTmr->notify ).name (), | 123 | N++, |
3803 | 138 | currentTime - this->pExpireTmr->exp ) ); | 124 | typeid ( *pTmpNotify ).name (), |
3804 | 125 | currentTime - m_pExpTmr->m_exp ) ); | ||
3805 | 139 | try { | 126 | try { |
3806 | 140 | expStat = pTmpNotify->expire ( currentTime ); | 127 | expStat = pTmpNotify->expire ( currentTime ); |
3807 | 141 | } | 128 | } |
3808 | 142 | catch ( std::exception & except ) { | 129 | catch ( std::exception & except ) { |
3810 | 143 | printExceptMsg ( except.what (), typeid ( except ) ); | 130 | m_printExceptMsg ( except.what (), typeid ( except ) ); |
3811 | 144 | } | 131 | } |
3812 | 145 | catch ( ... ) { | 132 | catch ( ... ) { |
3814 | 146 | printExceptMsg ( "non-standard exception", typeid ( void ) ); | 133 | m_printExceptMsg ( "non-standard exception", typeid ( void ) ); |
3815 | 147 | } | 134 | } |
3816 | 148 | } | 135 | } |
3817 | 149 | 136 | ||
3818 | 150 | // | 137 | // |
3821 | 151 | // only restart if they didnt cancel() the timer | 138 | // !! The position of a timer in the queue is allowed to change |
3822 | 152 | // while the call back was running | 139 | // !! while its timer callback is running. This happens |
3823 | 140 | // !! potentially when they reschedule a timer or cancel a | ||
3824 | 141 | // !! timer. A small amount of additional labor is expended | ||
3825 | 142 | // !! to properly handle this type of change below (we | ||
3826 | 143 | // !! test the return from fix_parent and conditionally | ||
3827 | 144 | // !! call fix_child, instead of calling only fix_child). | ||
3828 | 153 | // | 145 | // |
3830 | 154 | if ( this->cancelPending ) { | 146 | if ( m_cancelPending ) { |
3831 | 147 | // | ||
3832 | 148 | // only restart if they didnt cancel() the currently | ||
3833 | 149 | // expiring timer while its call-back is running | ||
3834 | 150 | // | ||
3835 | 155 | // 1) if another thread is canceling then cancel() waits for | 151 | // 1) if another thread is canceling then cancel() waits for |
3836 | 156 | // the event below | 152 | // the event below |
3837 | 157 | // 2) if this thread is canceling in the timer callback then | 153 | // 2) if this thread is canceling in the timer callback then |
3838 | 158 | // dont touch timer or notify here because the cancel might | 154 | // dont touch timer or notify here because the cancel might |
3839 | 159 | // have occurred because they destroyed the timer in the | 155 | // have occurred because they destroyed the timer in the |
3840 | 160 | // callback | 156 | // callback |
3843 | 161 | this->cancelPending = false; | 157 | // 3) timer::cancel sets timer state to limbo and timer index |
3844 | 162 | this->cancelBlockingEvent.signal (); | 158 | // to invalid |
3845 | 159 | // | ||
3846 | 160 | m_cancelPending = false; | ||
3847 | 161 | m_cancelBlockingEvent.signal (); | ||
3848 | 163 | } | 162 | } |
3849 | 164 | else { | 163 | else { |
3857 | 165 | this->pExpireTmr->curState = timer::stateLimbo; | 164 | if ( m_pExpTmr->m_pNotify ) { |
3858 | 166 | if ( this->pExpireTmr->pNotify ) { | 165 | // pNotify was cleared above; if its valid now we |
3859 | 167 | // pNotify was cleared above so if it is valid now we know that | 166 | // know that someone has restarted the timer when |
3860 | 168 | // someone has started the timer from another thread and that | 167 | // its callback is currently running either |
3861 | 169 | // predominates over the restart parameters from expire. | 168 | // asynchronously from another thread or from |
3862 | 170 | this->pExpireTmr->privateStart ( | 169 | // within the currently running expire callback, |
3863 | 171 | *this->pExpireTmr->pNotify, this->pExpireTmr->exp ); | 170 | // possibly moving its position in the heap. As |
3864 | 171 | // a defined policy either of these situations | ||
3865 | 172 | // overrides any restart request parameters | ||
3866 | 173 | // returned from expire | ||
3867 | 172 | } | 174 | } |
3868 | 173 | else if ( expStat.restart() ) { | 175 | else if ( expStat.restart() ) { |
3869 | 174 | // restart as nec | 176 | // restart as nec |
3884 | 175 | this->pExpireTmr->privateStart ( | 177 | m_pExpTmr->m_pNotify = pTmpNotify; |
3885 | 176 | *pTmpNotify, currentTime + expStat.expirationDelay() ); | 178 | m_pExpTmr->m_exp = currentTime + expStat.expirationDelay (); |
3886 | 177 | } | 179 | if ( ! m_fixParent ( m_pExpTmr->m_index ) ) { |
3887 | 178 | } | 180 | m_fixChildren ( m_pExpTmr->m_index ); |
3888 | 179 | this->pExpireTmr = 0; | 181 | } |
3875 | 180 | |||
3876 | 181 | if ( this->timerList.first () ) { | ||
3877 | 182 | if ( currentTime >= this->timerList.first ()->exp ) { | ||
3878 | 183 | this->pExpireTmr = this->timerList.first (); | ||
3879 | 184 | this->timerList.remove ( *this->pExpireTmr ); | ||
3880 | 185 | this->pExpireTmr->curState = timer::stateActive; | ||
3881 | 186 | # ifdef DEBUG | ||
3882 | 187 | this->pExpireTmr->show ( 0u ); | ||
3883 | 188 | # endif | ||
3889 | 189 | } | 182 | } |
3890 | 190 | else { | 183 | else { |
3894 | 191 | delay = this->timerList.first ()->exp - currentTime; | 184 | m_pExpTmr->m_remove ( guard ); |
3892 | 192 | this->processThread = 0; | ||
3893 | 193 | break; | ||
3895 | 194 | } | 185 | } |
3896 | 195 | } | 186 | } |
3900 | 196 | else { | 187 | delay = m_expDelay ( currentTime ); |
3901 | 197 | this->processThread = 0; | 188 | } |
3902 | 198 | delay = DBL_MAX; | 189 | m_pExpTmr = 0; |
3903 | 190 | m_processThread = 0; | ||
3904 | 191 | return delay; | ||
3905 | 192 | } | ||
3906 | 193 | |||
3907 | 194 | bool timerQueue :: m_fixParent ( size_t childIdx ) | ||
3908 | 195 | { | ||
3909 | 196 | bool itMoved = false; | ||
3910 | 197 | while ( childIdx != 0u ) { | ||
3911 | 198 | const size_t parentIdx = m_parent ( childIdx ); | ||
3912 | 199 | if ( *m_heap[parentIdx] <= *m_heap[childIdx] ) { | ||
3913 | 199 | break; | 200 | break; |
3914 | 200 | } | 201 | } |
3915 | 202 | m_swapEntries ( parentIdx, childIdx ); | ||
3916 | 203 | childIdx = parentIdx; | ||
3917 | 204 | itMoved = true; | ||
3918 | 201 | } | 205 | } |
3920 | 202 | return delay; | 206 | return itMoved; |
3921 | 203 | } | 207 | } |
3922 | 204 | 208 | ||
3924 | 205 | epicsTimer & timerQueue::createTimer () | 209 | void timerQueue :: m_fixChildren ( size_t parentIdx ) |
3925 | 206 | { | 210 | { |
3927 | 207 | return * new ( this->timerFreeList ) timer ( * this ); | 211 | const size_t hpsz = m_heap.size (); |
3928 | 212 | while ( true ) { | ||
3929 | 213 | const size_t leftChildIdx = m_leftChild ( parentIdx ); | ||
3930 | 214 | const size_t rightChildIdx = m_rightChild ( parentIdx ); | ||
3931 | 215 | size_t smallestIdx = parentIdx; | ||
3932 | 216 | if ( leftChildIdx < hpsz ) { | ||
3933 | 217 | if ( *m_heap[parentIdx] > *m_heap[leftChildIdx] ) { | ||
3934 | 218 | smallestIdx = leftChildIdx; | ||
3935 | 219 | } | ||
3936 | 220 | } | ||
3937 | 221 | if ( rightChildIdx < hpsz ) { | ||
3938 | 222 | if ( *m_heap[smallestIdx] > *m_heap[rightChildIdx] ) { | ||
3939 | 223 | smallestIdx = rightChildIdx; | ||
3940 | 224 | } | ||
3941 | 225 | } | ||
3942 | 226 | if ( smallestIdx == parentIdx ) { | ||
3943 | 227 | break; | ||
3944 | 228 | } | ||
3945 | 229 | m_swapEntries ( parentIdx, smallestIdx ); | ||
3946 | 230 | parentIdx = smallestIdx; | ||
3947 | 231 | } | ||
3948 | 232 | } | ||
3949 | 233 | |||
3950 | 234 | Timer & timerQueue :: createTimerImpl () | ||
3951 | 235 | { | ||
3952 | 236 | // better to throw now in contrast with later during start | ||
3953 | 237 | Guard guard ( *this ); | ||
3954 | 238 | m_numTimers++; | ||
3955 | 239 | m_heap.reserve ( m_numTimers ); | ||
3956 | 240 | return * new Timer ( * this ); | ||
3957 | 208 | } | 241 | } |
3958 | 209 | 242 | ||
3960 | 210 | epicsTimerForC & timerQueue::createTimerForC ( epicsTimerCallback pCallback, void *pArg ) | 243 | epicsTimer & timerQueue :: createTimer () |
3961 | 211 | { | 244 | { |
3963 | 212 | return * new ( this->timerForCFreeList ) epicsTimerForC ( *this, pCallback, pArg ); | 245 | return createTimerImpl (); |
3964 | 213 | } | 246 | } |
3965 | 214 | 247 | ||
3967 | 215 | void timerQueue::show ( unsigned level ) const | 248 | TimerForC & timerQueue :: createTimerForC ( |
3968 | 249 | epicsTimerCallback pCallback, void *pArg ) | ||
3969 | 216 | { | 250 | { |
3972 | 217 | epicsGuard < epicsMutex > locker ( this->mutex ); | 251 | return * new TimerForC ( *this, pCallback, pArg ); |
3973 | 218 | printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () ); | 252 | } |
3974 | 253 | |||
3975 | 254 | void timerQueue :: show ( unsigned level ) const | ||
3976 | 255 | { | ||
3977 | 256 | Guard guard ( const_cast < timerQueue & > ( *this ) ); | ||
3978 | 257 | this->show ( guard, level ); | ||
3979 | 258 | } | ||
3980 | 259 | |||
3981 | 260 | void timerQueue :: show ( Guard & guard, unsigned level ) const | ||
3982 | 261 | { | ||
3983 | 262 | guard.assertIdenticalMutex ( *this ); | ||
3984 | 263 | printf ( "epicsTimerQueue with %lu items pending\n", | ||
I haven't looked at the code, yet, but it builds fine on Ubuntu 19.10 with GCC 9.2: no warnings, all tests pass :-) A few compiler/OS combinations fail to build on Travis (https:/ /travis- ci.com/ github/ mark0n/ epics-base/ builds/ 161843145) and AppVeyor (https:/ /ci.appveyor. com/project/ MartinKonrad/ epics-base/ builds/ 32416944), though.
Before digging into the code, I benchmarked the old and the proposed timer queue implementation. You can find the results here:
https:/ /www.martin- konrad. net/nextcloud/ index.php/ s/mYHXWQxbJFz4L kL
The old implementation takes more than 100 us to create and start a new timer if there are already 30,000 timers in the queue (this number is based on a real-world scenario with Asyn/Stream at FRIB) and the new timer needs to be inserted at the beginning of the list (30,000 existing timers with 60 s, the new timer has 30 s duration). In this case the old code has to walk a linked list from the back all the way to the front (the code was optimized for inserting timers with the same duration). The new implementation performs two orders of magnitude better in this scenario. Not bad! None of the cases I benchmarked is performing significantly worse than with the old implementation. For comparison I also benchmarked boost::asio's timers - its performance is in the same order of magnitude.
See https:/ /github. com/mark0n/ benchmark_ timerqueue for my benchmark code.