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 | deleted file mode 160000 |
3 | index e91a588..0000000 |
4 | --- a/.ci |
5 | +++ /dev/null |
6 | @@ -1 +0,0 @@ |
7 | -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 | WARN_CFLAGS_YES = -Wall |
14 | WARN_CFLAGS_NO = -w |
15 | OPT_CFLAGS_YES = -O3 |
16 | -OPT_CFLAGS_NO = -g |
17 | +OPT_CFLAGS_NO = -g -DMEMCHECK |
18 | |
19 | PROF_CXXFLAGS_YES = -p |
20 | GPROF_CXXFLAGS_YES = -pg |
21 | @@ -44,7 +44,7 @@ CODE_CXXFLAGS += $(ASAN_FLAGS_$(ENABLE_ASAN)) |
22 | WARN_CXXFLAGS_YES = -Wall |
23 | WARN_CXXFLAGS_NO = -w |
24 | OPT_CXXFLAGS_YES = -O3 |
25 | -OPT_CXXFLAGS_NO = -g |
26 | +OPT_CXXFLAGS_NO = -g -DMEMCHECK |
27 | |
28 | CODE_LDFLAGS = $(PROF_CXXFLAGS_$(PROFILE)) $(GPROF_CXXFLAGS_$(GPROF)) |
29 | 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 | #USR_CFLAGS += -DNVALGRIND |
36 | |
37 | INC += valgrind/valgrind.h |
38 | +INC += valgrind/memcheck.h |
39 | +INC += valgrind/epicsMemChk.h |
40 | |
41 | INC += libComVersion.h |
42 | INC += libComVersionNum.h |
43 | diff --git a/modules/libcom/src/misc/AllocatorArena.h b/modules/libcom/src/misc/AllocatorArena.h |
44 | 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 | + |
50 | +/*************************************************************************\ |
51 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
52 | +* National Laboratory |
53 | +* EPICS BASE is distributed subject to a Software License Agreement found |
54 | +* in file LICENSE that is included with this distribution. |
55 | +\*************************************************************************/ |
56 | + |
57 | +/* |
58 | + * Author Jeffrey O. Hill |
59 | + */ |
60 | + |
61 | +// |
62 | +// Arena Allocator |
63 | +// |
64 | +// A thread private allocator for N of type T allocated in-mass, and |
65 | +// deallocated in-mass. The individual calls to allocate run very |
66 | +// efficiently because they simply return a pointer to preallocated |
67 | +// space for a type T, and advance the index identifying the space for |
68 | +// the next allocation, using thread private storage sans mutex |
69 | +// protection overhead. The in-bulk deallocation is postponed until |
70 | +// the last active T, residing within a bulk allocated block, is |
71 | +// deallocated. |
72 | +// |
73 | +// The allocator falls back to ordinary global new/delete if the user |
74 | +// requests more than one element for allocation using the vector |
75 | +// form of new. |
76 | +// |
77 | +// This facility is fully safe for use in a multi-threaded environment. |
78 | +// A thread private variable, and a thread private cleanup, are used to |
79 | +// maintain a thread private context so that the overhead associated |
80 | +// with a mutual exclusion locking can be avoided. |
81 | +// |
82 | +// Be aware that the storage overhead for any type T is sizeof ( T ) plus |
83 | +// sizeof ( void * ), a substantial consideration probably only if small |
84 | +// objects are stored, and storage efficency is more important than |
85 | +// performance considerations. Furthermore, storage overhead increases |
86 | +// for C++ compilers predating C++ 11. |
87 | +// |
88 | +// In this code a contiguous bulk block of storage for N of type T |
89 | +// is template type Rack<S,A,N> where S is the size of T, and A is the |
90 | +// required alignment. The thread-private allocaton occurs when peeling |
91 | +// off storage for an individual T from the thread's private Rack. When |
92 | +// the thread's private Rack is exausted, then a new Rack is allocated |
93 | +// from the specified Rack allocator type. Currently two global Rack |
94 | +// allocator implementations are provided. One that uses ordinary global |
95 | +// new/delete, and one that uses a mutex protected global free list |
96 | +// (the default) designated by rack allocator policies rap_pool and |
97 | +// rap_freeList respectively. |
98 | +// |
99 | +// Define ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE in order to |
100 | +// temporarily bypass bulk allocation and return to ordinary global |
101 | +// operator new and delete, so that memory access inspectors such as |
102 | +// purify or valgrind might be more effective. Furthermore, when the |
103 | +// allocator _is_ active, and it is a debug build, specialized |
104 | +// valgrind macros document with valgrind memory regions owned by this |
105 | +// allocater for which ownership has not yet passed to an end application |
106 | +// so that they do not appear as undesireable noise in valgrind leak |
107 | +// reports. |
108 | +// |
109 | + |
110 | +#ifndef epicsAllocatorArena_h |
111 | +#define epicsAllocatorArena_h |
112 | + |
113 | +// see comment above |
114 | +#if 0 |
115 | +#define ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE |
116 | +#endif |
117 | + |
118 | +#if __cplusplus >= 201103L |
119 | +# include <cstdint> |
120 | +#endif |
121 | + |
122 | +#include <new> |
123 | +#include <cstdlib> |
124 | +#include <string> |
125 | +#include <cstdio> |
126 | +#include <typeinfo> |
127 | + |
128 | +#include "epicsAtomic.h" |
129 | +#include "epicsMutex.h" |
130 | +#include "epicsThread.h" |
131 | +#include "epicsStaticInstance.h" |
132 | +#include "compilerDependencies.h" |
133 | +#include "valgrind/epicsMemChk.h" |
134 | +#include "shareLib.h" |
135 | + |
136 | + |
137 | +#if __cplusplus >= 201103L |
138 | +# if defined ( __GNUC__ ) |
139 | +# define GCC_VERSION_AA \ |
140 | + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) |
141 | + // perhaps the nios2 cross compiler doesnt properly implement |
142 | + // alignment even at gcc 4.8, based on experimental evidence |
143 | +# define ALIGNAS_CMPLR_OK ( GCC_VERSION_AA >= 50000 ) |
144 | +# else |
145 | +# define ALIGNAS_CMPLR_OK 1 |
146 | +# endif |
147 | +#else |
148 | +# define ALIGNAS_CMPLR_OK 0 |
149 | +#endif |
150 | + |
151 | +#ifndef SIZE_MAX |
152 | +# define SIZE_MAX ( static_cast < size_t > ( -1L ) ) |
153 | +#endif |
154 | + |
155 | +namespace epics { |
156 | +namespace _impl { |
157 | + |
158 | +using std :: size_t; |
159 | +using std :: type_info; |
160 | +using std :: ptrdiff_t; |
161 | +using std :: allocator; |
162 | +using std :: bad_alloc; |
163 | + |
164 | +// |
165 | +// S - the allocation size (the size of T) |
166 | +// A - the required alignment |
167 | +// N - the block size for pool allocation |
168 | +// (pool allocation size will be approximately S * N) |
169 | +// |
170 | +template < size_t S, size_t A, size_t N = 256 > class Rack; |
171 | + |
172 | +// |
173 | +// R - the rack type |
174 | +// P - the allocation policy type |
175 | +// TRACE - if true, print a message each time that a new racks allocated |
176 | +// counter power of two is exceeded for a particular allocation type |
177 | +// |
178 | +enum RackAllocPolicy { rap_pool, rap_freeList }; |
179 | +template < typename R, RackAllocPolicy P, bool TRACE > class RackAlloc; |
180 | + |
181 | +// |
182 | +// T - the allocation type |
183 | +// G - the discriminator for the thread private variable group |
184 | +// (proper scaling requires an independent thread private variable |
185 | +// group for each library) any type is allowed and appropriate for |
186 | +// identifying an independent thread private context |
187 | +// N - the block size for pool allocation |
188 | +// A - the rack allocator |
189 | +// |
190 | +// on old compilers prior to ALIGNAS_CMPLR_OK all blocks are aligned |
191 | +// for worst case alignment requirements |
192 | +// |
193 | +template < typename T, typename G, size_t N = 256, |
194 | + RackAllocPolicy P = rap_freeList, bool TRACE = false > |
195 | +class AllocatorArena { |
196 | +public: |
197 | + typedef T value_type; |
198 | + typedef value_type * pointer; |
199 | + typedef const value_type * const_pointer; |
200 | + typedef value_type & reference; |
201 | + typedef const value_type & const_reference; |
202 | + typedef size_t size_type; |
203 | + typedef ptrdiff_t difference_type; |
204 | + typedef typename allocator < void > :: const_pointer void_const_pointer; |
205 | + typedef typename allocator < void > :: pointer void_pointer; |
206 | + |
207 | + template < typename T0 > |
208 | + struct rebind { |
209 | + typedef AllocatorArena < T0, G, N, P, TRACE > other; |
210 | + }; |
211 | + |
212 | + AllocatorArena (); |
213 | + |
214 | + template < typename Y > |
215 | + AllocatorArena ( const Y & ); |
216 | + |
217 | + // address |
218 | + static pointer address ( reference r ); |
219 | + static const_pointer address ( const_reference r ); |
220 | + |
221 | + // memory allocation |
222 | + static pointer allocate ( size_type nAlloc, |
223 | + void_const_pointer pNearbyHint = 0 ); |
224 | + static void deallocate ( const pointer p, size_type nAlloc ); |
225 | + |
226 | + // helpers, for creating class specific operator new and delete |
227 | + static void * allocateOctets ( size_type size ); |
228 | + static void * allocateOctets ( size_type size, const std :: nothrow_t & ); |
229 | + static void deallocateOctets ( void * p, size_type size ); |
230 | + |
231 | + // remove the allocator's thread private reference to any partially |
232 | + // consumed block so that it can be released back into system pool |
233 | + // (this action is automated at thread exit) |
234 | + static void cleanup (); |
235 | + |
236 | + // size |
237 | + static size_type max_size (); |
238 | + |
239 | + // construction / destruction of target |
240 | +#if __cplusplus >= 201103L |
241 | + template < typename T0, typename ... Args > |
242 | + static void construct ( T0 *, Args && ... ); // depricated in c++ 17 |
243 | +#else |
244 | + template < typename T0, typename A0 > |
245 | + static void construct ( T0 *, A0 ); |
246 | + template < typename T0, typename A0, typename A1 > |
247 | + static void construct ( T0 *, A0, A1 ); |
248 | + template < typename T0, typename A0, typename A1, typename A2 > |
249 | + static void construct ( T0 *, A0, A1, A2 ); |
250 | + template < typename T0, typename A0, typename A1, typename A2, |
251 | + typename A3 > |
252 | + static void construct ( T0 *, A0, A1, A2, A3 ); |
253 | + template < typename T0, typename A0, typename A1, typename A2, |
254 | + typename A3, typename A4 > |
255 | + static void construct ( T0 *, A0, A1, A2, A3, A4 ); |
256 | +#endif |
257 | + template < class U > |
258 | + static void destroy ( U * ); // depricated in c++ 17 |
259 | + |
260 | + bool operator == ( AllocatorArena const & ); |
261 | + bool operator != ( AllocatorArena const & ); |
262 | + |
263 | + static size_t rackCount (); |
264 | + static size_t byteCount (); |
265 | +private: |
266 | +#if ALIGNAS_CMPLR_OK |
267 | + typedef Rack < sizeof ( T ), alignof ( T ), N > M_Rack; |
268 | +#else |
269 | + /* worst case alignment is used */ |
270 | + typedef Rack < sizeof ( T ), 0u, N > M_Rack; |
271 | +#endif |
272 | + typedef RackAlloc < M_Rack, P, TRACE > M_RackAlloc; |
273 | + static pointer m_threadPrivateAlloc (); |
274 | + static void m_rackCleanup ( void * ); |
275 | +}; // end of class AllocatorArena |
276 | + |
277 | +class epicsShareClass ThreadPrivateIdBadAlloc : public bad_alloc |
278 | +{ |
279 | + const char * what () const throw (); |
280 | +}; |
281 | + |
282 | +class epicsShareClass AtThreadExitBadAlloc : public bad_alloc |
283 | +{ |
284 | + const char * what () const throw (); |
285 | +}; |
286 | + |
287 | +#if __cplusplus >= 201103L |
288 | + typedef std :: uint8_t Octet; |
289 | +#else |
290 | + typedef unsigned char Octet; |
291 | +#endif |
292 | + |
293 | +typedef Octet * POctet; |
294 | + |
295 | +// align for all possible types, similar to malloc |
296 | +union MaxAlign { |
297 | + struct SomeStruct {}; |
298 | + long double m_ld; |
299 | + long long m_ll; |
300 | + long double * m_pd; |
301 | + SomeStruct * m_pss; |
302 | + void (* m_pf) (); |
303 | + long double SomeStruct :: * m_pmd; |
304 | + void ( SomeStruct :: * m_pmf ) (); |
305 | + /* |
306 | + * eye of newt ... |
307 | + */ |
308 | +}; |
309 | + |
310 | +template < size_t S, size_t A, size_t N > |
311 | +class Rack { |
312 | +public: |
313 | + typedef size_t size_type; |
314 | + typedef ptrdiff_t difference_type; |
315 | + |
316 | + Rack (); |
317 | + ~Rack (); |
318 | + bool empty () const; |
319 | + void * alloc (); |
320 | + size_t removeReference (); |
321 | + void addReference (); |
322 | + |
323 | + static const size_t number; |
324 | + static const size_t alignment; |
325 | + static Rack * dealloc ( void * ); |
326 | +private: |
327 | + struct M_Wrapper { |
328 | + VALGRIND_RED_ZONE ( m_redZoneBefore ) |
329 | +# if ALIGNAS_CMPLR_OK |
330 | + alignas ( A ) Octet m_bufForObj [ S ]; |
331 | +# else |
332 | + union { |
333 | + Octet m_bufForObj [ S ]; |
334 | + MaxAlign m_maxAlign; |
335 | + }; |
336 | +# endif |
337 | + VALGRIND_RED_ZONE ( m_redZoneAfter ) |
338 | + void * m_pRack; |
339 | + }; |
340 | + typedef M_Wrapper * PWrapper; |
341 | + M_Wrapper m_wrapped[N]; |
342 | + size_t m_nAlloc; |
343 | + size_t m_refCount; |
344 | +#if __cplusplus >= 201103L |
345 | + Rack ( const Rack & ) = delete; |
346 | + Rack & operator = ( const Rack & ) = delete; |
347 | +#endif |
348 | +}; |
349 | + |
350 | +struct RackManager { |
351 | + void * m_pRack; |
352 | + void ( *m_pThreadExitFunc )( void * pRack ); |
353 | +}; |
354 | + |
355 | +class epicsShareClass AllocCtxCom { |
356 | +public: |
357 | + AllocCtxCom (); |
358 | + RackManager * getRackHandlerPtr ( size_t idx ); |
359 | + void cleanup ( size_t idx ); |
360 | + size_t allocIdx (); |
361 | +private: |
362 | + epicsThreadPrivateId m_threadPrivateId; |
363 | + size_t m_curIdx; |
364 | + static void m_threadExitFunc ( void * const pPriv ); |
365 | + static const size_t m_initialCapacity; |
366 | +}; |
367 | + |
368 | +// |
369 | +// G - the discriminator for the thread private variable group |
370 | +// (proper scaling requires an indepent thread private variable |
371 | +// group for each library) any type is allowed and appropriate |
372 | +// for identifying an independent thread private context |
373 | +// |
374 | +template < typename G > class AllocCtxGrouped : public AllocCtxCom {}; |
375 | + |
376 | +// |
377 | +// G - the discriminator for the thread private variable group |
378 | +// (proper scaling requires an indepent thread private variable |
379 | +// group for each library) any type is allowed and appropriate |
380 | +// for identifying an independent thread private context |
381 | +// |
382 | +template < typename G > |
383 | +class AllocCtx { |
384 | +public: |
385 | + AllocCtx (); |
386 | + void cleanup (); |
387 | + RackManager * getRackHandlerPtr (); |
388 | +private: |
389 | + size_t m_idx; |
390 | +}; |
391 | + |
392 | +// |
393 | +// T - the allocation type |
394 | +// G - the discriminator for the thread private variable group |
395 | +// (proper scaling requires an indepent thread private variable |
396 | +// group for each library) any type is allowed and appropriate |
397 | +// for identifying an independent thread private context |
398 | +// |
399 | +template < typename T, typename G > |
400 | +class AllocCtxTyped : public AllocCtx < G > {}; |
401 | + |
402 | +template < bool TRACE > |
403 | +class epicsShareClass AllocCounter; |
404 | + |
405 | +template <> |
406 | +class epicsShareClass AllocCounter < true > { |
407 | +public: |
408 | + AllocCounter (); |
409 | + void increment ( size_t nBytesThisTime, const type_info & ); |
410 | + void decrement ( size_t nBytesThisTime ); |
411 | + size_t rackCount () const { return m_nRacks; } |
412 | + size_t byteCount () const { return m_bytes; } |
413 | + void show ( const type_info & ) const; |
414 | +private: |
415 | + size_t m_nRacksTrace; |
416 | + size_t m_nRacks; |
417 | + size_t m_bytes; |
418 | +}; |
419 | + |
420 | +template <> |
421 | +class epicsShareClass AllocCounter < false > { |
422 | +public: |
423 | + void increment ( size_t nBytesThisTime, |
424 | + const type_info & ) {} |
425 | + void decrement ( size_t nBytesThisTime ) {} |
426 | + size_t rackCount () const { return 0u; } |
427 | + size_t byteCount () const { return 0u; } |
428 | + void show ( const char * pContext ) const {} |
429 | +}; |
430 | + |
431 | +template < typename R, bool TRACE > |
432 | +class RackAlloc < R, rap_pool, TRACE > { |
433 | +public: |
434 | + typedef R Rack; |
435 | + template < typename R0 > |
436 | + struct rebind { |
437 | + typedef RackAlloc < R0, rap_pool, TRACE > other; |
438 | + }; |
439 | + static R * create ( const type_info & ti ); |
440 | + static void destroy ( R * p ); |
441 | + static size_t rackCount (); |
442 | + static size_t byteCount (); |
443 | +private: |
444 | + static AllocCounter < TRACE > m_counter; |
445 | +}; |
446 | + |
447 | +template < typename R, bool TRACE > |
448 | +class RackAlloc < R, rap_freeList, TRACE > { |
449 | +public: |
450 | + template < typename R0 > |
451 | + struct rebind { |
452 | + typedef RackAlloc < R0, rap_freeList, TRACE > other; |
453 | + }; |
454 | + typedef R Rack; |
455 | + RackAlloc (); |
456 | + ~RackAlloc (); |
457 | + R * create ( const type_info & ); |
458 | + void destroy ( R * p ); |
459 | + size_t rackCount () const; |
460 | + size_t byteCount () const; |
461 | +private: |
462 | + typedef epicsMutex Mutex; |
463 | + typedef epicsGuard < epicsMutex > Guard; |
464 | + struct M_Alias { |
465 | + VALGRIND_RED_ZONE ( m_redZoneBefore ) |
466 | + union { |
467 | +# if ALIGNAS_CMPLR_OK |
468 | + alignas ( R :: alignment ) |
469 | + Octet m_rackBuf [ sizeof ( R ) ]; |
470 | +# else |
471 | + union { |
472 | + Octet m_rackBuf [ sizeof ( R ) ]; |
473 | + MaxAlign m_maxAlign; |
474 | + }; |
475 | +# endif |
476 | + struct M_Alias * m_pNext; |
477 | + }; |
478 | + VALGRIND_RED_ZONE ( m_redZoneAfter ) |
479 | + }; |
480 | + M_Alias * m_pAlias; |
481 | + Mutex m_mutex; |
482 | + AllocCounter < TRACE > m_counter; |
483 | + RackAlloc ( const RackAlloc & ) epicsDeleteMethod; |
484 | + RackAlloc operator = ( const RackAlloc & ) epicsDeleteMethod; |
485 | +}; |
486 | + |
487 | +template < typename G > |
488 | +inline AllocCtx < G > :: AllocCtx () : |
489 | + m_idx ( staticInstance < AllocCtxGrouped < G > > ().allocIdx () ) |
490 | +{ |
491 | +} |
492 | + |
493 | +template < typename G > |
494 | +inline RackManager * AllocCtx < G > :: getRackHandlerPtr () |
495 | +{ |
496 | + AllocCtxCom & grp = staticInstance < AllocCtxGrouped < G > > (); |
497 | + return grp.getRackHandlerPtr ( m_idx ); |
498 | +} |
499 | + |
500 | +template < typename G > |
501 | +void AllocCtx < G > :: cleanup () |
502 | +{ |
503 | + AllocCtxCom & grp = staticInstance < AllocCtxGrouped < G > > (); |
504 | + grp.cleanup ( m_idx ); |
505 | +} |
506 | + |
507 | +template < typename R, bool TRACE > |
508 | +inline R * RackAlloc < R, rap_pool, TRACE > :: create ( const type_info & ti ) |
509 | +{ |
510 | + R * const p = new R (); |
511 | + m_counter.increment ( sizeof ( R ), ti ); |
512 | + return p; |
513 | +} |
514 | + |
515 | +template < typename R, bool TRACE > |
516 | +inline void RackAlloc < R, rap_pool, TRACE > :: destroy ( R * p ) |
517 | +{ |
518 | + m_counter.decrement ( sizeof ( R ) ); |
519 | + delete p; |
520 | +} |
521 | + |
522 | +template < typename R, bool TRACE > |
523 | +inline size_t RackAlloc < R, rap_pool, TRACE > :: rackCount () |
524 | +{ |
525 | + return m_counter.rackCount (); |
526 | +} |
527 | + |
528 | +template < typename R, bool TRACE > |
529 | +inline size_t RackAlloc < R, rap_pool, TRACE > :: byteCount () |
530 | +{ |
531 | + return m_counter.byteCount (); |
532 | +} |
533 | + |
534 | +template < typename R, bool TRACE > |
535 | +inline RackAlloc < R, rap_freeList, TRACE > :: RackAlloc () : |
536 | + m_pAlias ( 0 ) |
537 | +{ |
538 | + VALGRIND_CREATE_MEMPOOL_EXT ( this, sizeof (epicsMemChkRedZone), |
539 | + false, VALGRIND_MEMPOOL_METAPOOL ); |
540 | +} |
541 | + |
542 | +template < typename R, bool TRACE > |
543 | +RackAlloc < R, rap_freeList, TRACE > :: ~RackAlloc () |
544 | +{ |
545 | + Guard guard ( m_mutex ); |
546 | + M_Alias * p = m_pAlias; |
547 | + while ( p ) { |
548 | + M_Alias * pNext = p->m_pNext; |
549 | + { |
550 | + delete p; |
551 | + } |
552 | + p = pNext; |
553 | + }; |
554 | + VALGRIND_DESTROY_MEMPOOL ( this ); |
555 | +} |
556 | + |
557 | +template < typename R, bool TRACE > |
558 | +R * RackAlloc < R, rap_freeList, TRACE > :: create ( |
559 | + const type_info & ti ) |
560 | +{ |
561 | + M_Alias * pA = 0; |
562 | + { |
563 | + Guard guard ( m_mutex ); |
564 | + if ( m_pAlias ) { |
565 | + pA = m_pAlias; |
566 | + m_pAlias = m_pAlias->m_pNext; |
567 | + } |
568 | + } |
569 | + if ( ! pA ) { |
570 | + pA = new M_Alias; |
571 | + VALGRIND_MAKE_MEM_NOACCESS ( pA, sizeof ( *pA ) ); |
572 | + } |
573 | + VALGRIND_MEMPOOL_ALLOC ( this, pA->m_rackBuf, |
574 | + sizeof ( pA->m_rackBuf ) ); |
575 | + m_counter.increment ( sizeof ( R ), ti ); |
576 | + |
577 | + return new ( pA->m_rackBuf ) R (); |
578 | +} |
579 | + |
580 | +template < typename R, bool TRACE > |
581 | +void RackAlloc < R, rap_freeList, TRACE > :: destroy ( R * const pR ) |
582 | +{ |
583 | + if ( pR ) { |
584 | + pR->~R (); |
585 | + static const size_t bufOffset = offsetof ( M_Alias, m_rackBuf ); |
586 | + const POctet pOctets = reinterpret_cast < POctet > ( pR ) - bufOffset; |
587 | + M_Alias * const pA = reinterpret_cast < M_Alias * > ( pOctets ); |
588 | + VALGRIND_MEMPOOL_FREE ( this, pA->m_rackBuf ); |
589 | + VALGRIND_MAKE_MEM_UNDEFINED ( &pA->m_pNext, sizeof ( pA->m_pNext ) ); |
590 | + { |
591 | + Guard guard ( m_mutex ); |
592 | + pA->m_pNext = m_pAlias; |
593 | + m_pAlias = pA; |
594 | + } |
595 | + VALGRIND_MAKE_MEM_DEFINED ( &pA->m_pNext, sizeof ( pA->m_pNext ) ); |
596 | + m_counter.decrement ( sizeof ( R ) ); |
597 | + } |
598 | +} |
599 | + |
600 | +template < typename R, bool TRACE > |
601 | +inline size_t RackAlloc < R, rap_freeList, TRACE > :: rackCount () const |
602 | +{ |
603 | + return m_counter.rackCount (); |
604 | +} |
605 | + |
606 | +template < typename R, bool TRACE > |
607 | +inline size_t RackAlloc < R, rap_freeList, TRACE > :: byteCount () const |
608 | +{ |
609 | + return m_counter.byteCount (); |
610 | +} |
611 | + |
612 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
613 | +inline AllocatorArena < T, G, N, P, TRACE > :: AllocatorArena () |
614 | +{ |
615 | +} |
616 | + |
617 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
618 | +template < typename Y > |
619 | +inline AllocatorArena < T, G, N, P, TRACE > :: AllocatorArena ( const Y & ) |
620 | +{ |
621 | +} |
622 | + |
623 | +// address |
624 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
625 | +inline typename AllocatorArena < T, G, N, P, TRACE > :: pointer |
626 | + AllocatorArena < T, G, N, P, TRACE > :: address ( reference r ) |
627 | +{ |
628 | + return & r; |
629 | +} |
630 | + |
631 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
632 | +inline typename AllocatorArena < T, G, N, P, TRACE > :: const_pointer |
633 | + AllocatorArena < T, G, N, P, TRACE > :: address ( const_reference r ) |
634 | +{ |
635 | + return & r; |
636 | +} |
637 | + |
638 | +// |
639 | +// memory allocation |
640 | +// (inline arranges for pNearbyHint to be eliminated |
641 | +// at compile time) |
642 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
643 | +inline typename AllocatorArena < T, G, N, P, TRACE > :: pointer |
644 | + AllocatorArena < T, G, N, P, TRACE > :: allocate ( size_type nAlloc, |
645 | + void_const_pointer pNearbyHint ) |
646 | +{ |
647 | + pointer p; |
648 | + if ( nAlloc == 1u ) { |
649 | +#if defined ( ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE ) |
650 | +# if __cplusplus >= 201703L |
651 | + void * const pVoid = :: operator new ( sizeof (T), |
652 | + alignof ( T ) ); |
653 | +# else |
654 | + // This function is required to return a pointer suitably |
655 | + // aligned to hold an object of any fundamental alignment. |
656 | + void * const pVoid = :: operator new ( sizeof (T) ); |
657 | +# endif |
658 | + p = static_cast < pointer > ( pVoid ); |
659 | +#else |
660 | + p = m_threadPrivateAlloc (); |
661 | +#endif |
662 | + } |
663 | + else { |
664 | +# if __cplusplus >= 201703L |
665 | + void * const pVoid = :: operator new ( sizeof (T) * nAlloc, |
666 | + alignof ( T ) ); |
667 | +# else |
668 | + // This function is required to return a pointer suitably |
669 | + // aligned to hold an object of any fundamental alignment. |
670 | + void * const pVoid = :: operator new ( sizeof (T) * nAlloc ); |
671 | +# endif |
672 | + p = static_cast < pointer > ( pVoid ); |
673 | + } |
674 | + return p; |
675 | +} |
676 | + |
677 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
678 | +inline void AllocatorArena < T, G, N, P, TRACE > :: deallocate ( const pointer p, |
679 | + size_type nAlloc ) |
680 | +{ |
681 | + if ( nAlloc == 1u ) { |
682 | +#if defined ( ALLOCATOR_ARENA_MEMORY_ACCESS_INSPECTION_ACTIVE ) |
683 | + :: operator delete ( p ); |
684 | +#else |
685 | + M_Rack * const pRack = M_Rack :: dealloc ( p ); |
686 | + if ( pRack ) { |
687 | + staticInstance < M_RackAlloc > ().destroy ( pRack ); |
688 | + } |
689 | +#endif |
690 | + } |
691 | + else { |
692 | + :: operator delete ( p ); |
693 | + } |
694 | +} |
695 | + |
696 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
697 | +inline void * AllocatorArena < T, G, N, P, TRACE > :: |
698 | + allocateOctets ( size_type sz ) |
699 | +{ |
700 | + if ( sz == sizeof ( T ) ) { |
701 | + return AllocatorArena :: allocate ( 1u ); |
702 | + } |
703 | + else { |
704 | + // This function is required to return a pointer suitably |
705 | + // aligned to hold an object of any fundamental alignment. |
706 | + return :: operator new ( sz ); |
707 | + } |
708 | +} |
709 | + |
710 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
711 | +inline void * AllocatorArena < T, G, N, P, TRACE > :: |
712 | + allocateOctets ( size_type sz, |
713 | + const std :: nothrow_t & ) |
714 | +{ |
715 | + if ( sz == sizeof ( T ) ) { |
716 | + try { |
717 | + return AllocatorArena :: allocate ( 1u ); |
718 | + } |
719 | + catch ( const std :: bad_alloc & ) { |
720 | + return static_cast < void * > ( 0 ); |
721 | + } |
722 | + } |
723 | + else { |
724 | + // This function is required to return a pointer suitably |
725 | + // aligned to hold an object of any fundamental alignment. |
726 | + return :: operator new ( sz, std :: nothrow ); |
727 | + } |
728 | +} |
729 | + |
730 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
731 | +inline void AllocatorArena < T, G, N, P, TRACE > :: |
732 | + deallocateOctets ( void * const p, size_type sz ) |
733 | +{ |
734 | + if ( sz == sizeof ( T ) ) { |
735 | + T * const pT = static_cast < T * > ( p ); |
736 | + AllocatorArena :: deallocate ( pT, 1u ); |
737 | + } |
738 | + else { |
739 | +#if __cplusplus >= 201400L |
740 | + :: operator delete ( p, sz ); |
741 | +#else |
742 | + :: operator delete ( p ); |
743 | +#endif |
744 | + } |
745 | +} |
746 | + |
747 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
748 | +inline typename AllocatorArena < T, G, N, P, TRACE > :: size_type |
749 | + AllocatorArena < T, G, N, P, TRACE > :: max_size () |
750 | +{ |
751 | + // This class is intended only for use with an allocation size of one; |
752 | + // if requests for more than one object occur it falls back to ordinary |
753 | + // new and delete based allocation. |
754 | + return 1u; |
755 | +} |
756 | + |
757 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
758 | +typename AllocatorArena < T, G, N, P, TRACE > :: pointer |
759 | + AllocatorArena < T, G, N, P, TRACE > :: m_threadPrivateAlloc () |
760 | +{ |
761 | + AllocCtx < G > & ctx = staticInstance < AllocCtxTyped < T, G > > (); |
762 | + RackManager * const pRM = ctx.getRackHandlerPtr (); |
763 | + M_Rack * pRack = static_cast < M_Rack * > ( pRM->m_pRack ); |
764 | + if ( pRack ) { |
765 | + pointer p = static_cast < T * > ( pRack->alloc () ); |
766 | + if ( p ) { |
767 | + if ( pRack->empty () ) { |
768 | + assert ( pRM->m_pThreadExitFunc ); |
769 | + ( *pRM->m_pThreadExitFunc ) ( pRack ); |
770 | + pRM->m_pThreadExitFunc = 0; |
771 | + pRM->m_pRack = 0; |
772 | + } |
773 | + return p; |
774 | + } |
775 | + assert ( pRM->m_pThreadExitFunc ); |
776 | + ( *pRM->m_pThreadExitFunc ) ( pRack ); |
777 | + pRM->m_pThreadExitFunc = 0; |
778 | + pRM->m_pRack = 0; |
779 | + } |
780 | + pRack = staticInstance < M_RackAlloc > ().create ( typeid ( T ) ); |
781 | + assert ( pRack ); |
782 | + pRack->addReference (); |
783 | + pRM->m_pThreadExitFunc = m_rackCleanup; |
784 | + pRM->m_pRack = pRack; |
785 | + pointer p = static_cast < pointer > ( pRack->alloc () ); |
786 | + assert ( p ); |
787 | + return p; |
788 | +} |
789 | + |
790 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
791 | +void AllocatorArena < T, G, N, P, TRACE > :: m_rackCleanup ( void * const pPriv ) |
792 | +{ |
793 | + assert ( pPriv ); |
794 | + M_Rack * pRack = static_cast < M_Rack * > ( pPriv ); |
795 | + if ( pRack->removeReference () == 0u ) { |
796 | + staticInstance < M_RackAlloc > ().destroy ( pRack ); |
797 | + } |
798 | +} |
799 | + |
800 | +#if __cplusplus >= 201103L |
801 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
802 | +template < typename T0, typename ... Args > |
803 | +inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( T0 * p, |
804 | + Args && ... args ) |
805 | +{ |
806 | + void * const pvoid = p; |
807 | + :: new ( pvoid ) T0 ( std :: forward <Args> ( args ) ... ); |
808 | +} |
809 | +#else |
810 | + |
811 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
812 | +template < typename T0, typename A0 > |
813 | +inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( |
814 | + T0 * p, A0 a0 ) |
815 | +{ |
816 | + new ( p ) T ( a0 ); |
817 | +} |
818 | + |
819 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
820 | +template < typename T0, typename A0, typename A1 > |
821 | +inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( |
822 | + T0 * p, A0 a0, A1 a1 ) |
823 | +{ |
824 | + new ( p ) T ( a0, a1 ); |
825 | +} |
826 | + |
827 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
828 | +template < typename T0, typename A0, typename A1, typename A2 > |
829 | +inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( |
830 | + T0 * p, A0 a0, A1 a1, A2 a2 ) |
831 | +{ |
832 | + new ( p ) T ( a0, a1, a2 ); |
833 | +} |
834 | + |
835 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
836 | +template < typename T0, typename A0, typename A1, typename A2, |
837 | + typename A3 > |
838 | +inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( |
839 | + T0 * p, A0 a0, A1 a1, A2 a2, A3 a3 ) |
840 | +{ |
841 | + new ( p ) T ( a0, a1, a2, a3 ); |
842 | +} |
843 | + |
844 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
845 | +template < typename T0, typename A0, typename A1, typename A2, |
846 | + typename A3, typename A4 > |
847 | +inline void AllocatorArena < T, G, N, P, TRACE > :: construct ( |
848 | + T0 * p, A0 a0, A1 a1, A2 a2, A3 a3, |
849 | + A4 a4 ) |
850 | +{ |
851 | + new ( p ) T ( a0, a1, a2, a3, a4 ); |
852 | +} |
853 | + |
854 | +#endif |
855 | + |
856 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
857 | +template < class U > |
858 | +inline void AllocatorArena < T, G, N, P, TRACE > :: destroy ( U * p ) |
859 | +{ |
860 | + p->~U(); |
861 | +} |
862 | + |
863 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
864 | +inline bool AllocatorArena < T, G, N, P, TRACE > :: operator == ( |
865 | + AllocatorArena const & ao ) |
866 | +{ |
867 | + return true; |
868 | +} |
869 | + |
870 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
871 | +inline bool AllocatorArena < T, G, N, P, TRACE > :: operator != ( |
872 | + AllocatorArena const & a ) |
873 | +{ |
874 | + return ! operator == ( a ); |
875 | +} |
876 | + |
877 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
878 | +inline void AllocatorArena < T, G, N, P, TRACE > :: cleanup () |
879 | +{ |
880 | + AllocCtx < G > & ctx = staticInstance < AllocCtxTyped < T, G > > (); |
881 | + ctx.cleanup (); |
882 | +} |
883 | + |
884 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
885 | +inline size_t AllocatorArena < T, G, N, P, TRACE > :: rackCount () |
886 | +{ |
887 | + return staticInstance < M_RackAlloc > ().rackCount(); |
888 | +} |
889 | + |
890 | +template < typename T, typename G, size_t N, RackAllocPolicy P, bool TRACE > |
891 | +inline size_t AllocatorArena < T, G, N, P, TRACE > :: byteCount () |
892 | +{ |
893 | + return staticInstance < M_RackAlloc > ().byteCount(); |
894 | +} |
895 | + |
896 | +template < size_t S, size_t A, size_t N > |
897 | +const size_t Rack < S, A, N > :: number = N; |
898 | + |
899 | +template < size_t S, size_t A, size_t N > |
900 | +const size_t Rack < S, A, N > :: alignment = A; |
901 | + |
902 | +template < size_t S, size_t A, size_t N > |
903 | +inline Rack < S, A, N > :: Rack () : |
904 | + m_nAlloc ( 0u ), |
905 | + m_refCount ( 0u ) |
906 | +{ |
907 | + VALGRIND_MAKE_MEM_NOACCESS ( m_wrapped, sizeof ( m_wrapped ) ); |
908 | +} |
909 | + |
910 | +template < size_t S, size_t A, size_t N > |
911 | +inline Rack < S, A, N > :: ~Rack () |
912 | +{ |
913 | + // if a Rack allocating free list is destroyed, and it destroys |
914 | + // a rack with outstanding references remaining then problems |
915 | + // will ensue |
916 | + assert ( m_refCount == 0u ); |
917 | + VALGRIND_MAKE_MEM_NOACCESS ( m_wrapped, sizeof ( m_wrapped ) ); |
918 | +} |
919 | + |
920 | +template < size_t S, size_t A, size_t N > |
921 | +inline bool Rack < S, A, N > :: empty () const |
922 | +{ |
923 | + return m_nAlloc >= N; |
924 | +} |
925 | + |
926 | +template < size_t S, size_t A, size_t N > |
927 | +void * Rack < S, A, N > :: alloc () |
928 | +{ |
929 | + void * p = 0; |
930 | + if ( m_nAlloc < N ) { |
931 | + M_Wrapper * const pAlloc = & m_wrapped[m_nAlloc]; |
932 | + m_nAlloc++; |
933 | + this->addReference (); |
934 | + VALGRIND_MAKE_MEM_UNDEFINED ( &pAlloc->m_pRack, |
935 | + sizeof ( pAlloc->m_pRack ) ); |
936 | + atomic :: set ( pAlloc->m_pRack, this ); |
937 | + VALGRIND_MAKE_MEM_DEFINED ( &pAlloc->m_pRack, |
938 | + sizeof ( pAlloc->m_pRack ) ); |
939 | + p = static_cast < void * > ( pAlloc->m_bufForObj ); |
940 | + VALGRIND_MALLOCLIKE_BLOCK ( pAlloc->m_bufForObj, |
941 | + sizeof ( pAlloc->m_bufForObj ), |
942 | + sizeof ( epicsMemChkRedZone ), |
943 | + false ); |
944 | + } |
945 | + return p; |
946 | +} |
947 | + |
948 | +template < size_t S, size_t A, size_t N > |
949 | +Rack < S, A , N > * Rack < S, A, N > :: dealloc ( void * const p ) |
950 | +{ |
951 | + static const size_t bufOffset = offsetof ( M_Wrapper, m_bufForObj ); |
952 | + const POctet pOctets = static_cast < POctet > ( p ) - bufOffset; |
953 | + const PWrapper pDealloc = reinterpret_cast < PWrapper > ( pOctets ); |
954 | + VALGRIND_FREELIKE_BLOCK ( pDealloc->m_bufForObj, |
955 | + sizeof ( epicsMemChkRedZone ) ); |
956 | + Rack * const pRack = static_cast < Rack * > |
957 | + ( atomic :: get ( pDealloc->m_pRack ) ); |
958 | + pDealloc->m_pRack = 0; // for imperfect error detection purposes |
959 | + assert ( pRack ); |
960 | + if ( pRack->removeReference () == 0u ) { |
961 | + return pRack; |
962 | + } |
963 | + return 0; |
964 | +} |
965 | + |
966 | +template < size_t S, size_t A, size_t N > |
967 | +inline void Rack < S, A, N > :: addReference () |
968 | +{ |
969 | + assert ( m_refCount < SIZE_MAX ); |
970 | + atomic :: increment ( m_refCount ); |
971 | +} |
972 | + |
973 | +template < size_t S, size_t A, size_t N > |
974 | +inline size_t Rack < S, A, N > :: removeReference () |
975 | +{ |
976 | + assert ( m_refCount > 0u ); |
977 | + return atomic :: decrement ( m_refCount ); |
978 | +} |
979 | + |
980 | +} // end of name space _impl |
981 | + |
982 | +using _impl :: Rack; |
983 | +using _impl :: RackAlloc; |
984 | +using _impl :: RackAllocPolicy; |
985 | +using _impl :: rap_pool; |
986 | +using _impl :: rap_freeList; |
987 | +using _impl :: AllocatorArena; |
988 | + |
989 | +} // end of name space epics |
990 | + |
991 | +#endif // if not defined epicsAllocatorArena_h |
992 | + |
993 | diff --git a/modules/libcom/src/misc/AllocatorArenaUntyped.cpp b/modules/libcom/src/misc/AllocatorArenaUntyped.cpp |
994 | 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 | + |
1000 | +/*************************************************************************\ |
1001 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1002 | +* National Laboratory |
1003 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1004 | +* in file LICENSE that is included with this distribution. |
1005 | +\*************************************************************************/ |
1006 | + |
1007 | +/* |
1008 | + * Author Jeffrey O. Hill |
1009 | + */ |
1010 | + |
1011 | +#include <cstdio> |
1012 | +#include <memory> |
1013 | + |
1014 | +#define epicsExportSharedSymbols |
1015 | +#include "AllocatorArena.h" |
1016 | +#include "errlog.h" |
1017 | +#include "epicsTypes.h" |
1018 | +#include "epicsExit.h" |
1019 | +#include "epicsDemangle.h" |
1020 | + |
1021 | +namespace epics { |
1022 | +namespace _impl { |
1023 | + |
1024 | +const char * ThreadPrivateIdBadAlloc :: what () const throw () |
1025 | +{ |
1026 | + return "epicsThreadPrivateCreate returned nill"; |
1027 | +} |
1028 | + |
1029 | +const char * AtThreadExitBadAlloc :: what () const throw () |
1030 | +{ |
1031 | + return "epicsAtThreadExit was unsuccessful"; |
1032 | +} |
1033 | + |
1034 | +const size_t AllocCtxCom :: m_initialCapacity = 16u; |
1035 | + |
1036 | +AllocCtxCom :: AllocCtxCom () : |
1037 | + m_threadPrivateId ( epicsThreadPrivateCreate () ), |
1038 | + m_curIdx ( 0 ) |
1039 | +{ |
1040 | +} |
1041 | + |
1042 | +union RackManagerUnion { |
1043 | + RackManager m_entry; |
1044 | + size_t m_capacity; |
1045 | +}; |
1046 | + |
1047 | +size_t AllocCtxCom :: allocIdx () |
1048 | +{ |
1049 | + return atomic :: increment ( m_curIdx ); |
1050 | +} |
1051 | + |
1052 | +/* |
1053 | + * from the bit twiddling hacks web site |
1054 | + */ |
1055 | +static inline epicsUInt32 nextPwrOf2 ( epicsUInt32 v ) |
1056 | +{ |
1057 | + v--; |
1058 | + v |= v >> 1u; |
1059 | + v |= v >> 2u; |
1060 | + v |= v >> 4u; |
1061 | + v |= v >> 8u; |
1062 | + v |= v >> 16u; |
1063 | + v++; |
1064 | + return v; |
1065 | +} |
1066 | + |
1067 | +RackManager * AllocCtxCom :: getRackHandlerPtr ( const size_t idx ) |
1068 | +{ |
1069 | + void * const pPriv = epicsThreadPrivateGet ( m_threadPrivateId ); |
1070 | + RackManagerUnion * pThrPriv; |
1071 | + if ( pPriv ) { |
1072 | + pThrPriv = static_cast < RackManagerUnion * > ( pPriv ); |
1073 | + if ( idx >= pThrPriv->m_capacity ) { |
1074 | + const size_t newCapacity = nextPwrOf2 ( idx + 1u ); |
1075 | + RackManagerUnion * const pThrPrivNew = |
1076 | + new RackManagerUnion [newCapacity]; |
1077 | + assert ( newCapacity > idx ); |
1078 | + size_t i; |
1079 | + for ( i = 1u; i < pThrPriv->m_capacity; i++ ) { |
1080 | + pThrPrivNew[i] = pThrPriv[i]; |
1081 | + } |
1082 | + for ( i = pThrPriv->m_capacity; i < newCapacity; i++ ) { |
1083 | + pThrPrivNew[i].m_entry.m_pRack = 0; |
1084 | + pThrPrivNew[i].m_entry.m_pThreadExitFunc = 0; |
1085 | + } |
1086 | + pThrPrivNew->m_capacity = newCapacity; |
1087 | + delete [] pThrPriv; |
1088 | + epicsThreadPrivateSet ( m_threadPrivateId, pThrPrivNew ); |
1089 | + pThrPriv = pThrPrivNew; |
1090 | + } |
1091 | + } |
1092 | + else { |
1093 | + size_t capacity; |
1094 | + if ( idx >= m_initialCapacity ) { |
1095 | + capacity = nextPwrOf2 ( idx + 1u ); |
1096 | + } |
1097 | + else { |
1098 | + capacity = m_initialCapacity; |
1099 | + } |
1100 | + pThrPriv = new RackManagerUnion [capacity]; |
1101 | + pThrPriv->m_capacity = capacity; |
1102 | + for ( size_t i = 1u; i < capacity; i++ ) { |
1103 | + pThrPriv[i].m_entry.m_pRack = 0; |
1104 | + pThrPriv[i].m_entry.m_pThreadExitFunc = 0; |
1105 | + } |
1106 | + int status = epicsAtThreadExit ( m_threadExitFunc, this ); |
1107 | + if ( status != 0 ) { |
1108 | + delete [] pThrPriv; |
1109 | + throw AtThreadExitBadAlloc (); |
1110 | + } |
1111 | + epicsThreadPrivateSet ( m_threadPrivateId, pThrPriv ); |
1112 | + } |
1113 | + return & pThrPriv[idx].m_entry; |
1114 | +} |
1115 | + |
1116 | +void AllocCtxCom :: cleanup ( const size_t idx ) |
1117 | +{ |
1118 | + void * const pPriv = epicsThreadPrivateGet ( m_threadPrivateId ); |
1119 | + if ( pPriv ) { |
1120 | + RackManagerUnion * pThrPriv = |
1121 | + static_cast < RackManagerUnion * > ( pPriv ); |
1122 | + if ( idx < pThrPriv->m_capacity ) { |
1123 | + if ( pThrPriv[idx].m_entry.m_pThreadExitFunc && |
1124 | + pThrPriv[idx].m_entry.m_pRack ) { |
1125 | + ( *pThrPriv[idx].m_entry.m_pThreadExitFunc ) |
1126 | + ( pThrPriv[idx].m_entry.m_pRack ); |
1127 | + pThrPriv[idx].m_entry.m_pRack = 0; |
1128 | + pThrPriv[idx].m_entry.m_pThreadExitFunc = 0; |
1129 | + } |
1130 | + } |
1131 | + } |
1132 | +} |
1133 | + |
1134 | +void AllocCtxCom :: m_threadExitFunc ( void * const pPriv ) |
1135 | +{ |
1136 | + AllocCtxCom * const pCtx = static_cast < AllocCtxCom * > ( pPriv ); |
1137 | + void * const pThrPrivVoid = epicsThreadPrivateGet ( pCtx->m_threadPrivateId ); |
1138 | + if ( pThrPrivVoid ) { |
1139 | + RackManagerUnion * const pThrPriv = |
1140 | + static_cast < RackManagerUnion * > ( pThrPrivVoid ); |
1141 | + for ( size_t i = 1u; i < pThrPriv->m_capacity; i++ ) { |
1142 | + if ( pThrPriv[i].m_entry.m_pThreadExitFunc && |
1143 | + pThrPriv[i].m_entry.m_pRack ) { |
1144 | + ( *pThrPriv[i].m_entry.m_pThreadExitFunc ) |
1145 | + ( pThrPriv[i].m_entry.m_pRack ); |
1146 | + } |
1147 | + } |
1148 | + delete [] pThrPriv; |
1149 | + epicsThreadPrivateSet ( pCtx->m_threadPrivateId, 0 ); |
1150 | + } |
1151 | +} |
1152 | + |
1153 | +AllocCounter < true > :: AllocCounter () |
1154 | +{ |
1155 | + atomic :: set ( m_nRacks, 0u ); |
1156 | + atomic :: set ( m_bytes, 0u ); |
1157 | + atomic :: set ( m_nRacksTrace, 8 ); |
1158 | +} |
1159 | + |
1160 | +static size_t m_nRacksTotal = 0u; |
1161 | +static size_t m_bytesTotal = 0u; |
1162 | + |
1163 | +void AllocCounter < true > :: increment ( size_t nBytesThisTime, |
1164 | + const type_info & ti ) |
1165 | +{ |
1166 | + const size_t newNRacksVal = atomic :: increment ( m_nRacks ); |
1167 | + atomic :: increment ( m_nRacksTotal ); |
1168 | + atomic :: add ( m_bytes, nBytesThisTime ); |
1169 | + atomic :: add ( m_bytesTotal, nBytesThisTime ); |
1170 | + if ( newNRacksVal >= m_nRacksTrace ) { |
1171 | + atomic :: add ( m_nRacksTrace, m_nRacksTrace ); |
1172 | + this->show ( ti ); |
1173 | + } |
1174 | +} |
1175 | + |
1176 | +void AllocCounter < true > :: decrement ( size_t nBytesThisTime ) |
1177 | +{ |
1178 | + atomic :: decrement ( m_nRacks ); |
1179 | + atomic :: decrement ( m_nRacksTotal ); |
1180 | + atomic :: subtract ( m_bytes, nBytesThisTime ); |
1181 | + atomic :: subtract ( m_bytesTotal, nBytesThisTime ); |
1182 | +} |
1183 | + |
1184 | +void AllocCounter < true > :: show ( const type_info & ti ) const |
1185 | +{ |
1186 | + const std :: string name = epicsDemangleTypeName ( ti ); |
1187 | + errlogPrintf ( |
1188 | + "AA C=%08lu SZ=%08lu CT=%08lu SZT=%08lu \"%s\"\n", |
1189 | + ( unsigned long ) m_nRacks, |
1190 | + ( unsigned long ) m_bytes, |
1191 | + ( unsigned long ) m_nRacksTotal, |
1192 | + ( unsigned long ) m_bytesTotal, |
1193 | + name.c_str () ); |
1194 | +} |
1195 | + |
1196 | +} // end of name space _impl |
1197 | +} // 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 | INC += compilerDependencies.h |
1204 | INC += epicsUnitTest.h |
1205 | INC += testMain.h |
1206 | +INC += AllocatorArena.h |
1207 | |
1208 | # epicsVersion.h is created by this Makefile |
1209 | INC += epicsVersion.h |
1210 | @@ -42,3 +43,5 @@ Com_SRCS += epicsString.c |
1211 | Com_SRCS += truncateFile.c |
1212 | Com_SRCS += ipAddrToAsciiAsynchronous.cpp |
1213 | Com_SRCS += epicsUnitTest.c |
1214 | +Com_SRCS += AllocatorArenaUntyped.cpp |
1215 | + |
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 | |
1222 | SRC_DIRS += $(LIBCOM)/osi |
1223 | |
1224 | +# compiler specific |
1225 | INC += compilerDependencies.h |
1226 | INC += compilerSpecific.h |
1227 | +INC += epicsStaticInstance.h |
1228 | +INC += epicsStaticInstanceCD.h |
1229 | +INC += epicsStaticInstanceSaneCmplr.h |
1230 | +INC += epicsStaticInstanceSketchyCmplr.h |
1231 | + |
1232 | +Com_SRCS += epicsStaticInstanceSketchyCmplr.cpp |
1233 | +Com_SRCS += compilerDependentDemangle.cpp |
1234 | |
1235 | INC += osiFileName.h |
1236 | INC += osiSock.h |
1237 | @@ -57,6 +65,7 @@ INC += epicsStdioRedirect.h |
1238 | INC += epicsTempFile.h |
1239 | INC += epicsGetopt.h |
1240 | INC += epicsStackTrace.h |
1241 | +INC += epicsDemangle.h |
1242 | |
1243 | INC += devLib.h |
1244 | INC += devLibVME.h |
1245 | @@ -157,3 +166,4 @@ Com_SRCS_WIN32 += forceBadAllocException.cpp |
1246 | Com_SRCS += epicsStackTrace.c |
1247 | Com_SRCS += osdBackTrace.cpp |
1248 | Com_SRCS += osdFindAddr.c |
1249 | + |
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 | /*************************************************************************\ |
1256 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1257 | +* National Laboratory |
1258 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1259 | * National Laboratory. |
1260 | * Copyright (c) 2002 The Regents of the University of California, as |
1261 | @@ -29,6 +31,8 @@ |
1262 | /* Expands to a 'const char*' which describes the name of the current function scope */ |
1263 | #define EPICS_FUNCTION __PRETTY_FUNCTION__ |
1264 | |
1265 | +#define NO_RETURN __attribute__((noreturn)) |
1266 | + |
1267 | #ifdef __cplusplus |
1268 | |
1269 | /* |
1270 | @@ -36,6 +40,8 @@ |
1271 | */ |
1272 | #define CXX_PLACEMENT_DELETE |
1273 | |
1274 | +#define USING_BASE_TYPE(B,T) using typename B :: T; |
1275 | + |
1276 | #endif /* __cplusplus */ |
1277 | |
1278 | /* |
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 | |
1285 | /*************************************************************************\ |
1286 | -* Copyright (c) 2011 LANS LLC, as Operator of |
1287 | -* Los Alamos National Laboratory. |
1288 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1289 | +* National Laboratory |
1290 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1291 | * National Laboratory. |
1292 | * 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 | 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 | + |
1300 | +/*************************************************************************\ |
1301 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1302 | +* National Laboratory |
1303 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1304 | +* in file LICENSE that is included with this distribution. |
1305 | +\*************************************************************************/ |
1306 | + |
1307 | +/* |
1308 | + * Author: Jeffrey O. Hill |
1309 | + */ |
1310 | +#include <exception> |
1311 | +#include <cstdlib> |
1312 | + |
1313 | +#define epicsExportSharedSymbols |
1314 | +#include "epicsDemangle.h" |
1315 | + |
1316 | +using std :: string; |
1317 | + |
1318 | +std :: string epicsDemangle ( const char * const pMangledName ) |
1319 | +{ |
1320 | + return std :: string ( pMangledName ); |
1321 | +} |
1322 | + |
1323 | +std :: string epicsDemangleTypeName ( const std :: type_info & ti ) |
1324 | +{ |
1325 | + return epicsDemangle ( ti.name () ); |
1326 | +} |
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 | /*************************************************************************\ |
1333 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1334 | +* National Laboratory |
1335 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1336 | * National Laboratory. |
1337 | * Copyright (c) 2002 The Regents of the University of California, as |
1338 | @@ -26,15 +28,18 @@ |
1339 | |
1340 | #ifdef __cplusplus |
1341 | |
1342 | +#define NO_RETURN |
1343 | + |
1344 | + |
1345 | /* |
1346 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete |
1347 | - * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification |
1348 | * |
1349 | * (our default guess is that the compiler implements the C++ 97 standard) |
1350 | */ |
1351 | -#define CXX_THROW_SPECIFICATION |
1352 | #define CXX_PLACEMENT_DELETE |
1353 | |
1354 | +#define USING_BASE_TYPE(B,T) using typename B :: T; |
1355 | + |
1356 | #endif /* __cplusplus */ |
1357 | |
1358 | |
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 | |
1365 | /*************************************************************************\ |
1366 | -* Copyright (c) 2011 LANS LLC, as Operator of |
1367 | -* Los Alamos National Laboratory. |
1368 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1369 | +* National Laboratory |
1370 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1371 | * National Laboratory. |
1372 | * 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 | 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 | + |
1380 | +/*************************************************************************\ |
1381 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1382 | +* National Laboratory |
1383 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1384 | +* in file LICENSE that is included with this distribution. |
1385 | +\*************************************************************************/ |
1386 | + |
1387 | +/* |
1388 | + * Author Jeffrey O. Hill |
1389 | + * johill@lanl.gov |
1390 | + */ |
1391 | + |
1392 | +#ifndef epicsStaticInstanceCD_h |
1393 | +#define epicsStaticInstanceCD_h |
1394 | + |
1395 | +// we dont trust compilers we dont know |
1396 | +# include "epicsStaticInstanceSketchyCmplr.h" |
1397 | + |
1398 | +#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 | 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 | + |
1406 | +/*************************************************************************\ |
1407 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1408 | +* National Laboratory |
1409 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1410 | +* in file LICENSE that is included with this distribution. |
1411 | +\*************************************************************************/ |
1412 | + |
1413 | +/* |
1414 | + * Author: Jeffrey O. Hill |
1415 | + */ |
1416 | +#include <exception> |
1417 | +#include <cstdlib> |
1418 | + |
1419 | +#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 300 |
1420 | +# include <cxxabi.h> |
1421 | +#endif |
1422 | + |
1423 | +#include "epicsThread.h" |
1424 | +#include "epicsAtomic.h" |
1425 | +#include "epicsExit.h" |
1426 | +#include "epicsAssert.h" |
1427 | + |
1428 | +#include "epicsDemangle.h" |
1429 | + |
1430 | +using std :: string; |
1431 | + |
1432 | +union ThreadPrivateId { |
1433 | + ThreadPrivateId () : m_epics ( 0 ) {} |
1434 | + epicsThreadPrivateId m_epics; |
1435 | + void * m_cmpAndSwap; |
1436 | +}; |
1437 | + |
1438 | +class ThreadPrivate { |
1439 | +public: |
1440 | + ThreadPrivate (); |
1441 | + ~ThreadPrivate (); |
1442 | + string demangle ( const char * pMangledName ); |
1443 | + static ThreadPrivate * get (); |
1444 | + static void epicsExit ( void * /* arg */ ); |
1445 | +private: |
1446 | + char * m_pBuf; |
1447 | + size_t m_bufSize; |
1448 | + static const size_t m_bufSizeInit; |
1449 | + static ThreadPrivateId m_privateId; |
1450 | +}; |
1451 | + |
1452 | +const size_t ThreadPrivate :: m_bufSizeInit = 256u; |
1453 | +ThreadPrivateId ThreadPrivate :: m_privateId; |
1454 | + |
1455 | +// |
1456 | +// __cxa_demangle doc indicates that buffer passed must be allocated |
1457 | +// with malloc |
1458 | +// |
1459 | +ThreadPrivate :: ThreadPrivate () : |
1460 | + m_pBuf ( static_cast < char * > ( malloc ( m_bufSizeInit ) ) ), |
1461 | + m_bufSize ( m_pBuf ? m_bufSizeInit : 0u ) |
1462 | +{ |
1463 | +} |
1464 | + |
1465 | +ThreadPrivate :: ~ThreadPrivate () |
1466 | +{ |
1467 | + // |
1468 | + // __cxa_demangle doc indicates that buffer passed must be allocated |
1469 | + // with malloc |
1470 | + // |
1471 | + free ( m_pBuf ); |
1472 | +} |
1473 | + |
1474 | +string ThreadPrivate :: demangle ( const char * const pMangledName ) |
1475 | +{ |
1476 | + if ( ! m_pBuf ) { |
1477 | + // |
1478 | + // __cxa_demangle doc indicates that buffer passed must be |
1479 | + // allocated with malloc |
1480 | + // |
1481 | + m_pBuf = static_cast < char * > ( malloc ( m_bufSizeInit ) ); |
1482 | + if ( m_pBuf ) { |
1483 | + m_bufSize = m_bufSizeInit; |
1484 | + } |
1485 | + else { |
1486 | + m_bufSize = 0u; |
1487 | + return pMangledName; |
1488 | + } |
1489 | + } |
1490 | + int status = -1000; |
1491 | + size_t sz = m_bufSize; |
1492 | + char * const pBufResult = |
1493 | + abi :: __cxa_demangle ( pMangledName, m_pBuf, |
1494 | + & sz, & status ); |
1495 | + if ( pBufResult ) { |
1496 | + m_pBuf = pBufResult; |
1497 | + m_bufSize = sz; |
1498 | + if ( status == 0 ) { |
1499 | + return string ( pBufResult ); |
1500 | + } |
1501 | + else { |
1502 | + return string ( pMangledName ); |
1503 | + } |
1504 | + } |
1505 | + return string ( pMangledName ); |
1506 | +} |
1507 | + |
1508 | +void ThreadPrivate :: epicsExit ( void * p ) |
1509 | +{ |
1510 | + if ( m_privateId.m_epics ) { |
1511 | + epicsThreadPrivateSet ( m_privateId.m_epics, 0 ); |
1512 | + } |
1513 | + ThreadPrivate * const pPriv = static_cast < ThreadPrivate * > ( p ); |
1514 | + delete pPriv; |
1515 | +} |
1516 | + |
1517 | +inline ThreadPrivate * ThreadPrivate :: get () |
1518 | +{ |
1519 | + STATIC_ASSERT ( sizeof ( m_privateId.m_cmpAndSwap ) == |
1520 | + sizeof ( m_privateId.m_epics ) ); |
1521 | + if ( ! m_privateId.m_epics ) { |
1522 | + ThreadPrivateId newId; |
1523 | + newId.m_epics = epicsThreadPrivateCreate (); |
1524 | + if ( ! newId.m_epics ) { |
1525 | + return 0; |
1526 | + } |
1527 | + const EpicsAtomicPtrT pBefore = |
1528 | + epicsAtomicCmpAndSwapPtrT ( & m_privateId.m_cmpAndSwap, |
1529 | + 0, newId.m_cmpAndSwap ); |
1530 | + if ( pBefore ) { |
1531 | + epicsThreadPrivateDelete ( newId.m_epics ); |
1532 | + } |
1533 | + } |
1534 | + void * const p = epicsThreadPrivateGet ( m_privateId.m_epics ); |
1535 | + ThreadPrivate * pPriv = static_cast < ThreadPrivate * > ( p ); |
1536 | + if ( ! pPriv ) { |
1537 | + pPriv = new ( std :: nothrow ) ThreadPrivate (); |
1538 | + if ( pPriv ) { |
1539 | + const int status = |
1540 | + epicsAtThreadExit ( ThreadPrivate :: epicsExit, pPriv ); |
1541 | + if ( status < 0 ) { |
1542 | + delete pPriv; |
1543 | + pPriv = 0; |
1544 | + } |
1545 | + epicsThreadPrivateSet ( m_privateId.m_epics, pPriv ); |
1546 | + } |
1547 | + } |
1548 | + return pPriv; |
1549 | +} |
1550 | + |
1551 | +std :: string epicsDemangle ( const char * const pMangledName ) |
1552 | +{ |
1553 | + if ( pMangledName ) { |
1554 | +# if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 300 |
1555 | + ThreadPrivate * const pPriv = ThreadPrivate :: get (); |
1556 | + if ( ! pPriv ) { |
1557 | + return pMangledName; |
1558 | + } |
1559 | + return pPriv->demangle ( pMangledName ); |
1560 | +# else |
1561 | + return pMangledName; |
1562 | +# endif |
1563 | + } |
1564 | + else { |
1565 | + return "<nil type name>"; |
1566 | + } |
1567 | +} |
1568 | + |
1569 | +std :: string epicsDemangleTypeName ( const std :: type_info & ti ) |
1570 | +{ |
1571 | + return epicsDemangle ( ti.name () ); |
1572 | +} |
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 | /*************************************************************************\ |
1579 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1580 | +* National Laboratory |
1581 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1582 | * National Laboratory. |
1583 | * Copyright (c) 2002 The Regents of the University of California, as |
1584 | @@ -29,6 +31,8 @@ |
1585 | /* Expands to a 'const char*' which describes the name of the current function scope */ |
1586 | #define EPICS_FUNCTION __PRETTY_FUNCTION__ |
1587 | |
1588 | +#define NO_RETURN __attribute__((noreturn)) |
1589 | + |
1590 | #ifdef __cplusplus |
1591 | |
1592 | /* |
1593 | @@ -50,6 +54,12 @@ |
1594 | */ |
1595 | #define EPICS_PRINTF_STYLE(f,a) __attribute__((format(__printf__,f,a))) |
1596 | |
1597 | +#if __GNUC__ > 4 || ( __GNUC__ == 4 && __GNUC_MINOR__ >= 8 ) |
1598 | +# define USING_BASE_TYPE(B,T) using typename B :: T; |
1599 | +#else |
1600 | +# define USING_BASE_TYPE(B,T) typedef typename B :: T T; |
1601 | +#endif |
1602 | + |
1603 | /* |
1604 | * Deprecation marker |
1605 | */ |
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 | |
1612 | /*************************************************************************\ |
1613 | -* Copyright (c) 2011 LANS LLC, as Operator of |
1614 | -* Los Alamos National Laboratory. |
1615 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1616 | +* National Laboratory |
1617 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1618 | * National Laboratory. |
1619 | * 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 | 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 | + |
1627 | +/*************************************************************************\ |
1628 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1629 | +* National Laboratory |
1630 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1631 | +* in file LICENSE that is included with this distribution. |
1632 | +\*************************************************************************/ |
1633 | + |
1634 | +/* |
1635 | + * Author Jeffrey O. Hill |
1636 | + * johill@lanl.gov |
1637 | + */ |
1638 | + |
1639 | +#ifndef epicsStaticInstanceCD_h |
1640 | +#define epicsStaticInstanceCD_h |
1641 | + |
1642 | +#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 |
1643 | + // any gcc version that has -fno-threadsafe-statics |
1644 | + // should be ok |
1645 | +# include "epicsStaticInstanceSaneCmplr.h" |
1646 | +#else // ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 |
1647 | + // other gcc maybe cant be trusted without more testing |
1648 | +# include "epicsStaticInstanceSketchyCmplr.h" |
1649 | +#endif // ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 401 |
1650 | + |
1651 | +#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 | /*************************************************************************\ |
1658 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1659 | +* National Laboratory |
1660 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1661 | * National Laboratory. |
1662 | * Copyright (c) 2002 The Regents of the University of California, as |
1663 | @@ -21,6 +23,8 @@ |
1664 | #endif |
1665 | |
1666 | #define EPICS_ALWAYS_INLINE __forceinline |
1667 | +#define NO_RETURN __declspec(noreturn) |
1668 | + |
1669 | |
1670 | /* Expands to a 'const char*' which describes the name of the current function scope */ |
1671 | #define EPICS_FUNCTION __FUNCTION__ |
1672 | @@ -36,10 +40,10 @@ |
1673 | |
1674 | /* |
1675 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete |
1676 | - * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification |
1677 | */ |
1678 | #define CXX_PLACEMENT_DELETE |
1679 | -#define CXX_THROW_SPECIFICATION |
1680 | + |
1681 | +#define USING_BASE_TYPE(B,T) using typename B :: T; |
1682 | |
1683 | #endif /* __cplusplus */ |
1684 | |
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 | |
1691 | /*************************************************************************\ |
1692 | -* Copyright (c) 2011 LANS LLC, as Operator of |
1693 | -* Los Alamos National Laboratory. |
1694 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1695 | +* National Laboratory |
1696 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1697 | * National Laboratory. |
1698 | * 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 | 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 | + |
1706 | +/*************************************************************************\ |
1707 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1708 | +* National Laboratory |
1709 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1710 | +* in file LICENSE that is included with this distribution. |
1711 | +\*************************************************************************/ |
1712 | + |
1713 | +/* |
1714 | + * Author Jeffrey O. Hill |
1715 | + * johill@lanl.gov |
1716 | + */ |
1717 | + |
1718 | +#ifndef epicsStaticInstanceCD_h |
1719 | +#define epicsStaticInstanceCD_h |
1720 | + |
1721 | +// visual c++ has some issues |
1722 | +// -------------------------- |
1723 | +// |
1724 | +// 1) it lacks mutual exclusion protecting concurrent on demand |
1725 | +// initialization of block scope static variables |
1726 | +// |
1727 | +// 2) it silently adds the path to the .cpp file to the very beginning |
1728 | +// of the include search list so if a msvc specific version of this |
1729 | +// file does not exist and we use the default version then it will |
1730 | +// also use compiler/default/ version, despite the existence |
1731 | +// of a more specialized version, and even if not told to do so on |
1732 | +// the command line |
1733 | +// |
1734 | +# include "epicsStaticInstanceSketchyCmplr.h" |
1735 | + |
1736 | +#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 | /*************************************************************************\ |
1743 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1744 | +* National Laboratory |
1745 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1746 | * National Laboratory. |
1747 | * Copyright (c) 2002 The Regents of the University of California, as |
1748 | @@ -31,11 +33,9 @@ |
1749 | |
1750 | /* |
1751 | * CXX_PLACEMENT_DELETE - defined if compiler supports placement delete |
1752 | - * CXX_THROW_SPECIFICATION - defined if compiler supports throw specification |
1753 | * |
1754 | * (our default guess is that the compiler implements the C++ 97 standard) |
1755 | */ |
1756 | -#define CXX_THROW_SPECIFICATION |
1757 | #define CXX_PLACEMENT_DELETE |
1758 | |
1759 | #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 | |
1766 | /*************************************************************************\ |
1767 | -* Copyright (c) 2011 LANS LLC, as Operator of |
1768 | -* Los Alamos National Laboratory. |
1769 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1770 | +* National Laboratory |
1771 | * Copyright (c) 2011 UChicago Argonne LLC, as Operator of Argonne |
1772 | * National Laboratory. |
1773 | * 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 | /*************************************************************************\ |
1780 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1781 | +* National Laboratory |
1782 | * Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne |
1783 | * National Laboratory. |
1784 | * Copyright (c) 2002 The Regents of the University of California, as |
1785 | @@ -20,6 +22,46 @@ |
1786 | |
1787 | #ifdef __cplusplus |
1788 | |
1789 | +#if __cplusplus >= 201103L |
1790 | +#define epicsOverride override |
1791 | +#else |
1792 | +#define epicsOverride |
1793 | +#endif |
1794 | + |
1795 | +#if __cplusplus >= 201103L |
1796 | +#define epicsFinal final |
1797 | +#else |
1798 | +#define epicsFinal |
1799 | +#endif |
1800 | + |
1801 | +#if __cplusplus >= 201103L |
1802 | +#define epicsNoexcept noexcept |
1803 | +#else |
1804 | +#define epicsNoexcept throw() |
1805 | +#endif |
1806 | + |
1807 | +#if __cplusplus >= 201103L |
1808 | +#define epicsConstexpr constexpr |
1809 | +#else |
1810 | +#define epicsConstexpr |
1811 | +#endif |
1812 | + |
1813 | +#if __cplusplus >= 201103L |
1814 | +#define epicsMove(A) std::move(A) |
1815 | +#else |
1816 | +#define epicsMove(A) (A) |
1817 | +#endif |
1818 | + |
1819 | +/* |
1820 | + * deleted method should also be declared private as per convention |
1821 | + * with old compilers |
1822 | + */ |
1823 | +#if __cplusplus >= 201103L |
1824 | +#define epicsDeleteMethod =delete |
1825 | +#else |
1826 | +#define epicsDeleteMethod |
1827 | +#endif |
1828 | + |
1829 | /* |
1830 | * usage: epicsPlacementDeleteOperator (( void *, myMemoryManager & )) |
1831 | */ |
1832 | diff --git a/modules/libcom/src/osi/epicsDemangle.h b/modules/libcom/src/osi/epicsDemangle.h |
1833 | 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 | + |
1839 | +/*************************************************************************\ |
1840 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1841 | +* National Laboratory |
1842 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1843 | +* in file LICENSE that is included with this distribution. |
1844 | +\*************************************************************************/ |
1845 | + |
1846 | +/* |
1847 | + * Author: Jeffrey O. Hill |
1848 | + */ |
1849 | + |
1850 | +#ifndef epicsDemangleh |
1851 | +#define epicsDemangleh |
1852 | + |
1853 | +#include <string> |
1854 | +#include <typeinfo> |
1855 | + |
1856 | +#include "shareLib.h" |
1857 | + |
1858 | +epicsShareFunc std :: string |
1859 | + epicsDemangle ( const char * const pMangledName ); |
1860 | +epicsShareFunc std :: string |
1861 | + epicsDemangleTypeName ( const std :: type_info & ); |
1862 | + |
1863 | +#endif /* epicsDemangleh */ |
1864 | diff --git a/modules/libcom/src/osi/epicsStaticInstance.h b/modules/libcom/src/osi/epicsStaticInstance.h |
1865 | 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 | +/*************************************************************************\ |
1871 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1872 | +* National Laboratory |
1873 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1874 | +* in file LICENSE that is included with this distribution. |
1875 | +\*************************************************************************/ |
1876 | + |
1877 | +/* |
1878 | + * Author Jeffrey O. Hill |
1879 | + * johill@lanl.gov |
1880 | + */ |
1881 | + |
1882 | +#ifndef epicsStaticInstance_h |
1883 | +#define epicsStaticInstance_h |
1884 | + |
1885 | +namespace epics { |
1886 | + |
1887 | +// |
1888 | +// c++ 11 specifies the behavior for concurrent |
1889 | +// access to block scope statics but some compiler |
1890 | +// writers, lacking clear guidance in the earlier |
1891 | +// c++ standards, curiously implement thread unsafe |
1892 | +// block static variables despite ensuring for |
1893 | +// proper multi-threaded behavior for many other |
1894 | +// aspects of the compiler runtime infrastructure |
1895 | +// such as runtime support for exception handling |
1896 | +// |
1897 | +// T - the type of the initialized once only static |
1898 | +// |
1899 | +template < typename T > T & staticInstance (); |
1900 | + |
1901 | +// |
1902 | +// !!!! perhaps we need versions here that pass parameters |
1903 | +// !!!! to the constructor using templates also |
1904 | +// |
1905 | + |
1906 | +} // end of name space epics |
1907 | + |
1908 | +// |
1909 | +// if the compiler claims its c++ 11 then we are optimistic, ref: |
1910 | +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf |
1911 | +// (see section 6.7) |
1912 | +// |
1913 | +#if __cplusplus >= 201103L |
1914 | +# include "epicsStaticInstanceSaneCmplr.h" |
1915 | +#else |
1916 | + // otherwise we can implement compiler specific behavior |
1917 | +# include "epicsStaticInstanceCD.h" |
1918 | +#endif |
1919 | + |
1920 | +#endif // ifndef epicsStaticInstance_h |
1921 | + |
1922 | diff --git a/modules/libcom/src/osi/epicsStaticInstanceSaneCmplr.h b/modules/libcom/src/osi/epicsStaticInstanceSaneCmplr.h |
1923 | 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 | + |
1929 | +/*************************************************************************\ |
1930 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1931 | +* National Laboratory |
1932 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1933 | +* in file LICENSE that is included with this distribution. |
1934 | +\*************************************************************************/ |
1935 | + |
1936 | +/* |
1937 | + * Author Jeffrey O. Hill |
1938 | + * johill@lanl.gov |
1939 | + */ |
1940 | + |
1941 | +#ifndef epicsStaticInstanceSaneCmplr_h |
1942 | +#define epicsStaticInstanceSaneCmplr_h |
1943 | + |
1944 | +namespace epics { |
1945 | + |
1946 | +// c++ 11 specifies the behavior for concurrent |
1947 | +// access to block scope statics but some compiler |
1948 | +// writers, lacking clear guidance in the earlier |
1949 | +// c++ standards, curiously implement thread unsafe |
1950 | +// block static variables despite ensuring for |
1951 | +// proper multi-threaded behavior for many other |
1952 | +// aspects of the compiler infrastructure such as |
1953 | +// runtime support for exception handling |
1954 | +// |
1955 | +// if the compiler claims its c++ 11 then we are optimistic, ref: |
1956 | +// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf |
1957 | +// (see section 6.7) |
1958 | +// "If control enters the (block-scope variable with static storage duration) |
1959 | +// declaration concurrently while the variable is being initialized, the |
1960 | +// concurrent execution shall wait for completion of the initialization." |
1961 | +// |
1962 | +// also see c++ FAQ, how do I prevent the |
1963 | +// "static initialization order fiasco"? |
1964 | +template < typename T > T & staticInstance () |
1965 | +{ |
1966 | + static T * const m_pInstance = new T; |
1967 | + return * m_pInstance; |
1968 | +} |
1969 | + |
1970 | +} // end of name space epics |
1971 | + |
1972 | +#endif // ifndef epicsStaticInstanceSaneCmplr_h |
1973 | diff --git a/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.cpp b/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.cpp |
1974 | 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 | + |
1980 | +/*************************************************************************\ |
1981 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
1982 | +* National Laboratory |
1983 | +* EPICS BASE is distributed subject to a Software License Agreement found |
1984 | +* in file LICENSE that is included with this distribution. |
1985 | +\*************************************************************************/ |
1986 | + |
1987 | +/* |
1988 | + * Author Jeffrey O. Hill |
1989 | + * johill@lanl.gov |
1990 | + */ |
1991 | + |
1992 | +#include <cstddef> |
1993 | + |
1994 | +#define epicsExportSharedSymbols |
1995 | +#include "errlog.h" |
1996 | +#include "epicsThread.h" |
1997 | +#include "epicsAssert.h" |
1998 | +#include "epicsStaticInstanceSketchyCmplr.h" |
1999 | + |
2000 | +namespace epics { |
2001 | + |
2002 | +epicsShareDef char staticInstanceBusy; |
2003 | + |
2004 | +// |
2005 | +// c++ 0x specifies the behavior for concurrent |
2006 | +// access to block scope statics but some compiler |
2007 | +// writers, lacking clear guidance in the earlier |
2008 | +// c++ standards, curiously implement thread unsafe |
2009 | +// block static variables despite ensuring for |
2010 | +// proper multi-threaded behavior for many other |
2011 | +// aspects of the compiler infrastructure such as |
2012 | +// runtime support for exception handling |
2013 | +// |
2014 | +// see also c++ faq, static initialization order fiasco |
2015 | +// |
2016 | +// This implementation is active if we don't |
2017 | +// know for certain that we can trust the compiler. |
2018 | +// This implementation is substantially more efficient |
2019 | +// at runtime than epicsThreadOnce |
2020 | +// |
2021 | +// we are careful to create T no more than once here |
2022 | +// |
2023 | +EpicsAtomicPtrT staticInstanceInit ( EpicsAtomicPtrT & target, |
2024 | + const PStaticInstanceFactory pFactory ) |
2025 | +{ |
2026 | + static const std :: size_t spinDownInit = 1000u; |
2027 | + static const std :: size_t spinCount = 10u; |
2028 | + STATIC_ASSERT ( spinDownInit > spinCount ); |
2029 | + static const std :: size_t spinThresh = spinDownInit - spinCount; |
2030 | + std :: size_t spinDown = spinDownInit; |
2031 | + EpicsAtomicPtrT pCur = 0; |
2032 | + while ( true ) { |
2033 | + pCur = epics :: atomic :: compareAndSwap ( target, |
2034 | + pStaticInstanceInit, |
2035 | + & staticInstanceBusy ); |
2036 | + if ( pCur == pStaticInstanceInit ) { |
2037 | + try { |
2038 | + pCur = ( * pFactory ) (); |
2039 | + epics :: atomic :: set ( target, pCur ); |
2040 | + break; |
2041 | + } |
2042 | + catch ( ... ) { |
2043 | + epics :: atomic :: set ( target, |
2044 | + pStaticInstanceInit ); |
2045 | + throw; |
2046 | + } |
2047 | + } |
2048 | + else if ( pCur != & staticInstanceBusy ) { |
2049 | + break; |
2050 | + } |
2051 | + if ( spinDown <= spinThresh ) { |
2052 | + epicsThreadSleep ( epicsThreadSleepQuantum () ); |
2053 | + } |
2054 | + if ( spinDown > 0u ) { |
2055 | + spinDown--; |
2056 | + } |
2057 | + else { |
2058 | + errlogPrintf ( "staticInstanceInit: waiting for another " |
2059 | + "thread to finish creating the static instance\n" ); |
2060 | + spinDown = spinThresh; |
2061 | + } |
2062 | + } |
2063 | + return pCur; |
2064 | +} |
2065 | + |
2066 | +} // end of name space epics |
2067 | diff --git a/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.h b/modules/libcom/src/osi/epicsStaticInstanceSketchyCmplr.h |
2068 | 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 | +/*************************************************************************\ |
2074 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
2075 | +* National Laboratory |
2076 | +* EPICS BASE is distributed subject to a Software License Agreement found |
2077 | +* in file LICENSE that is included with this distribution. |
2078 | +\*************************************************************************/ |
2079 | + |
2080 | +/* |
2081 | + * Author Jeffrey O. Hill |
2082 | + * johill@lanl.gov |
2083 | + */ |
2084 | + |
2085 | +#ifndef epicsStaticInstanceSketchyCmplr_h |
2086 | +#define epicsStaticInstanceSketchyCmplr_h |
2087 | + |
2088 | +#include "epicsAtomic.h" |
2089 | + |
2090 | +#include "shareLib.h" |
2091 | + |
2092 | +namespace epics { |
2093 | + |
2094 | +typedef EpicsAtomicPtrT ( * PStaticInstanceFactory ) (); |
2095 | + |
2096 | +epicsShareFunc EpicsAtomicPtrT staticInstanceInit ( EpicsAtomicPtrT & target, |
2097 | + const PStaticInstanceFactory pFactory ); |
2098 | + |
2099 | +epicsShareExtern char staticInstanceBusy; |
2100 | + |
2101 | +namespace { |
2102 | + template < typename T > |
2103 | + EpicsAtomicPtrT staticInstanceFactory () |
2104 | + { |
2105 | + return new T (); |
2106 | + } |
2107 | + static const EpicsAtomicPtrT pStaticInstanceInit = 0; |
2108 | +} |
2109 | + |
2110 | +// this should _not_ be an in line function so that |
2111 | +// we guarantee that only one instance of type T per |
2112 | +// executable is created |
2113 | +template < typename T > T & staticInstance () |
2114 | +{ |
2115 | + static EpicsAtomicPtrT pInstance = pStaticInstanceInit; |
2116 | + EpicsAtomicPtrT pCur = epics :: atomic :: get ( pInstance ); |
2117 | + if ( pCur == pStaticInstanceInit || pCur == & staticInstanceBusy ) { |
2118 | + pCur = staticInstanceInit ( pInstance, staticInstanceFactory < T > ); |
2119 | + } |
2120 | + return * reinterpret_cast < T * > ( pCur ); |
2121 | +} |
2122 | + |
2123 | +} // end of name space epics |
2124 | + |
2125 | +#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 | /*************************************************************************\ |
2132 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
2133 | +* National Laboratory |
2134 | * Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne |
2135 | * National Laboratory. |
2136 | * Copyright (c) 2002 The Regents of the University of California, as |
2137 | @@ -12,151 +14,144 @@ |
2138 | * 505 665 1831 |
2139 | */ |
2140 | |
2141 | +#include <cstdio> |
2142 | #include <string> |
2143 | #include <stdexcept> |
2144 | |
2145 | #define epicsExportSharedSymbols |
2146 | #include "epicsMath.h" |
2147 | #include "epicsTimer.h" |
2148 | -#include "epicsGuard.h" |
2149 | #include "timerPrivate.h" |
2150 | |
2151 | -#ifdef _MSC_VER |
2152 | -# pragma warning ( push ) |
2153 | -# pragma warning ( disable:4660 ) |
2154 | -#endif |
2155 | - |
2156 | -#ifdef _MSC_VER |
2157 | -# pragma warning ( pop ) |
2158 | -#endif |
2159 | - |
2160 | -template class tsFreeList < epicsTimerForC, 0x20 >; |
2161 | - |
2162 | epicsTimer::~epicsTimer () {} |
2163 | |
2164 | -epicsTimerQueueNotify::~epicsTimerQueueNotify () {} |
2165 | +void epicsTimerNotify :: show ( unsigned /* level */ ) const {} |
2166 | |
2167 | -epicsTimerNotify::~epicsTimerNotify () {} |
2168 | - |
2169 | -void epicsTimerNotify::show ( unsigned /* level */ ) const {} |
2170 | - |
2171 | -epicsTimerForC::epicsTimerForC ( timerQueue &queue, epicsTimerCallback pCBIn, void *pPrivateIn ) : |
2172 | - timer ( queue ), pCallBack ( pCBIn ), pPrivate ( pPrivateIn ) |
2173 | +TimerForC :: TimerForC ( timerQueue & queue, epicsTimerCallback const pCB, |
2174 | + void * const pPrivate ) : |
2175 | + m_pTimer ( & queue.createTimerImpl () ), |
2176 | + m_pCallBack ( pCB ), |
2177 | + m_pPrivate ( pPrivate ) |
2178 | { |
2179 | } |
2180 | |
2181 | -epicsTimerForC::~epicsTimerForC () |
2182 | +TimerForC :: ~TimerForC () |
2183 | { |
2184 | + m_pTimer->destroy (); |
2185 | } |
2186 | |
2187 | -void epicsTimerForC::destroy () |
2188 | +void TimerForC :: show ( unsigned level ) const |
2189 | { |
2190 | - timerQueue & queueTmp = this->queue; |
2191 | - this->~epicsTimerForC (); |
2192 | - queueTmp.timerForCFreeList.release ( this ); |
2193 | + printf ( "TimerForC: callback ptr %p private ptr %p\n", |
2194 | + m_pCallBack, m_pPrivate ); |
2195 | + if ( level > 1 && m_pTimer ) { |
2196 | + m_pTimer->show ( level - 1 ); |
2197 | + } |
2198 | } |
2199 | |
2200 | -epicsTimerNotify::expireStatus epicsTimerForC::expire ( const epicsTime & ) |
2201 | +epicsTimerNotify :: expireStatus |
2202 | + TimerForC :: expire ( const epicsTime & ) |
2203 | { |
2204 | - ( *this->pCallBack ) ( this->pPrivate ); |
2205 | + ( *m_pCallBack ) ( m_pPrivate ); |
2206 | return noRestart; |
2207 | } |
2208 | |
2209 | epicsTimerQueueActiveForC :: |
2210 | - epicsTimerQueueActiveForC ( RefMgr & refMgr, |
2211 | - bool okToShare, unsigned priority ) : |
2212 | - timerQueueActive ( refMgr, okToShare, priority ) |
2213 | + epicsTimerQueueActiveForC ( bool okToShare, unsigned priority ) : |
2214 | + timerQueueActive ( okToShare, priority ) |
2215 | { |
2216 | - timerQueueActive::start(); |
2217 | } |
2218 | |
2219 | -epicsTimerQueueActiveForC::~epicsTimerQueueActiveForC () |
2220 | +epicsTimerQueueActiveForC :: ~epicsTimerQueueActiveForC () |
2221 | { |
2222 | } |
2223 | |
2224 | -void epicsTimerQueueActiveForC::release () |
2225 | +void epicsTimerQueueActiveForC :: release () |
2226 | { |
2227 | - _refMgr->release ( *this ); |
2228 | + timerQueueActiveMgr :: master ().release ( *this ); |
2229 | } |
2230 | |
2231 | -epicsTimerQueuePassiveForC::epicsTimerQueuePassiveForC ( |
2232 | - epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, |
2233 | - epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, |
2234 | - void * pPrivateIn ) : |
2235 | - timerQueuePassive ( * static_cast < epicsTimerQueueNotify * > ( this ) ), |
2236 | - pRescheduleCallback ( pRescheduleCallbackIn ), |
2237 | - pSleepQuantumCallback ( pSleepQuantumCallbackIn ), |
2238 | - pPrivate ( pPrivateIn ) |
2239 | +epicsTimerQueuePassiveForC :: epicsTimerQueuePassiveForC ( |
2240 | + epicsTimerQueueNotifyReschedule pRescheduleCallback, |
2241 | + epicsTimerQueueNotifyQuantum pSleepQuantumCallback, |
2242 | + void * pPrivate ) : |
2243 | + timerQueuePassive ( |
2244 | + * static_cast < epicsTimerQueueNotify * > ( this ) ), |
2245 | + m_pRescheduleCallback ( pRescheduleCallback ), |
2246 | + m_pSleepQuantumCallback ( pSleepQuantumCallback ), |
2247 | + m_pPrivate ( pPrivate ) |
2248 | { |
2249 | } |
2250 | |
2251 | -epicsTimerQueuePassiveForC::~epicsTimerQueuePassiveForC () |
2252 | +epicsTimerQueuePassiveForC :: ~epicsTimerQueuePassiveForC () |
2253 | { |
2254 | } |
2255 | |
2256 | -void epicsTimerQueuePassiveForC::reschedule () |
2257 | +void epicsTimerQueuePassiveForC :: reschedule () |
2258 | { |
2259 | - (*this->pRescheduleCallback) ( this->pPrivate ); |
2260 | + ( *m_pRescheduleCallback ) ( m_pPrivate ); |
2261 | } |
2262 | |
2263 | -double epicsTimerQueuePassiveForC::quantum () |
2264 | -{ |
2265 | - return (*this->pSleepQuantumCallback) ( this->pPrivate ); |
2266 | -} |
2267 | - |
2268 | -void epicsTimerQueuePassiveForC::destroy () |
2269 | +void epicsTimerQueuePassiveForC :: destroy () |
2270 | { |
2271 | delete this; |
2272 | } |
2273 | |
2274 | -epicsShareFunc epicsTimerNotify::expireStatus::expireStatus ( restart_t restart ) : |
2275 | - delay ( - DBL_MAX ) |
2276 | +epicsShareFunc epicsTimerNotify :: expireStatus :: |
2277 | + expireStatus ( restart_t restart ) : |
2278 | + m_delay ( -DBL_MAX ) |
2279 | { |
2280 | if ( restart != noRestart ) { |
2281 | throw std::logic_error |
2282 | - ( "timer restart was requested without specifying a delay?" ); |
2283 | + ( "timer restart was requested " |
2284 | + "without specifying a delay?" ); |
2285 | } |
2286 | } |
2287 | |
2288 | -epicsShareFunc epicsTimerNotify::expireStatus::expireStatus |
2289 | - ( restart_t restartIn, const double & expireDelaySec ) : |
2290 | - delay ( expireDelaySec ) |
2291 | +epicsShareFunc epicsTimerNotify :: expireStatus :: expireStatus |
2292 | + ( restart_t restartIn, const double & expireDelaySec ) : |
2293 | + m_delay ( expireDelaySec ) |
2294 | { |
2295 | - if ( restartIn != epicsTimerNotify::restart ) { |
2296 | + if ( restartIn != epicsTimerNotify :: restart ) { |
2297 | throw std::logic_error |
2298 | - ( "no timer restart was requested, but a delay was specified?" ); |
2299 | + ( "no timer restart was requested, " |
2300 | + "but a delay was specified?" ); |
2301 | } |
2302 | - if ( this->delay < 0.0 || !finite(this->delay) ) { |
2303 | + if ( m_delay < 0.0 || ! finite ( m_delay ) ) { |
2304 | throw std::logic_error |
2305 | - ( "timer restart was requested, but a negative delay was specified?" ); |
2306 | + ( "timer restart was requested, but a " |
2307 | + "negative delay was specified?" ); |
2308 | } |
2309 | } |
2310 | |
2311 | -epicsShareFunc bool epicsTimerNotify::expireStatus::restart () const |
2312 | +epicsShareFunc bool |
2313 | + epicsTimerNotify :: expireStatus :: restart () const |
2314 | { |
2315 | - return this->delay >= 0.0 && finite(this->delay); |
2316 | + return m_delay >= 0.0 && finite ( m_delay ); |
2317 | } |
2318 | |
2319 | -epicsShareFunc double epicsTimerNotify::expireStatus::expirationDelay () const |
2320 | +epicsShareFunc double |
2321 | + epicsTimerNotify :: expireStatus :: expirationDelay () const |
2322 | { |
2323 | - if ( this->delay < 0.0 || !finite(this->delay) ) { |
2324 | + if ( m_delay < 0.0 || ! finite ( m_delay ) ) { |
2325 | throw std::logic_error |
2326 | - ( "no timer restart was requested, but you are asking for a restart delay?" ); |
2327 | + ( "no timer restart was requested, " |
2328 | + "but you are asking for a restart delay?" ); |
2329 | } |
2330 | - return this->delay; |
2331 | + return m_delay; |
2332 | } |
2333 | |
2334 | extern "C" epicsTimerQueuePassiveId epicsShareAPI |
2335 | epicsTimerQueuePassiveCreate ( |
2336 | - epicsTimerQueueNotifyReschedule pRescheduleCallbackIn, |
2337 | - epicsTimerQueueNotifyQuantum pSleepQuantumCallbackIn, |
2338 | - void * pPrivateIn ) |
2339 | + const epicsTimerQueueNotifyReschedule pRescheduleCallback, |
2340 | + const epicsTimerQueueNotifyQuantum pSleepQuantumCallback, |
2341 | + void * const pPrivate ) |
2342 | { |
2343 | try { |
2344 | return new epicsTimerQueuePassiveForC ( |
2345 | - pRescheduleCallbackIn, |
2346 | - pSleepQuantumCallbackIn, |
2347 | - pPrivateIn ); |
2348 | + pRescheduleCallback, |
2349 | + pSleepQuantumCallback, |
2350 | + pPrivate ); |
2351 | } |
2352 | catch ( ... ) { |
2353 | return 0; |
2354 | @@ -173,28 +168,31 @@ extern "C" double epicsShareAPI |
2355 | epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId pQueue ) |
2356 | { |
2357 | try { |
2358 | - return pQueue->process ( epicsTime::getCurrent() ); |
2359 | + return pQueue->process ( epicsTime :: getCurrent () ); |
2360 | } |
2361 | catch ( ... ) { |
2362 | return 1.0; |
2363 | } |
2364 | } |
2365 | |
2366 | -extern "C" epicsTimerId epicsShareAPI epicsTimerQueuePassiveCreateTimer ( |
2367 | - epicsTimerQueuePassiveId pQueue, epicsTimerCallback pCallback, void *pArg ) |
2368 | +extern "C" epicsTimerId epicsShareAPI |
2369 | + epicsTimerQueuePassiveCreateTimer ( |
2370 | + epicsTimerQueuePassiveId pQueue, |
2371 | + epicsTimerCallback pCallback, void *pArg ) |
2372 | { |
2373 | try { |
2374 | - return & pQueue->createTimerForC ( pCallback, pArg ); |
2375 | + return & pQueue->createTimerForC ( pCallback, pArg ); |
2376 | } |
2377 | catch ( ... ) { |
2378 | return 0; |
2379 | } |
2380 | } |
2381 | |
2382 | -extern "C" epicsShareFunc void epicsShareAPI epicsTimerQueuePassiveDestroyTimer ( |
2383 | - epicsTimerQueuePassiveId /* pQueue */, epicsTimerId pTmr ) |
2384 | +extern "C" epicsShareFunc void epicsShareAPI |
2385 | + epicsTimerQueuePassiveDestroyTimer ( |
2386 | + epicsTimerQueuePassiveId pQueue, epicsTimerId pTmr ) |
2387 | { |
2388 | - pTmr->destroy (); |
2389 | + delete pTmr; |
2390 | } |
2391 | |
2392 | extern "C" void epicsShareAPI epicsTimerQueuePassiveShow ( |
2393 | @@ -207,11 +205,10 @@ extern "C" epicsTimerQueueId epicsShareAPI |
2394 | epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority ) |
2395 | { |
2396 | try { |
2397 | - epicsSingleton < timerQueueActiveMgr > :: reference ref = |
2398 | - timerQueueMgrEPICS.getReference (); |
2399 | epicsTimerQueueActiveForC & tmr = |
2400 | - ref->allocate ( ref, okToShare ? true : false, threadPriority ); |
2401 | - return &tmr; |
2402 | + timerQueueActiveMgr :: master (). |
2403 | + allocate ( okToShare ? true : false, threadPriority ); |
2404 | + return & tmr; |
2405 | } |
2406 | catch ( ... ) { |
2407 | return 0; |
2408 | @@ -223,8 +220,10 @@ extern "C" void epicsShareAPI epicsTimerQueueRelease ( epicsTimerQueueId pQueue |
2409 | pQueue->release (); |
2410 | } |
2411 | |
2412 | -extern "C" epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( |
2413 | - epicsTimerQueueId pQueue, epicsTimerCallback pCallback, void *pArg ) |
2414 | +extern "C" epicsTimerId epicsShareAPI |
2415 | + epicsTimerQueueCreateTimer ( |
2416 | + epicsTimerQueueId pQueue, |
2417 | + epicsTimerCallback pCallback, void *pArg ) |
2418 | { |
2419 | try { |
2420 | return & pQueue->createTimerForC ( pCallback, pArg ); |
2421 | @@ -234,43 +233,49 @@ extern "C" epicsTimerId epicsShareAPI epicsTimerQueueCreateTimer ( |
2422 | } |
2423 | } |
2424 | |
2425 | -extern "C" void epicsShareAPI epicsTimerQueueShow ( |
2426 | - epicsTimerQueueId pQueue, unsigned int level ) |
2427 | +extern "C" void epicsShareAPI |
2428 | + epicsTimerQueueShow ( |
2429 | + epicsTimerQueueId pQueue, unsigned int level ) |
2430 | { |
2431 | pQueue->show ( level ); |
2432 | } |
2433 | |
2434 | -extern "C" void epicsShareAPI epicsTimerQueueDestroyTimer ( |
2435 | - epicsTimerQueueId /* pQueue */, epicsTimerId pTmr ) |
2436 | +extern "C" void epicsShareAPI |
2437 | + epicsTimerQueueDestroyTimer ( |
2438 | + epicsTimerQueueId pQueue, epicsTimerId pTmr ) |
2439 | { |
2440 | - pTmr->destroy (); |
2441 | + delete pTmr; |
2442 | } |
2443 | |
2444 | -extern "C" void epicsShareAPI epicsTimerStartTime ( |
2445 | - epicsTimerId pTmr, const epicsTimeStamp *pTime ) |
2446 | +extern "C" unsigned epicsShareAPI |
2447 | + epicsTimerStartTime ( |
2448 | + epicsTimerId pTmr, const epicsTimeStamp *pTime ) |
2449 | { |
2450 | - pTmr->start ( *pTmr, *pTime ); |
2451 | + return pTmr->start ( *pTime ); |
2452 | } |
2453 | |
2454 | -extern "C" void epicsShareAPI epicsTimerStartDelay ( |
2455 | - epicsTimerId pTmr, double delaySeconds ) |
2456 | +extern "C" unsigned epicsShareAPI |
2457 | + epicsTimerStartDelay ( |
2458 | + epicsTimerId pTmr, double delaySeconds ) |
2459 | { |
2460 | - pTmr->start ( *pTmr, delaySeconds ); |
2461 | + return pTmr->start ( delaySeconds ); |
2462 | } |
2463 | |
2464 | -extern "C" void epicsShareAPI epicsTimerCancel ( epicsTimerId pTmr ) |
2465 | +extern "C" int epicsShareAPI |
2466 | + epicsTimerCancel ( epicsTimerId pTmr ) |
2467 | { |
2468 | - pTmr->cancel (); |
2469 | + return pTmr->cancel (); |
2470 | } |
2471 | |
2472 | -extern "C" double epicsShareAPI epicsTimerGetExpireDelay ( epicsTimerId pTmr ) |
2473 | +extern "C" double epicsShareAPI |
2474 | + epicsTimerGetExpireDelay ( epicsTimerId pTmr ) |
2475 | { |
2476 | return pTmr->getExpireDelay (); |
2477 | } |
2478 | |
2479 | -extern "C" void epicsShareAPI epicsTimerShow ( |
2480 | - epicsTimerId pTmr, unsigned int level ) |
2481 | +extern "C" void epicsShareAPI |
2482 | + epicsTimerShow ( epicsTimerId pTmr, unsigned int level ) |
2483 | { |
2484 | - pTmr->timer::show ( level ); |
2485 | + pTmr->show ( level ); |
2486 | } |
2487 | |
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 | /*************************************************************************\ |
2494 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
2495 | +* National Laboratory |
2496 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
2497 | * National Laboratory. |
2498 | * Copyright (c) 2002 The Regents of the University of California, as |
2499 | * Operator of Los Alamos National Laboratory. |
2500 | -* EPICS BASE Versions 3.13.7 |
2501 | -* and higher are distributed subject to a Software License Agreement found |
2502 | -* in file LICENSE that is included with this distribution. |
2503 | +* EPICS BASE is distributed subject to a Software License Agreement found |
2504 | +* in file LICENSE that is included with this distribution. |
2505 | \*************************************************************************/ |
2506 | /* epicsTimer.h */ |
2507 | |
2508 | @@ -14,50 +15,80 @@ |
2509 | #ifndef epicsTimerH |
2510 | #define epicsTimerH |
2511 | |
2512 | -#include <float.h> |
2513 | - |
2514 | -#include "shareLib.h" |
2515 | #include "epicsTime.h" |
2516 | #include "epicsThread.h" |
2517 | +#include "shareLib.h" |
2518 | |
2519 | #ifdef __cplusplus |
2520 | |
2521 | +#include <cfloat> |
2522 | + |
2523 | /* |
2524 | * Notes: |
2525 | - * 1) epicsTimer does not hold its lock when calling callbacks. |
2526 | + * 1) The timer queue process method does not hold the timer |
2527 | + * queue lock when calling callbacks, to avoid deadlocks. |
2528 | + * |
2529 | + * 2) The timer start method has three different possible outcomes |
2530 | + * |
2531 | + * 2a) If start is called and the timer isnt pending in the timer |
2532 | + * queue, and the timer callback isnt currently being orchestrated, |
2533 | + * then the timer is schedualed in the queue and start returns |
2534 | + * 1u, indicating that the timer callback will run once as a |
2535 | + * direct consequence of this invocation of start. |
2536 | + * |
2537 | + * 2b) If start is called and the timer is already pending in |
2538 | + * the timer queue, then the timer is reschedualed back into |
2539 | + * a new positon in the queue and start returns 0u. The |
2540 | + * timer callback will run once as a reschedualed (postponed) |
2541 | + * consequence of a previous invocation of start, but zero |
2542 | + * times as a direct consequence of this call to start. |
2543 | + * |
2544 | + * 2c) If start is called and the timer isnt pending in the timer |
2545 | + * queue, but the timer callback _is_ currently being orchestrated |
2546 | + * then it is reschedualed in the queue for a new expiration |
2547 | + * time, and start returns 1u. The timer callback will run twice. |
2548 | + * Once as a consequence of this invocation of start, and once |
2549 | + * as a consequence of a previous call to start. |
2550 | + * |
2551 | + * 3) Cancel returns true if the timer was pending in the queue |
2552 | + * when cancel was called, and false otherwise. |
2553 | */ |
2554 | |
2555 | /* code using a timer must implement epicsTimerNotify */ |
2556 | class epicsShareClass epicsTimerNotify { |
2557 | public: |
2558 | enum restart_t { noRestart, restart }; |
2559 | - class expireStatus { |
2560 | + class epicsShareClass expireStatus { |
2561 | public: |
2562 | - epicsShareFunc expireStatus ( restart_t ); |
2563 | - epicsShareFunc expireStatus ( restart_t, const double & expireDelaySec ); |
2564 | - epicsShareFunc bool restart () const; |
2565 | - epicsShareFunc double expirationDelay () const; |
2566 | + expireStatus ( restart_t ); |
2567 | + expireStatus ( restart_t, const double & expireDelaySec ); |
2568 | + bool restart () const; |
2569 | + double expirationDelay () const; |
2570 | private: |
2571 | - double delay; |
2572 | + double m_delay; |
2573 | }; |
2574 | |
2575 | - virtual ~epicsTimerNotify () = 0; |
2576 | /* return "noRestart" or "expireStatus ( restart, 30.0 )" */ |
2577 | virtual expireStatus expire ( const epicsTime & currentTime ) = 0; |
2578 | virtual void show ( unsigned int level ) const; |
2579 | +protected: |
2580 | + virtual ~epicsTimerNotify () {} // protected disables delete through intf |
2581 | }; |
2582 | |
2583 | class epicsShareClass epicsTimer { |
2584 | public: |
2585 | /* calls cancel (see warning below) and then destroys the timer */ |
2586 | virtual void destroy () = 0; |
2587 | - virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0; |
2588 | - virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0; |
2589 | - /* WARNING: A deadlock will occur if you hold a lock while |
2590 | + /* see note 2 above for the significance of the return value */ |
2591 | + virtual unsigned start ( epicsTimerNotify &, const epicsTime & ) = 0; |
2592 | + virtual unsigned start ( epicsTimerNotify &, double delaySeconds ) = 0; |
2593 | + /* see note 3 for the significance of the return value |
2594 | + * |
2595 | + * WARNING: A deadlock will occur if you hold a lock while |
2596 | * calling this function that you also take within the timer |
2597 | * expiration callback. |
2598 | */ |
2599 | - virtual void cancel () = 0; |
2600 | + virtual bool cancel () = 0; |
2601 | struct expireInfo { |
2602 | expireInfo ( bool active, const epicsTime & expireTime ); |
2603 | bool active; |
2604 | @@ -67,7 +98,7 @@ public: |
2605 | double getExpireDelay (); |
2606 | virtual void show ( unsigned int level ) const = 0; |
2607 | protected: |
2608 | - virtual ~epicsTimer () = 0; /* protected => delete() must not be called */ |
2609 | + virtual ~epicsTimer () = 0; /* disable delete */ |
2610 | }; |
2611 | |
2612 | class epicsTimerQueue { |
2613 | @@ -75,7 +106,7 @@ public: |
2614 | virtual epicsTimer & createTimer () = 0; |
2615 | virtual void show ( unsigned int level ) const = 0; |
2616 | protected: |
2617 | - epicsShareFunc virtual ~epicsTimerQueue () = 0; |
2618 | + virtual ~epicsTimerQueue () {} /* disable delete */ |
2619 | }; |
2620 | |
2621 | class epicsTimerQueueActive |
2622 | @@ -85,7 +116,7 @@ public: |
2623 | bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 ); |
2624 | virtual void release () = 0; |
2625 | protected: |
2626 | - epicsShareFunc virtual ~epicsTimerQueueActive () = 0; |
2627 | + virtual ~epicsTimerQueueActive () {} /* disable delete */ |
2628 | }; |
2629 | |
2630 | class epicsTimerQueueNotify { |
2631 | @@ -93,18 +124,14 @@ public: |
2632 | /* called when a new timer is inserted into the queue and the */ |
2633 | /* delay to the next expire has changed */ |
2634 | virtual void reschedule () = 0; |
2635 | - /* if there is a quantum in the scheduling of timer intervals */ |
2636 | - /* return this quantum in seconds. If unknown then return zero. */ |
2637 | - virtual double quantum () = 0; |
2638 | protected: |
2639 | - epicsShareFunc virtual ~epicsTimerQueueNotify () = 0; |
2640 | + virtual ~epicsTimerQueueNotify () {} /* disable delete */ |
2641 | }; |
2642 | |
2643 | -class epicsTimerQueuePassive |
2644 | - : public epicsTimerQueue { |
2645 | +class epicsTimerQueuePassive : public epicsTimerQueue { |
2646 | public: |
2647 | static epicsShareFunc epicsTimerQueuePassive & create ( epicsTimerQueueNotify & ); |
2648 | - epicsShareFunc virtual ~epicsTimerQueuePassive () = 0; /* ok to call delete */ |
2649 | + virtual ~epicsTimerQueuePassive () {} /* ok to call delete */ |
2650 | virtual double process ( const epicsTime & currentTime ) = 0; /* returns delay to next expire */ |
2651 | }; |
2652 | |
2653 | @@ -114,11 +141,11 @@ inline epicsTimer::expireInfo::expireInfo ( bool activeIn, |
2654 | { |
2655 | } |
2656 | |
2657 | -inline double epicsTimer::getExpireDelay () |
2658 | +inline double epicsTimer :: getExpireDelay () |
2659 | { |
2660 | epicsTimer::expireInfo info = this->getExpireInfo (); |
2661 | if ( info.active ) { |
2662 | - double delay = info.expireTime - epicsTime::getCurrent (); |
2663 | + double delay = info.expireTime - epicsTime :: getCurrent (); |
2664 | if ( delay < 0.0 ) { |
2665 | delay = 0.0; |
2666 | } |
2667 | @@ -130,7 +157,7 @@ inline double epicsTimer::getExpireDelay () |
2668 | extern "C" { |
2669 | #endif /* __cplusplus */ |
2670 | |
2671 | -typedef struct epicsTimerForC * epicsTimerId; |
2672 | +typedef struct TimerForC * epicsTimerId; |
2673 | typedef void ( *epicsTimerCallback ) ( void *pPrivate ); |
2674 | |
2675 | /* thread managed timer queue */ |
2676 | @@ -167,11 +194,11 @@ epicsShareFunc void epicsShareAPI |
2677 | epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId id, unsigned int level ); |
2678 | |
2679 | /* timer */ |
2680 | -epicsShareFunc void epicsShareAPI |
2681 | +epicsShareFunc unsigned epicsShareAPI |
2682 | epicsTimerStartTime ( epicsTimerId id, const epicsTimeStamp *pTime ); |
2683 | -epicsShareFunc void epicsShareAPI |
2684 | +epicsShareFunc unsigned epicsShareAPI |
2685 | epicsTimerStartDelay ( epicsTimerId id, double delaySeconds ); |
2686 | -epicsShareFunc void epicsShareAPI |
2687 | +epicsShareFunc int epicsShareAPI |
2688 | epicsTimerCancel ( epicsTimerId id ); |
2689 | epicsShareFunc double epicsShareAPI |
2690 | 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 | /*************************************************************************\ |
2697 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
2698 | +* National Laboratory |
2699 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
2700 | * National Laboratory. |
2701 | * Copyright (c) 2002 The Regents of the University of California, as |
2702 | * Operator of Los Alamos National Laboratory. |
2703 | -* EPICS BASE Versions 3.13.7 |
2704 | -* and higher are distributed subject to a Software License Agreement found |
2705 | -* in file LICENSE that is included with this distribution. |
2706 | +* EPICS BASE is distributed subject to a Software License Agreement found |
2707 | +* in file LICENSE that is included with this distribution. |
2708 | \*************************************************************************/ |
2709 | |
2710 | /* |
2711 | @@ -17,227 +18,226 @@ |
2712 | #include <typeinfo> |
2713 | #include <string> |
2714 | #include <stdexcept> |
2715 | -#include <stdio.h> |
2716 | +#include <cstdio> |
2717 | |
2718 | #define epicsExportSharedSymbols |
2719 | -#include "epicsGuard.h" |
2720 | #include "timerPrivate.h" |
2721 | #include "errlog.h" |
2722 | |
2723 | -#ifdef _MSC_VER |
2724 | -# pragma warning ( push ) |
2725 | -# pragma warning ( disable:4660 ) |
2726 | -#endif |
2727 | - |
2728 | -template class tsFreeList < timer, 0x20 >; |
2729 | - |
2730 | -#ifdef _MSC_VER |
2731 | -# pragma warning ( pop ) |
2732 | -#endif |
2733 | - |
2734 | -timer::timer ( timerQueue & queueIn ) : |
2735 | - queue ( queueIn ), curState ( stateLimbo ), pNotify ( 0 ) |
2736 | +Timer :: Timer ( timerQueue & queueIn ) : |
2737 | + m_queue ( queueIn ), |
2738 | + m_curState ( stateLimbo ), |
2739 | + m_pNotify ( 0 ), |
2740 | + m_index ( m_invalidIndex ) |
2741 | { |
2742 | } |
2743 | |
2744 | -timer::~timer () |
2745 | +Timer :: ~Timer () |
2746 | { |
2747 | - this->cancel (); |
2748 | + M_CancelStatus cs = { false, false }; |
2749 | + { |
2750 | + Guard guard ( m_queue ); |
2751 | + cs = m_cancelPvt ( guard ); |
2752 | + m_queue.m_numTimers--; |
2753 | + } |
2754 | + // we are careful to wakeup the timer queue thread after |
2755 | + // we nolonger hold the lock |
2756 | + if ( cs.reschedule ) { |
2757 | + m_queue.m_notify.reschedule (); |
2758 | + } |
2759 | } |
2760 | |
2761 | -void timer::destroy () |
2762 | +void Timer :: destroy () |
2763 | { |
2764 | - timerQueue & queueTmp = this->queue; |
2765 | - this->~timer (); |
2766 | - queueTmp.timerFreeList.release ( this ); |
2767 | + delete this; |
2768 | } |
2769 | |
2770 | -void timer::start ( epicsTimerNotify & notify, double delaySeconds ) |
2771 | +unsigned Timer :: start ( epicsTimerNotify & notify, double delaySeconds ) |
2772 | { |
2773 | - this->start ( notify, epicsTime::getCurrent () + delaySeconds ); |
2774 | + const epicsTime current = epicsTime :: getCurrent (); |
2775 | + const epicsTime exp = current + delaySeconds; |
2776 | + const Timer :: M_StartReturn sr = m_privateStart ( notify, exp ); |
2777 | + // we are careful to wakeup the timer queue thread after |
2778 | + // we nolonger hold the lock |
2779 | + if ( sr.resched ) { |
2780 | + m_queue.m_notify.reschedule (); |
2781 | + } |
2782 | + return sr.numNew; |
2783 | } |
2784 | |
2785 | -void timer::start ( epicsTimerNotify & notify, const epicsTime & expire ) |
2786 | +unsigned Timer :: start ( epicsTimerNotify & notify, |
2787 | + const epicsTime & expire ) |
2788 | { |
2789 | - epicsGuard < epicsMutex > locker ( this->queue.mutex ); |
2790 | - this->privateStart ( notify, expire ); |
2791 | + Timer :: M_StartReturn sr = m_privateStart ( notify, expire ); |
2792 | + // we are careful to wakeup the timer queue thread after |
2793 | + // we nolonger hold the lock |
2794 | + if ( sr.resched ) { |
2795 | + m_queue.m_notify.reschedule (); |
2796 | + } |
2797 | + return sr.numNew; |
2798 | } |
2799 | |
2800 | -void timer::privateStart ( epicsTimerNotify & notify, const epicsTime & expire ) |
2801 | +Timer :: M_StartReturn |
2802 | + Timer :: m_privateStart ( epicsTimerNotify & notify, |
2803 | + const epicsTime & expire ) |
2804 | { |
2805 | - this->pNotify = & notify; |
2806 | - this->exp = expire - ( this->queue.notify.quantum () / 2.0 ); |
2807 | - |
2808 | - bool reschedualNeeded = false; |
2809 | - if ( this->curState == stateActive ) { |
2810 | - // above expire time and notify will override any restart parameters |
2811 | - // that may be returned from the timer expire callback |
2812 | - return; |
2813 | - } |
2814 | - else if ( this->curState == statePending ) { |
2815 | - this->queue.timerList.remove ( *this ); |
2816 | - if ( this->queue.timerList.first() == this && |
2817 | - this->queue.timerList.count() > 0 ) { |
2818 | - reschedualNeeded = true; |
2819 | + Timer :: M_StartReturn sr; |
2820 | + Guard locker ( m_queue ); |
2821 | + m_pNotify = & notify; |
2822 | + if ( m_curState == statePending ) { |
2823 | + const epicsTime oldExp = m_queue.m_heap.front ()->m_exp; |
2824 | + m_exp = expire; |
2825 | + if ( ! m_queue.m_fixParent ( m_index ) ) { |
2826 | + m_queue.m_fixChildren ( m_index ); |
2827 | + } |
2828 | + if ( m_queue.m_pExpTmr == this ) { |
2829 | + // new expire time and notify will override |
2830 | + // any restart parameters that may be returned |
2831 | + // from the timer expire callback |
2832 | + sr.numNew = 1u; |
2833 | + sr.resched = false; |
2834 | + } |
2835 | + else { |
2836 | + sr.numNew = 0u; |
2837 | + sr.resched = ( oldExp > m_queue.m_heap.front ()->m_exp ); |
2838 | } |
2839 | } |
2840 | - |
2841 | -# ifdef DEBUG |
2842 | - unsigned preemptCount=0u; |
2843 | -# endif |
2844 | - |
2845 | - // |
2846 | - // insert into the pending queue |
2847 | - // |
2848 | - // Finds proper time sorted location using a linear search. |
2849 | - // |
2850 | - // **** this should use a binary tree ???? |
2851 | - // |
2852 | - tsDLIter < timer > pTmr = this->queue.timerList.lastIter (); |
2853 | - while ( true ) { |
2854 | - if ( ! pTmr.valid () ) { |
2855 | - // |
2856 | - // add to the beginning of the list |
2857 | - // |
2858 | - this->queue.timerList.push ( *this ); |
2859 | - reschedualNeeded = true; |
2860 | - break; |
2861 | + else { |
2862 | + sr.numNew = 1u; |
2863 | + m_curState = Timer :: statePending; |
2864 | + m_index = m_queue.m_heap.size (); |
2865 | + if ( m_index > 0u ) { |
2866 | + const epicsTime oldExp = m_queue.m_heap.front ()->m_exp; |
2867 | + m_exp = expire; |
2868 | + m_queue.m_heap.push_back ( this ); |
2869 | + m_queue.m_fixParent ( m_index ); |
2870 | + sr.resched = ( oldExp > m_queue.m_heap.front ()->m_exp ); |
2871 | } |
2872 | - if ( pTmr->exp <= this->exp ) { |
2873 | - // |
2874 | - // add after the item found that expires earlier |
2875 | - // |
2876 | - this->queue.timerList.insertAfter ( *this, *pTmr ); |
2877 | - break; |
2878 | + else { |
2879 | + m_exp = expire; |
2880 | + m_queue.m_heap.push_back ( this ); |
2881 | + sr.resched = true; |
2882 | } |
2883 | -# ifdef DEBUG |
2884 | - preemptCount++; |
2885 | -# endif |
2886 | - --pTmr; |
2887 | } |
2888 | + debugPrintf ( ("Start of \"%s\" with delay %f at %p\n", |
2889 | + m_pNotify ? |
2890 | + typeid ( *m_pNotify ).name () : |
2891 | + typeid ( m_pNotify ).name (), |
2892 | + m_exp - epicsTime :: getCurrent (), |
2893 | + this ) ); |
2894 | + return sr; |
2895 | +} |
2896 | |
2897 | - this->curState = timer::statePending; |
2898 | - |
2899 | - if ( reschedualNeeded ) { |
2900 | - this->queue.notify.reschedule (); |
2901 | - } |
2902 | - |
2903 | -# if defined(DEBUG) && 0 |
2904 | - this->show ( 10u ); |
2905 | - this->queue.show ( 10u ); |
2906 | -# endif |
2907 | - |
2908 | - debugPrintf ( ("Start of \"%s\" with delay %f at %p preempting %u\n", |
2909 | - typeid ( this->notify ).name (), |
2910 | - expire - epicsTime::getCurrent (), |
2911 | - this, preemptCount ) ); |
2912 | +void Timer :: m_remove ( Guard & guard ) |
2913 | +{ |
2914 | + Timer * const pMoved = m_queue.m_heap.back (); |
2915 | + m_queue.m_heap.pop_back (); |
2916 | + if ( m_index != m_queue.m_heap.size () ) { |
2917 | + const size_t oldIndex = m_index; |
2918 | + m_queue.m_heap[oldIndex] = pMoved; |
2919 | + pMoved->m_index = oldIndex; |
2920 | + if ( ! m_queue.m_fixParent ( oldIndex ) ) { |
2921 | + m_queue.m_fixChildren ( oldIndex ); |
2922 | + } |
2923 | + } |
2924 | + m_index = m_invalidIndex; |
2925 | + m_curState = stateLimbo; |
2926 | } |
2927 | |
2928 | -void timer::cancel () |
2929 | +bool Timer :: cancel () |
2930 | { |
2931 | - bool reschedual = false; |
2932 | - bool wakeupCancelBlockingThreads = false; |
2933 | + M_CancelStatus cs = { false, false }; |
2934 | { |
2935 | - epicsGuard < epicsMutex > locker ( this->queue.mutex ); |
2936 | - this->pNotify = 0; |
2937 | - if ( this->curState == statePending ) { |
2938 | - this->queue.timerList.remove ( *this ); |
2939 | - this->curState = stateLimbo; |
2940 | - if ( this->queue.timerList.first() == this && |
2941 | - this->queue.timerList.count() > 0 ) { |
2942 | - reschedual = true; |
2943 | - } |
2944 | - } |
2945 | - else if ( this->curState == stateActive ) { |
2946 | - this->queue.cancelPending = true; |
2947 | - this->curState = timer::stateLimbo; |
2948 | - if ( this->queue.processThread != epicsThreadGetIdSelf() ) { |
2949 | - // make certain timer expire() does not run after cancel () returns, |
2950 | - // but dont require that lock is applied while calling expire() |
2951 | - while ( this->queue.cancelPending && |
2952 | - this->queue.pExpireTmr == this ) { |
2953 | - epicsGuardRelease < epicsMutex > autoRelease ( locker ); |
2954 | - this->queue.cancelBlockingEvent.wait (); |
2955 | + Guard guard ( m_queue ); |
2956 | + cs = m_cancelPvt ( guard ); |
2957 | + } |
2958 | + // we are careful to wakeup the timer queue thread after |
2959 | + // we nolonger hold the lock |
2960 | + if ( cs.reschedule ) { |
2961 | + m_queue.m_notify.reschedule (); |
2962 | + } |
2963 | + return cs.wasPending; |
2964 | +} |
2965 | + |
2966 | +Timer :: M_CancelStatus Timer :: m_cancelPvt ( Guard & gd ) |
2967 | +{ |
2968 | + gd.assertIdenticalMutex ( m_queue ); |
2969 | + M_CancelStatus cs = { false, false }; |
2970 | + Guard guard ( m_queue ); |
2971 | + if ( m_curState == statePending ) { |
2972 | + const epicsTime oldExp = m_queue.m_heap.front ()->m_exp; |
2973 | + m_remove ( guard ); |
2974 | + m_queue.m_cancelPending = ( m_queue.m_pExpTmr == this ); |
2975 | + if ( m_queue.m_cancelPending ) { |
2976 | + if ( m_queue.m_processThread != epicsThreadGetIdSelf() ) { |
2977 | + // 1) make certain timer expire cllback does not run |
2978 | + // after this cancel method returns |
2979 | + // 2) dont require that lock is applied while calling |
2980 | + // expire callback |
2981 | + // 3) assume that timer could be deleted in its |
2982 | + // expire callback so we dont touch this after lock |
2983 | + // is released |
2984 | + timerQueue & queue = m_queue; |
2985 | + while ( queue.m_cancelPending && |
2986 | + queue.m_pExpTmr == this ) { |
2987 | + GuardRelease unguard ( guard ); |
2988 | + queue.m_cancelBlockingEvent.wait (); |
2989 | } |
2990 | // in case other threads are waiting |
2991 | - wakeupCancelBlockingThreads = true; |
2992 | + queue.m_cancelBlockingEvent.signal (); |
2993 | + } |
2994 | + } |
2995 | + else { |
2996 | + cs.wasPending = true; |
2997 | + if ( oldExp > m_queue.m_heap.front ()->m_exp ) { |
2998 | + cs.reschedule = true; |
2999 | } |
3000 | } |
3001 | } |
3002 | - if ( reschedual ) { |
3003 | - this->queue.notify.reschedule (); |
3004 | - } |
3005 | - if ( wakeupCancelBlockingThreads ) { |
3006 | - this->queue.cancelBlockingEvent.signal (); |
3007 | - } |
3008 | + return cs; |
3009 | } |
3010 | |
3011 | -epicsTimer::expireInfo timer::getExpireInfo () const |
3012 | +epicsTimer :: expireInfo Timer :: getExpireInfo () const |
3013 | { |
3014 | // taking a lock here guarantees that users will not |
3015 | // see brief intervals when a timer isnt active because |
3016 | // it is is canceled when start is called |
3017 | - epicsGuard < epicsMutex > locker ( this->queue.mutex ); |
3018 | - if ( this->curState == statePending || this->curState == stateActive ) { |
3019 | - return expireInfo ( true, this->exp ); |
3020 | + Guard locker ( m_queue ); |
3021 | + if ( m_curState == statePending ) { |
3022 | + return expireInfo ( true, m_exp ); |
3023 | } |
3024 | return expireInfo ( false, epicsTime() ); |
3025 | } |
3026 | |
3027 | -void timer::show ( unsigned int level ) const |
3028 | +void Timer :: show ( unsigned int level ) const |
3029 | { |
3030 | - epicsGuard < epicsMutex > locker ( this->queue.mutex ); |
3031 | + Guard locker ( m_queue ); |
3032 | double delay; |
3033 | - if ( this->curState == statePending || this->curState == stateActive ) { |
3034 | + if ( m_curState == statePending ) { |
3035 | try { |
3036 | - delay = this->exp - epicsTime::getCurrent(); |
3037 | + delay = m_exp - epicsTime :: getCurrent (); |
3038 | } |
3039 | catch ( ... ) { |
3040 | - delay = - DBL_MAX; |
3041 | + delay = -DBL_MAX; |
3042 | } |
3043 | } |
3044 | else { |
3045 | delay = -DBL_MAX; |
3046 | } |
3047 | const char *pStateName; |
3048 | - if ( this->curState == statePending ) { |
3049 | + if ( m_curState == statePending ) { |
3050 | pStateName = "pending"; |
3051 | } |
3052 | - else if ( this->curState == stateActive ) { |
3053 | - pStateName = "active"; |
3054 | - } |
3055 | - else if ( this->curState == stateLimbo ) { |
3056 | + else if ( m_curState == stateLimbo ) { |
3057 | pStateName = "limbo"; |
3058 | } |
3059 | else { |
3060 | pStateName = "corrupt"; |
3061 | } |
3062 | - printf ( "timer, state = %s, delay = %f\n", |
3063 | - pStateName, delay ); |
3064 | - if ( level >= 1u && this->pNotify ) { |
3065 | - this->pNotify->show ( level - 1u ); |
3066 | + printf ( "Timer, state = %s, index = %lu, delay = %f\n", |
3067 | + pStateName, (unsigned long ) m_index, delay ); |
3068 | + if ( level >= 1u && m_pNotify ) { |
3069 | + m_pNotify->show ( level - 1u ); |
3070 | } |
3071 | } |
3072 | |
3073 | -void timer::operator delete ( void * ) |
3074 | -{ |
3075 | - // Visual C++ .net appears to require operator delete if |
3076 | - // placement operator delete is defined? I smell a ms rat |
3077 | - // because if I declare placement new and delete, but |
3078 | - // comment out the placement delete definition there are |
3079 | - // no undefined symbols. |
3080 | - errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", |
3081 | - __FILE__, __LINE__ ); |
3082 | -} |
3083 | - |
3084 | -void epicsTimerForC::operator delete ( void * ) |
3085 | -{ |
3086 | - // Visual C++ .net appears to require operator delete if |
3087 | - // placement operator delete is defined? I smell a ms rat |
3088 | - // because if I declare placement new and delete, but |
3089 | - // comment out the placement delete definition there are |
3090 | - // no undefined symbols. |
3091 | - errlogPrintf ( "%s:%d this compiler is confused about placement delete - memory was probably leaked", |
3092 | - __FILE__, __LINE__ ); |
3093 | -} |
3094 | - |
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 | /*************************************************************************\ |
3101 | -* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne |
3102 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
3103 | +* National Laboratory |
3104 | +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
3105 | * National Laboratory. |
3106 | * Copyright (c) 2002 The Regents of the University of California, as |
3107 | * Operator of Los Alamos National Laboratory. |
3108 | @@ -7,6 +9,7 @@ |
3109 | * in file LICENSE that is included with this distribution. |
3110 | \*************************************************************************/ |
3111 | /* |
3112 | + * |
3113 | * Author Jeffrey O. Hill |
3114 | * johill@lanl.gov |
3115 | * 505 665 1831 |
3116 | @@ -16,12 +19,15 @@ |
3117 | #define epicsTimerPrivate_h |
3118 | |
3119 | #include <typeinfo> |
3120 | +#include <vector> |
3121 | |
3122 | -#include "tsFreeList.h" |
3123 | -#include "epicsSingleton.h" |
3124 | #include "tsDLList.h" |
3125 | #include "epicsTimer.h" |
3126 | +#include "epicsMutex.h" |
3127 | +#include "epicsGuard.h" |
3128 | #include "compilerDependencies.h" |
3129 | +#include "epicsStaticInstance.h" |
3130 | +#include "AllocatorArena.h" |
3131 | |
3132 | #ifdef DEBUG |
3133 | # define debugPrintf(ARGSINPAREN) printf ARGSINPAREN |
3134 | @@ -29,90 +35,130 @@ |
3135 | # define debugPrintf(ARGSINPAREN) |
3136 | #endif |
3137 | |
3138 | -template < class T > class epicsGuard; |
3139 | +using std :: type_info; |
3140 | + |
3141 | +class Timer; |
3142 | +class timerQueue; |
3143 | |
3144 | -class timer : public epicsTimer, public tsDLNode < timer > { |
3145 | +bool operator < ( const Timer &, const Timer & ); |
3146 | +bool operator > ( const Timer &, const Timer & ); |
3147 | +bool operator <= ( const Timer &, const Timer & ); |
3148 | +bool operator >= ( const Timer &, const Timer & ); |
3149 | + |
3150 | +class Timer : public epicsTimer { |
3151 | public: |
3152 | + typedef epicsMutex Mutex; |
3153 | + typedef epicsGuard < Mutex > Guard; |
3154 | + typedef epicsGuardRelease < Mutex > GuardRelease; |
3155 | + Timer ( timerQueue & ); |
3156 | + ~Timer (); |
3157 | void destroy (); |
3158 | - void start ( class epicsTimerNotify &, const epicsTime & ); |
3159 | - void start ( class epicsTimerNotify &, double delaySeconds ); |
3160 | - void cancel (); |
3161 | + unsigned start ( class epicsTimerNotify &, const epicsTime & ); |
3162 | + unsigned start ( class epicsTimerNotify &, double delaySeconds ); |
3163 | + bool cancel (); |
3164 | expireInfo getExpireInfo () const; |
3165 | + double getExpireDelay ( const epicsTime & currentTime ); |
3166 | void show ( unsigned int level ) const; |
3167 | - void * operator new ( size_t size, tsFreeList < timer, 0x20 > & ); |
3168 | - epicsPlacementDeleteOperator (( void *, tsFreeList < timer, 0x20 > & )) |
3169 | + static void * operator new ( size_t sz ); |
3170 | + static void operator delete ( void * ptr, size_t sz ); |
3171 | protected: |
3172 | - timer ( class timerQueue & ); |
3173 | - ~timer (); |
3174 | - timerQueue & queue; |
3175 | + timerQueue & m_queue; |
3176 | private: |
3177 | - enum state { statePending = 45, stateActive = 56, stateLimbo = 78 }; |
3178 | - epicsTime exp; // experation time |
3179 | - state curState; // current state |
3180 | - epicsTimerNotify * pNotify; // callback |
3181 | - void privateStart ( epicsTimerNotify & notify, const epicsTime & ); |
3182 | - timer & operator = ( const timer & ); |
3183 | - // Visual C++ .net appears to require operator delete if |
3184 | - // placement operator delete is defined? I smell a ms rat |
3185 | - // because if I declare placement new and delete, but |
3186 | - // comment out the placement delete definition there are |
3187 | - // no undefined symbols. |
3188 | - void operator delete ( void * ); |
3189 | + typedef epics :: AllocatorArena < Timer, timerQueue, 16u > M_Allocator; |
3190 | + enum state { |
3191 | + statePending = 45, |
3192 | + stateLimbo = 78 }; |
3193 | + epicsTime m_exp; // experation time |
3194 | + state m_curState; // current state |
3195 | + epicsTimerNotify * m_pNotify; // callback |
3196 | + size_t m_index; |
3197 | + static const size_t m_invalidIndex = |
3198 | + ~ static_cast < size_t > ( 0u ); |
3199 | + struct M_StartReturn { |
3200 | + unsigned numNew; |
3201 | + bool resched; |
3202 | + }; |
3203 | + M_StartReturn m_privateStart ( epicsTimerNotify & notify, |
3204 | + const epicsTime & expire ); |
3205 | + struct M_CancelStatus { |
3206 | + bool reschedule; |
3207 | + bool wasPending; |
3208 | + }; |
3209 | + M_CancelStatus m_cancelPvt ( Guard & ); |
3210 | + void m_remove ( Guard & guard ); |
3211 | + Timer & operator = ( const Timer & ); |
3212 | friend class timerQueue; |
3213 | + friend bool operator < ( const Timer &, const Timer & ); |
3214 | + friend bool operator > ( const Timer &, const Timer & ); |
3215 | + friend bool operator <= ( const Timer &, const Timer & ); |
3216 | + friend bool operator >= ( const Timer &, const Timer & ); |
3217 | }; |
3218 | |
3219 | -struct epicsTimerForC : public epicsTimerNotify, public timer { |
3220 | +struct TimerForC : public epicsTimerNotify { |
3221 | public: |
3222 | - void destroy (); |
3223 | -protected: |
3224 | - epicsTimerForC ( timerQueue &, epicsTimerCallback, void *pPrivateIn ); |
3225 | - ~epicsTimerForC (); |
3226 | - void * operator new ( size_t size, tsFreeList < epicsTimerForC, 0x20 > & ); |
3227 | - epicsPlacementDeleteOperator (( void *, tsFreeList < epicsTimerForC, 0x20 > & )) |
3228 | -private: |
3229 | - epicsTimerCallback pCallBack; |
3230 | - void * pPrivate; |
3231 | + typedef epicsMutex Mutex; |
3232 | + typedef epicsGuard < Mutex > Guard; |
3233 | + TimerForC ( class timerQueue &, epicsTimerCallback, void * pPrivateIn ); |
3234 | + ~TimerForC (); |
3235 | + unsigned start ( const epicsTime & ); |
3236 | + unsigned start ( double delaySeconds ); |
3237 | + bool cancel (); |
3238 | + void show ( unsigned level ) const; |
3239 | expireStatus expire ( const epicsTime & currentTime ); |
3240 | - epicsTimerForC & operator = ( const epicsTimerForC & ); |
3241 | - // Visual C++ .net appears to require operator delete if |
3242 | - // placement operator delete is defined? I smell a ms rat |
3243 | - // because if I declare placement new and delete, but |
3244 | - // comment out the placement delete definition there are |
3245 | - // no undefined symbols. |
3246 | - void operator delete ( void * ); |
3247 | - friend class timerQueue; |
3248 | -}; |
3249 | - |
3250 | -using std :: type_info; |
3251 | + double getExpireDelay (); |
3252 | + static void * operator new ( size_t sz ); |
3253 | + static void operator delete ( void * ptr, size_t sz ); |
3254 | +private: |
3255 | + typedef epics :: AllocatorArena < TimerForC, timerQueue, 16u > M_Allocator; |
3256 | + Timer * m_pTimer; |
3257 | + epicsTimerCallback m_pCallBack; |
3258 | + void * m_pPrivate; |
3259 | + TimerForC & operator = ( const TimerForC & ); |
3260 | +}; |
3261 | |
3262 | -class timerQueue : public epicsTimerQueue { |
3263 | +class timerQueue : |
3264 | + public epicsTimerQueue, |
3265 | + public epicsMutex { |
3266 | public: |
3267 | + typedef epicsMutex Mutex; |
3268 | + typedef epicsGuard < Mutex > Guard; |
3269 | + typedef epicsGuardRelease < Mutex > GuardRelease; |
3270 | timerQueue ( epicsTimerQueueNotify ¬ify ); |
3271 | virtual ~timerQueue (); |
3272 | epicsTimer & createTimer (); |
3273 | - epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); |
3274 | + Timer & createTimerImpl (); |
3275 | + TimerForC & createTimerForC ( |
3276 | + epicsTimerCallback pCallback, void *pArg ); |
3277 | double process ( const epicsTime & currentTime ); |
3278 | + double process ( Guard &, const epicsTime & currentTime ); |
3279 | void show ( unsigned int level ) const; |
3280 | + void show ( Guard &, unsigned int level ) const; |
3281 | private: |
3282 | - tsFreeList < timer, 0x20 > timerFreeList; |
3283 | - tsFreeList < epicsTimerForC, 0x20 > timerForCFreeList; |
3284 | - mutable epicsMutex mutex; |
3285 | - epicsEvent cancelBlockingEvent; |
3286 | - tsDLList < timer > timerList; |
3287 | - epicsTimerQueueNotify & notify; |
3288 | - timer * pExpireTmr; |
3289 | - epicsThreadId processThread; |
3290 | - epicsTime exceptMsgTimeStamp; |
3291 | - bool cancelPending; |
3292 | - static const double exceptMsgMinPeriod; |
3293 | - void printExceptMsg ( const char * pName, |
3294 | - const type_info & type ); |
3295 | + epicsEvent m_cancelBlockingEvent; |
3296 | + std :: vector < Timer * > m_heap; |
3297 | + epicsTime m_exceptMsgTimeStamp; |
3298 | + epicsTimerQueueNotify & m_notify; |
3299 | + Timer * m_pExpTmr; |
3300 | + epicsThreadId m_processThread; |
3301 | + size_t m_numTimers; |
3302 | + bool m_cancelPending; |
3303 | + static const double m_exceptMsgMinPeriod; |
3304 | timerQueue ( const timerQueue & ); |
3305 | timerQueue & operator = ( const timerQueue & ); |
3306 | - friend class timer; |
3307 | - friend struct epicsTimerForC; |
3308 | + void m_printExceptMsg ( const char * pName, const type_info & type ); |
3309 | + bool m_fixParent ( size_t childIdx ); |
3310 | + void m_fixChildren ( size_t parentIdx ); |
3311 | + void m_swapEntries ( size_t idx0, size_t idx1 ); |
3312 | + double m_expDelay ( const epicsTime & currentTime ); |
3313 | + static size_t m_parent ( size_t childIdx ); |
3314 | + static size_t m_leftChild ( size_t parentIdx ); |
3315 | + static size_t m_rightChild ( size_t parentIdx ); |
3316 | + friend class Timer; |
3317 | + friend struct TimerForC; |
3318 | }; |
3319 | |
3320 | +class timerQueueActiveMgr; |
3321 | + |
3322 | class timerQueueActiveMgrPrivate { |
3323 | public: |
3324 | timerQueueActiveMgrPrivate (); |
3325 | @@ -123,101 +169,95 @@ private: |
3326 | friend class timerQueueActiveMgr; |
3327 | }; |
3328 | |
3329 | -class timerQueueActiveMgr; |
3330 | - |
3331 | -class timerQueueActive : public epicsTimerQueueActive, |
3332 | - public epicsThreadRunable, public epicsTimerQueueNotify, |
3333 | +class timerQueueActive : |
3334 | + public epicsTimerQueueActive, |
3335 | + public epicsThreadRunable, |
3336 | + public epicsTimerQueueNotify, |
3337 | public timerQueueActiveMgrPrivate { |
3338 | public: |
3339 | - typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr; |
3340 | - timerQueueActive ( RefMgr &, bool okToShare, unsigned priority ); |
3341 | - void start (); |
3342 | + timerQueueActive ( bool okToShare, unsigned priority ); |
3343 | epicsTimer & createTimer (); |
3344 | - epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); |
3345 | + TimerForC & createTimerForC ( |
3346 | + epicsTimerCallback pCallback, void *pArg ); |
3347 | + void run (); |
3348 | + void reschedule (); |
3349 | + epicsTimerQueue & getEpicsTimerQueue (); |
3350 | void show ( unsigned int level ) const; |
3351 | bool sharingOK () const; |
3352 | unsigned threadPriority () const; |
3353 | protected: |
3354 | ~timerQueueActive (); |
3355 | - RefMgr _refMgr; |
3356 | private: |
3357 | - timerQueue queue; |
3358 | - epicsEvent rescheduleEvent; |
3359 | - epicsEvent exitEvent; |
3360 | - epicsThread thread; |
3361 | - const double sleepQuantum; |
3362 | - bool okToShare; |
3363 | - int exitFlag; // use atomic ops |
3364 | - bool terminateFlag; |
3365 | - void run (); |
3366 | - void reschedule (); |
3367 | - double quantum (); |
3368 | - void _printLastChanceExceptionMessage ( |
3369 | - const char * pExceptionTypeName, |
3370 | - const char * pExceptionContext ); |
3371 | - epicsTimerQueue & getEpicsTimerQueue (); |
3372 | + typedef epicsMutex Mutex; |
3373 | + typedef epicsGuard < Mutex > Guard; |
3374 | + typedef epicsGuardRelease < Mutex > GuardRelease; |
3375 | + timerQueue m_queue; |
3376 | + epicsEvent m_rescheduleEvent; |
3377 | + epicsEvent m_exitEvent; |
3378 | + epicsThread m_thread; |
3379 | + bool m_okToShare; |
3380 | + bool m_exitFlag; |
3381 | + bool m_terminateFlag; |
3382 | timerQueueActive ( const timerQueueActive & ); |
3383 | timerQueueActive & operator = ( const timerQueueActive & ); |
3384 | }; |
3385 | |
3386 | class timerQueueActiveMgr { |
3387 | public: |
3388 | - typedef epicsSingleton < timerQueueActiveMgr > :: reference RefThis; |
3389 | + static timerQueueActiveMgr & master (); |
3390 | timerQueueActiveMgr (); |
3391 | ~timerQueueActiveMgr (); |
3392 | - epicsTimerQueueActiveForC & allocate ( RefThis &, bool okToShare, |
3393 | + epicsTimerQueueActiveForC & allocate ( bool okToShare, |
3394 | unsigned threadPriority = epicsThreadPriorityMin + 10 ); |
3395 | void release ( epicsTimerQueueActiveForC & ); |
3396 | private: |
3397 | - epicsMutex mutex; |
3398 | - tsDLList < epicsTimerQueueActiveForC > sharedQueueList; |
3399 | + typedef epicsMutex Mutex; |
3400 | + typedef epicsGuard < Mutex > Guard; |
3401 | + Mutex m_mutex; |
3402 | + tsDLList < epicsTimerQueueActiveForC > m_sharedQueueList; |
3403 | timerQueueActiveMgr ( const timerQueueActiveMgr & ); |
3404 | timerQueueActiveMgr & operator = ( const timerQueueActiveMgr & ); |
3405 | }; |
3406 | |
3407 | -extern epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; |
3408 | - |
3409 | class timerQueuePassive : public epicsTimerQueuePassive { |
3410 | public: |
3411 | timerQueuePassive ( epicsTimerQueueNotify & ); |
3412 | epicsTimer & createTimer (); |
3413 | - epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg ); |
3414 | + TimerForC & createTimerForC ( |
3415 | + epicsTimerCallback pCallback, void *pArg ); |
3416 | void show ( unsigned int level ) const; |
3417 | double process ( const epicsTime & currentTime ); |
3418 | + epicsTimerQueue & getEpicsTimerQueue (); |
3419 | protected: |
3420 | - timerQueue queue; |
3421 | + timerQueue m_queue; |
3422 | ~timerQueuePassive (); |
3423 | - epicsTimerQueue & getEpicsTimerQueue (); |
3424 | timerQueuePassive ( const timerQueuePassive & ); |
3425 | timerQueuePassive & operator = ( const timerQueuePassive & ); |
3426 | }; |
3427 | |
3428 | -struct epicsTimerQueuePassiveForC : |
3429 | - public epicsTimerQueueNotify, public timerQueuePassive { |
3430 | +struct epicsTimerQueuePassiveForC : |
3431 | + public epicsTimerQueueNotify, |
3432 | + public timerQueuePassive { |
3433 | public: |
3434 | - epicsTimerQueuePassiveForC ( |
3435 | - epicsTimerQueueNotifyReschedule, |
3436 | + epicsTimerQueuePassiveForC ( |
3437 | + epicsTimerQueueNotifyReschedule, |
3438 | epicsTimerQueueNotifyQuantum, |
3439 | void * pPrivate ); |
3440 | void destroy (); |
3441 | + void reschedule (); |
3442 | protected: |
3443 | ~epicsTimerQueuePassiveForC (); |
3444 | private: |
3445 | - epicsTimerQueueNotifyReschedule pRescheduleCallback; |
3446 | - epicsTimerQueueNotifyQuantum pSleepQuantumCallback; |
3447 | - void * pPrivate; |
3448 | - static epicsSingleton < tsFreeList < epicsTimerQueuePassiveForC, 0x10 > > pFreeList; |
3449 | - void reschedule (); |
3450 | - double quantum (); |
3451 | + epicsTimerQueueNotifyReschedule m_pRescheduleCallback; |
3452 | + epicsTimerQueueNotifyQuantum m_pSleepQuantumCallback; |
3453 | + void * m_pPrivate; |
3454 | }; |
3455 | |
3456 | -struct epicsTimerQueueActiveForC : public timerQueueActive, |
3457 | +struct epicsTimerQueueActiveForC : public timerQueueActive, |
3458 | public tsDLNode < epicsTimerQueueActiveForC > { |
3459 | public: |
3460 | - epicsTimerQueueActiveForC ( RefMgr &, bool okToShare, unsigned priority ); |
3461 | + epicsTimerQueueActiveForC ( bool okToShare, unsigned priority ); |
3462 | void release (); |
3463 | - void * operator new ( size_t ); |
3464 | - void operator delete ( void * ); |
3465 | protected: |
3466 | virtual ~epicsTimerQueueActiveForC (); |
3467 | private: |
3468 | @@ -225,52 +265,106 @@ private: |
3469 | epicsTimerQueueActiveForC & operator = ( const epicsTimerQueueActiveForC & ); |
3470 | }; |
3471 | |
3472 | -inline bool timerQueueActive::sharingOK () const |
3473 | +inline double Timer :: getExpireDelay ( const epicsTime & currentTime ) |
3474 | +{ |
3475 | + return m_exp - currentTime; |
3476 | +} |
3477 | + |
3478 | +inline void * Timer :: operator new ( size_t sz ) |
3479 | +{ |
3480 | + return M_Allocator :: allocateOctets ( sz ); |
3481 | +} |
3482 | + |
3483 | +inline void Timer :: operator delete ( void * p, size_t sz ) |
3484 | { |
3485 | - return this->okToShare; |
3486 | + M_Allocator :: deallocateOctets ( p, sz ); |
3487 | +} |
3488 | + |
3489 | +inline bool operator < ( const Timer & lhs, const Timer & rhs ) |
3490 | +{ |
3491 | + return lhs.m_exp < rhs.m_exp; |
3492 | +} |
3493 | + |
3494 | +inline bool operator > ( const Timer & lhs, const Timer & rhs ) |
3495 | +{ |
3496 | + return rhs < lhs; |
3497 | +} |
3498 | + |
3499 | +inline bool operator <= ( const Timer & lhs, const Timer & rhs ) |
3500 | +{ |
3501 | + return ! ( lhs > rhs ); |
3502 | } |
3503 | |
3504 | -inline unsigned timerQueueActive::threadPriority () const |
3505 | +inline bool operator >= ( const Timer & lhs, const Timer & rhs ) |
3506 | +{ |
3507 | + return ! ( lhs < rhs ); |
3508 | +} |
3509 | + |
3510 | +inline size_t timerQueue :: m_parent ( const size_t childIdx ) |
3511 | { |
3512 | - return thread.getPriority (); |
3513 | + return ( childIdx + ( childIdx & 1u ) ) / 2u - 1u; |
3514 | } |
3515 | |
3516 | -inline void * timer::operator new ( size_t size, |
3517 | - tsFreeList < timer, 0x20 > & freeList ) |
3518 | +inline size_t timerQueue :: m_leftChild ( const size_t parentIdx ) |
3519 | { |
3520 | - return freeList.allocate ( size ); |
3521 | + return ( parentIdx + 1u ) * 2u - 1u; |
3522 | } |
3523 | |
3524 | -#ifdef CXX_PLACEMENT_DELETE |
3525 | -inline void timer::operator delete ( void * pCadaver, |
3526 | - tsFreeList < timer, 0x20 > & freeList ) |
3527 | +inline size_t timerQueue :: m_rightChild ( const size_t parentIdx ) |
3528 | { |
3529 | - freeList.release ( pCadaver ); |
3530 | + return ( parentIdx + 1u ) * 2u; |
3531 | } |
3532 | -#endif |
3533 | |
3534 | -inline void * epicsTimerForC::operator new ( size_t size, |
3535 | - tsFreeList < epicsTimerForC, 0x20 > & freeList ) |
3536 | +inline void timerQueue :: m_swapEntries ( size_t idx0, size_t idx1 ) |
3537 | { |
3538 | - return freeList.allocate ( size ); |
3539 | + std :: swap ( m_heap[idx0], m_heap[idx1] ); |
3540 | + m_heap[idx0]->m_index = idx0; |
3541 | + m_heap[idx1]->m_index = idx1; |
3542 | } |
3543 | |
3544 | -#ifdef CXX_PLACEMENT_DELETE |
3545 | -inline void epicsTimerForC::operator delete ( void * pCadaver, |
3546 | - tsFreeList < epicsTimerForC, 0x20 > & freeList ) |
3547 | +inline bool timerQueueActive :: sharingOK () const |
3548 | { |
3549 | - freeList.release ( pCadaver ); |
3550 | + return m_okToShare; |
3551 | +} |
3552 | + |
3553 | +inline unsigned timerQueueActive :: threadPriority () const |
3554 | +{ |
3555 | + return m_thread.getPriority (); |
3556 | +} |
3557 | + |
3558 | +inline unsigned TimerForC :: start ( const epicsTime & expTime ) |
3559 | +{ |
3560 | + return m_pTimer->start ( *this, expTime ); |
3561 | +} |
3562 | + |
3563 | +inline unsigned TimerForC :: start ( double delaySeconds ) |
3564 | +{ |
3565 | + return m_pTimer->start ( *this, delaySeconds ); |
3566 | +} |
3567 | + |
3568 | +inline bool TimerForC :: cancel () |
3569 | +{ |
3570 | + return m_pTimer->cancel (); |
3571 | +} |
3572 | + |
3573 | +inline double TimerForC :: getExpireDelay () |
3574 | +{ |
3575 | + return m_pTimer->getExpireDelay ( epicsTime :: getCurrent () ); |
3576 | +} |
3577 | + |
3578 | +inline void * TimerForC :: operator new ( size_t sz ) |
3579 | +{ |
3580 | + return M_Allocator :: allocateOctets ( sz ); |
3581 | } |
3582 | -#endif |
3583 | |
3584 | -inline void * epicsTimerQueueActiveForC::operator new ( size_t size ) |
3585 | +inline void TimerForC :: operator delete ( void * p, size_t sz ) |
3586 | { |
3587 | - return ::operator new ( size ); |
3588 | + M_Allocator :: deallocateOctets ( p, sz ); |
3589 | } |
3590 | |
3591 | -inline void epicsTimerQueueActiveForC::operator delete ( void * pCadaver ) |
3592 | +inline timerQueueActiveMgr & timerQueueActiveMgr :: master () |
3593 | { |
3594 | - ::operator delete ( pCadaver ); |
3595 | + return epics :: staticInstance < timerQueueActiveMgr > (); |
3596 | } |
3597 | |
3598 | #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 | /*************************************************************************\ |
3605 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
3606 | +* National Laboratory |
3607 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
3608 | * National Laboratory. |
3609 | * Copyright (c) 2002 The Regents of the University of California, as |
3610 | @@ -12,56 +14,45 @@ |
3611 | * 505 665 1831 |
3612 | */ |
3613 | |
3614 | -#include <stdio.h> |
3615 | -#include <float.h> |
3616 | +#include <cstdio> |
3617 | |
3618 | #define epicsExportSharedSymbols |
3619 | -#include "epicsGuard.h" |
3620 | -#include "timerPrivate.h" |
3621 | #include "errlog.h" |
3622 | +#include "timerPrivate.h" |
3623 | |
3624 | -const double timerQueue :: exceptMsgMinPeriod = 60.0 * 5.0; // seconds |
3625 | - |
3626 | -epicsTimerQueue::~epicsTimerQueue () {} |
3627 | +const double timerQueue :: m_exceptMsgMinPeriod = 60.0 * 5.0; // seconds |
3628 | |
3629 | -timerQueue::timerQueue ( epicsTimerQueueNotify & notifyIn ) : |
3630 | - mutex(__FILE__, __LINE__), |
3631 | - notify ( notifyIn ), |
3632 | - pExpireTmr ( 0 ), |
3633 | - processThread ( 0 ), |
3634 | - exceptMsgTimeStamp ( |
3635 | - epicsTime :: getCurrent () - exceptMsgMinPeriod ), |
3636 | - cancelPending ( false ) |
3637 | +timerQueue :: timerQueue ( epicsTimerQueueNotify & notifyIn ) : |
3638 | + Mutex ( __FILE__, __LINE__ ), |
3639 | + m_exceptMsgTimeStamp ( epicsTime :: getCurrent () |
3640 | + - m_exceptMsgMinPeriod ), |
3641 | + m_notify ( notifyIn ), |
3642 | + m_pExpTmr ( 0 ), |
3643 | + m_processThread ( 0 ), |
3644 | + m_numTimers ( 0u ), |
3645 | + m_cancelPending ( false ) |
3646 | { |
3647 | } |
3648 | |
3649 | -timerQueue::~timerQueue () |
3650 | +timerQueue :: ~timerQueue () |
3651 | { |
3652 | - timer *pTmr; |
3653 | - while ( ( pTmr = this->timerList.get () ) ) { |
3654 | - pTmr->curState = timer::stateLimbo; |
3655 | + if ( m_heap.size () ) { |
3656 | + while ( Timer * const pTmr = m_heap.back () ) { |
3657 | + pTmr->m_curState = Timer :: stateLimbo; |
3658 | + m_heap.pop_back (); |
3659 | + } |
3660 | } |
3661 | } |
3662 | |
3663 | void timerQueue :: |
3664 | - printExceptMsg ( const char * pName, const type_info & type ) |
3665 | + m_printExceptMsg ( const char * pName, const type_info & type ) |
3666 | { |
3667 | - char date[64]; |
3668 | - double delay; |
3669 | - try { |
3670 | - epicsTime cur = epicsTime :: getCurrent (); |
3671 | - delay = cur - this->exceptMsgTimeStamp; |
3672 | - cur.strftime ( date, sizeof ( date ), |
3673 | - "%a %b %d %Y %H:%M:%S.%f" ); |
3674 | - if ( delay >= exceptMsgMinPeriod ) { |
3675 | - this->exceptMsgTimeStamp = cur; |
3676 | - } |
3677 | - } |
3678 | - catch ( ... ) { |
3679 | - delay = DBL_MAX; |
3680 | - strcpy ( date, "UKN DATE" ); |
3681 | - } |
3682 | - if ( delay >= exceptMsgMinPeriod ) { |
3683 | + const epicsTime cur = epicsTime :: getCurrent (); |
3684 | + const double delay = cur - m_exceptMsgTimeStamp; |
3685 | + if ( delay >= m_exceptMsgMinPeriod ) { |
3686 | + m_exceptMsgTimeStamp = cur; |
3687 | + char date[64]; |
3688 | + cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f" ); |
3689 | // we dont touch the typeid for the timer expiration |
3690 | // notify interface here because they might have |
3691 | // destroyed the timer during its callback |
3692 | @@ -72,155 +63,212 @@ void timerQueue :: |
3693 | pName, |
3694 | type.name (), |
3695 | date ); |
3696 | + errlogPrintf ( "!!!! WARNING - PERIODIC TIMER MAY NOT RESTART !!!!\n" ); |
3697 | errlogFlush (); |
3698 | } |
3699 | } |
3700 | |
3701 | -double timerQueue::process ( const epicsTime & currentTime ) |
3702 | -{ |
3703 | - epicsGuard < epicsMutex > guard ( this->mutex ); |
3704 | +inline double timerQueue :: m_expDelay ( const epicsTime & currentTime ) |
3705 | +{ |
3706 | + double delay = DBL_MAX; |
3707 | + if ( m_heap.size () > 0u ) { |
3708 | + delay = m_heap.front ()->m_exp - currentTime; |
3709 | + } |
3710 | + return delay; |
3711 | +} |
3712 | + |
3713 | +double timerQueue :: process ( const epicsTime & currentTime ) |
3714 | +{ |
3715 | + Guard guard ( *this ); |
3716 | + return this->process ( guard, currentTime ); |
3717 | +} |
3718 | |
3719 | - if ( this->pExpireTmr ) { |
3720 | +double timerQueue :: process ( Guard & guard, |
3721 | + const epicsTime & currentTime ) |
3722 | +{ |
3723 | + guard.assertIdenticalMutex ( *this ); |
3724 | + if ( m_processThread ) { |
3725 | // if some other thread is processing the queue |
3726 | // (or if this is a recursive call) |
3727 | - timer * pTmr = this->timerList.first (); |
3728 | - if ( pTmr ) { |
3729 | - double delay = pTmr->exp - currentTime; |
3730 | - if ( delay < 0.0 ) { |
3731 | - delay = 0.0; |
3732 | - } |
3733 | - return delay; |
3734 | - } |
3735 | - else { |
3736 | - return DBL_MAX; |
3737 | + double delay = m_expDelay ( currentTime ); |
3738 | + if ( delay <= 0.0 ) { |
3739 | + delay = 0.0; |
3740 | } |
3741 | - } |
3742 | - |
3743 | - // |
3744 | - // Tag current epired tmr so that we can detect if call back |
3745 | - // is in progress when canceling the timer. |
3746 | - // |
3747 | - if ( this->timerList.first () ) { |
3748 | - if ( currentTime >= this->timerList.first ()->exp ) { |
3749 | - this->pExpireTmr = this->timerList.first (); |
3750 | - this->timerList.remove ( *this->pExpireTmr ); |
3751 | - this->pExpireTmr->curState = timer::stateActive; |
3752 | - this->processThread = epicsThreadGetIdSelf (); |
3753 | -# ifdef DEBUG |
3754 | - this->pExpireTmr->show ( 0u ); |
3755 | -# endif |
3756 | - } |
3757 | - else { |
3758 | - double delay = this->timerList.first ()->exp - currentTime; |
3759 | - debugPrintf ( ( "no activity process %f to next\n", delay ) ); |
3760 | - return delay; |
3761 | - } |
3762 | - } |
3763 | - else { |
3764 | - return DBL_MAX; |
3765 | + return delay; |
3766 | } |
3767 | |
3768 | # ifdef DEBUG |
3769 | unsigned N = 0u; |
3770 | # endif |
3771 | |
3772 | - double delay = DBL_MAX; |
3773 | - while ( true ) { |
3774 | - epicsTimerNotify *pTmpNotify = this->pExpireTmr->pNotify; |
3775 | - this->pExpireTmr->pNotify = 0; |
3776 | - epicsTimerNotify::expireStatus expStat ( epicsTimerNotify::noRestart ); |
3777 | - |
3778 | - { |
3779 | - epicsGuardRelease < epicsMutex > unguard ( guard ); |
3780 | - |
3781 | + m_processThread = epicsThreadGetIdSelf (); |
3782 | + double delay = m_expDelay ( currentTime ); |
3783 | + while ( delay <= 0.0 ) { |
3784 | + // |
3785 | + // if delay is zero or less we know at least one timer is on |
3786 | + // the queue |
3787 | + // |
3788 | + // tag current expired tmr so that we can detect if call back |
3789 | + // is in progress when canceling the timer |
3790 | + // |
3791 | + delay = 0.0; |
3792 | + m_pExpTmr = m_heap.front (); |
3793 | + epicsTimerNotify * const pTmpNotify = m_pExpTmr->m_pNotify; |
3794 | + m_pExpTmr->m_pNotify = 0; |
3795 | + epicsTimerNotify :: expireStatus |
3796 | + expStat ( epicsTimerNotify :: noRestart ); |
3797 | + if ( pTmpNotify ) { |
3798 | + GuardRelease unguard ( guard ); |
3799 | debugPrintf ( ( "%5u expired \"%s\" with error %f sec\n", |
3800 | - N++, typeid ( this->pExpireTmr->notify ).name (), |
3801 | - currentTime - this->pExpireTmr->exp ) ); |
3802 | + N++, |
3803 | + typeid ( *pTmpNotify ).name (), |
3804 | + currentTime - m_pExpTmr->m_exp ) ); |
3805 | try { |
3806 | expStat = pTmpNotify->expire ( currentTime ); |
3807 | } |
3808 | catch ( std::exception & except ) { |
3809 | - printExceptMsg ( except.what (), typeid ( except ) ); |
3810 | + m_printExceptMsg ( except.what (), typeid ( except ) ); |
3811 | } |
3812 | catch ( ... ) { |
3813 | - printExceptMsg ( "non-standard exception", typeid ( void ) ); |
3814 | + m_printExceptMsg ( "non-standard exception", typeid ( void ) ); |
3815 | } |
3816 | } |
3817 | |
3818 | // |
3819 | - // only restart if they didnt cancel() the timer |
3820 | - // while the call back was running |
3821 | + // !! The position of a timer in the queue is allowed to change |
3822 | + // !! while its timer callback is running. This happens |
3823 | + // !! potentially when they reschedule a timer or cancel a |
3824 | + // !! timer. A small amount of additional labor is expended |
3825 | + // !! to properly handle this type of change below (we |
3826 | + // !! test the return from fix_parent and conditionally |
3827 | + // !! call fix_child, instead of calling only fix_child). |
3828 | // |
3829 | - if ( this->cancelPending ) { |
3830 | + if ( m_cancelPending ) { |
3831 | + // |
3832 | + // only restart if they didnt cancel() the currently |
3833 | + // expiring timer while its call-back is running |
3834 | + // |
3835 | // 1) if another thread is canceling then cancel() waits for |
3836 | // the event below |
3837 | // 2) if this thread is canceling in the timer callback then |
3838 | // dont touch timer or notify here because the cancel might |
3839 | // have occurred because they destroyed the timer in the |
3840 | // callback |
3841 | - this->cancelPending = false; |
3842 | - this->cancelBlockingEvent.signal (); |
3843 | + // 3) timer::cancel sets timer state to limbo and timer index |
3844 | + // to invalid |
3845 | + // |
3846 | + m_cancelPending = false; |
3847 | + m_cancelBlockingEvent.signal (); |
3848 | } |
3849 | else { |
3850 | - this->pExpireTmr->curState = timer::stateLimbo; |
3851 | - if ( this->pExpireTmr->pNotify ) { |
3852 | - // pNotify was cleared above so if it is valid now we know that |
3853 | - // someone has started the timer from another thread and that |
3854 | - // predominates over the restart parameters from expire. |
3855 | - this->pExpireTmr->privateStart ( |
3856 | - *this->pExpireTmr->pNotify, this->pExpireTmr->exp ); |
3857 | + if ( m_pExpTmr->m_pNotify ) { |
3858 | + // pNotify was cleared above; if its valid now we |
3859 | + // know that someone has restarted the timer when |
3860 | + // its callback is currently running either |
3861 | + // asynchronously from another thread or from |
3862 | + // within the currently running expire callback, |
3863 | + // possibly moving its position in the heap. As |
3864 | + // a defined policy either of these situations |
3865 | + // overrides any restart request parameters |
3866 | + // returned from expire |
3867 | } |
3868 | else if ( expStat.restart() ) { |
3869 | // restart as nec |
3870 | - this->pExpireTmr->privateStart ( |
3871 | - *pTmpNotify, currentTime + expStat.expirationDelay() ); |
3872 | - } |
3873 | - } |
3874 | - this->pExpireTmr = 0; |
3875 | - |
3876 | - if ( this->timerList.first () ) { |
3877 | - if ( currentTime >= this->timerList.first ()->exp ) { |
3878 | - this->pExpireTmr = this->timerList.first (); |
3879 | - this->timerList.remove ( *this->pExpireTmr ); |
3880 | - this->pExpireTmr->curState = timer::stateActive; |
3881 | -# ifdef DEBUG |
3882 | - this->pExpireTmr->show ( 0u ); |
3883 | -# endif |
3884 | + m_pExpTmr->m_pNotify = pTmpNotify; |
3885 | + m_pExpTmr->m_exp = currentTime + expStat.expirationDelay (); |
3886 | + if ( ! m_fixParent ( m_pExpTmr->m_index ) ) { |
3887 | + m_fixChildren ( m_pExpTmr->m_index ); |
3888 | + } |
3889 | } |
3890 | else { |
3891 | - delay = this->timerList.first ()->exp - currentTime; |
3892 | - this->processThread = 0; |
3893 | - break; |
3894 | + m_pExpTmr->m_remove ( guard ); |
3895 | } |
3896 | } |
3897 | - else { |
3898 | - this->processThread = 0; |
3899 | - delay = DBL_MAX; |
3900 | + delay = m_expDelay ( currentTime ); |
3901 | + } |
3902 | + m_pExpTmr = 0; |
3903 | + m_processThread = 0; |
3904 | + return delay; |
3905 | +} |
3906 | + |
3907 | +bool timerQueue :: m_fixParent ( size_t childIdx ) |
3908 | +{ |
3909 | + bool itMoved = false; |
3910 | + while ( childIdx != 0u ) { |
3911 | + const size_t parentIdx = m_parent ( childIdx ); |
3912 | + if ( *m_heap[parentIdx] <= *m_heap[childIdx] ) { |
3913 | break; |
3914 | } |
3915 | + m_swapEntries ( parentIdx, childIdx ); |
3916 | + childIdx = parentIdx; |
3917 | + itMoved = true; |
3918 | } |
3919 | - return delay; |
3920 | + return itMoved; |
3921 | } |
3922 | |
3923 | -epicsTimer & timerQueue::createTimer () |
3924 | +void timerQueue :: m_fixChildren ( size_t parentIdx ) |
3925 | { |
3926 | - return * new ( this->timerFreeList ) timer ( * this ); |
3927 | + const size_t hpsz = m_heap.size (); |
3928 | + while ( true ) { |
3929 | + const size_t leftChildIdx = m_leftChild ( parentIdx ); |
3930 | + const size_t rightChildIdx = m_rightChild ( parentIdx ); |
3931 | + size_t smallestIdx = parentIdx; |
3932 | + if ( leftChildIdx < hpsz ) { |
3933 | + if ( *m_heap[parentIdx] > *m_heap[leftChildIdx] ) { |
3934 | + smallestIdx = leftChildIdx; |
3935 | + } |
3936 | + } |
3937 | + if ( rightChildIdx < hpsz ) { |
3938 | + if ( *m_heap[smallestIdx] > *m_heap[rightChildIdx] ) { |
3939 | + smallestIdx = rightChildIdx; |
3940 | + } |
3941 | + } |
3942 | + if ( smallestIdx == parentIdx ) { |
3943 | + break; |
3944 | + } |
3945 | + m_swapEntries ( parentIdx, smallestIdx ); |
3946 | + parentIdx = smallestIdx; |
3947 | + } |
3948 | +} |
3949 | + |
3950 | +Timer & timerQueue :: createTimerImpl () |
3951 | +{ |
3952 | + // better to throw now in contrast with later during start |
3953 | + Guard guard ( *this ); |
3954 | + m_numTimers++; |
3955 | + m_heap.reserve ( m_numTimers ); |
3956 | + return * new Timer ( * this ); |
3957 | } |
3958 | |
3959 | -epicsTimerForC & timerQueue::createTimerForC ( epicsTimerCallback pCallback, void *pArg ) |
3960 | +epicsTimer & timerQueue :: createTimer () |
3961 | { |
3962 | - return * new ( this->timerForCFreeList ) epicsTimerForC ( *this, pCallback, pArg ); |
3963 | + return createTimerImpl (); |
3964 | } |
3965 | |
3966 | -void timerQueue::show ( unsigned level ) const |
3967 | +TimerForC & timerQueue :: createTimerForC ( |
3968 | + epicsTimerCallback pCallback, void *pArg ) |
3969 | { |
3970 | - epicsGuard < epicsMutex > locker ( this->mutex ); |
3971 | - printf ( "epicsTimerQueue with %u items pending\n", this->timerList.count () ); |
3972 | + return * new TimerForC ( *this, pCallback, pArg ); |
3973 | +} |
3974 | + |
3975 | +void timerQueue :: show ( unsigned level ) const |
3976 | +{ |
3977 | + Guard guard ( const_cast < timerQueue & > ( *this ) ); |
3978 | + this->show ( guard, level ); |
3979 | +} |
3980 | + |
3981 | +void timerQueue :: show ( Guard & guard, unsigned level ) const |
3982 | +{ |
3983 | + guard.assertIdenticalMutex ( *this ); |
3984 | + printf ( "epicsTimerQueue with %lu items pending\n", |
3985 | + (unsigned long) m_heap.size () ); |
3986 | if ( level >= 1u ) { |
3987 | - tsDLIterConst < timer > iter = this->timerList.firstIter (); |
3988 | - while ( iter.valid () ) { |
3989 | - iter->show ( level - 1u ); |
3990 | - ++iter; |
3991 | + std :: vector < Timer * > :: const_iterator |
3992 | + ppTimer = m_heap.begin (); |
3993 | + while ( ppTimer != m_heap.end () && *ppTimer ) { |
3994 | + (*ppTimer)->show ( level - 1u ); |
3995 | + ++ppTimer; |
3996 | } |
3997 | } |
3998 | } |
3999 | + |
4000 | diff --git a/modules/libcom/src/timer/timerQueueActive.cpp b/modules/libcom/src/timer/timerQueueActive.cpp |
4001 | index 4db68c0..3efedee 100644 |
4002 | --- a/modules/libcom/src/timer/timerQueueActive.cpp |
4003 | +++ b/modules/libcom/src/timer/timerQueueActive.cpp |
4004 | @@ -1,5 +1,7 @@ |
4005 | /*************************************************************************\ |
4006 | -* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne |
4007 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
4008 | +* National Laboratory |
4009 | +* Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
4010 | * National Laboratory. |
4011 | * Copyright (c) 2002 The Regents of the University of California, as |
4012 | * Operator of Los Alamos National Laboratory. |
4013 | @@ -12,144 +14,100 @@ |
4014 | * 505 665 1831 |
4015 | */ |
4016 | |
4017 | -#include <stdio.h> |
4018 | +#include <cstdio> |
4019 | |
4020 | #define epicsExportSharedSymbols |
4021 | -#include "epicsAtomic.h" |
4022 | +#include "epicsGuard.h" |
4023 | #include "timerPrivate.h" |
4024 | -#include "errlog.h" |
4025 | |
4026 | -#ifdef _MSC_VER |
4027 | -# pragma warning ( push ) |
4028 | -# pragma warning ( disable:4660 ) |
4029 | -#endif |
4030 | - |
4031 | -template class epicsSingleton < timerQueueActiveMgr >; |
4032 | - |
4033 | -#ifdef _MSC_VER |
4034 | -# pragma warning ( pop ) |
4035 | -#endif |
4036 | - |
4037 | -epicsSingleton < timerQueueActiveMgr > timerQueueMgrEPICS; |
4038 | - |
4039 | -epicsTimerQueueActive::~epicsTimerQueueActive () {} |
4040 | - |
4041 | -epicsTimerQueueActive & epicsTimerQueueActive::allocate ( bool okToShare, unsigned threadPriority ) |
4042 | +epicsTimerQueueActive & epicsTimerQueueActive :: allocate ( bool okToShare, |
4043 | + unsigned threadPriority ) |
4044 | { |
4045 | - epicsSingleton < timerQueueActiveMgr >::reference pMgr = |
4046 | - timerQueueMgrEPICS.getReference (); |
4047 | - return pMgr->allocate ( pMgr, okToShare, threadPriority ); |
4048 | + return timerQueueActiveMgr :: master (). |
4049 | + allocate ( okToShare, threadPriority ); |
4050 | } |
4051 | |
4052 | timerQueueActive :: |
4053 | - timerQueueActive ( RefMgr & refMgr, |
4054 | - bool okToShareIn, unsigned priority ) : |
4055 | - _refMgr ( refMgr ), queue ( *this ), thread ( *this, "timerQueue", |
4056 | + timerQueueActive ( bool okToShareIn, unsigned priority ) : |
4057 | + m_queue ( *this ), |
4058 | + m_thread ( *this, "timerQueue", |
4059 | epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ), |
4060 | - sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ), |
4061 | - exitFlag ( 0 ), terminateFlag ( false ) |
4062 | + m_okToShare ( okToShareIn ), |
4063 | + m_exitFlag ( false ), |
4064 | + m_terminateFlag ( false ) |
4065 | { |
4066 | + epicsGuard < epicsMutex > guard ( m_queue ); |
4067 | + m_thread.start (); |
4068 | } |
4069 | |
4070 | -void timerQueueActive::start () |
4071 | +timerQueueActive :: ~timerQueueActive () |
4072 | { |
4073 | - this->thread.start (); |
4074 | -} |
4075 | - |
4076 | -timerQueueActive::~timerQueueActive () |
4077 | -{ |
4078 | - this->terminateFlag = true; |
4079 | - this->rescheduleEvent.signal (); |
4080 | - while ( ! epics::atomic::get(this->exitFlag) ) { |
4081 | - this->exitEvent.wait ( 1.0 ); |
4082 | + Guard guard ( m_queue ); |
4083 | + m_terminateFlag = true; |
4084 | + m_rescheduleEvent.signal (); |
4085 | + while ( ! m_exitFlag ) { |
4086 | + GuardRelease release ( guard ); |
4087 | + m_exitEvent.wait ( 1.0 ); |
4088 | } |
4089 | // in case other threads are waiting here also |
4090 | - this->exitEvent.signal (); |
4091 | -} |
4092 | - |
4093 | -void timerQueueActive :: _printLastChanceExceptionMessage ( |
4094 | - const char * pExceptionTypeName, |
4095 | - const char * pExceptionContext ) |
4096 | -{ |
4097 | - char date[64]; |
4098 | - try { |
4099 | - epicsTime cur = epicsTime :: getCurrent (); |
4100 | - cur.strftime ( date, sizeof ( date ), "%a %b %d %Y %H:%M:%S.%f"); |
4101 | - } |
4102 | - catch ( ... ) { |
4103 | - strcpy ( date, "<UKN DATE>" ); |
4104 | - } |
4105 | - errlogPrintf ( |
4106 | - "timerQueueActive: Unexpected C++ exception \"%s\" with type \"%s\" " |
4107 | - "while processing timer queue, at %s\n", |
4108 | - pExceptionContext, pExceptionTypeName, date ); |
4109 | + m_exitEvent.signal (); |
4110 | } |
4111 | |
4112 | - |
4113 | void timerQueueActive :: run () |
4114 | { |
4115 | - epics::atomic::set(this->exitFlag, 0); |
4116 | - while ( ! this->terminateFlag ) { |
4117 | - try { |
4118 | - double delay = this->queue.process ( epicsTime::getCurrent() ); |
4119 | - debugPrintf ( ( "timer thread sleeping for %g sec (max)\n", delay ) ); |
4120 | - this->rescheduleEvent.wait ( delay ); |
4121 | - } |
4122 | - catch ( std :: exception & except ) { |
4123 | - _printLastChanceExceptionMessage ( |
4124 | - typeid ( except ).name (), except.what () ); |
4125 | - epicsThreadSleep ( 10.0 ); |
4126 | - } |
4127 | - catch ( ... ) { |
4128 | - _printLastChanceExceptionMessage ( |
4129 | - "catch ( ... )", "Non-standard C++ exception" ); |
4130 | - epicsThreadSleep ( 10.0 ); |
4131 | + Guard guard ( m_queue ); |
4132 | + m_exitFlag = false; |
4133 | + while ( ! m_terminateFlag ) { |
4134 | + const epicsTime curr = epicsTime :: getCurrent (); |
4135 | + double delay = m_queue.process ( guard, curr ); |
4136 | + { |
4137 | + GuardRelease release ( guard ); |
4138 | + debugPrintf ( ( "timer thread sleeping for %g sec (max)\n", |
4139 | + delay ) ); |
4140 | + m_rescheduleEvent.wait ( delay ); |
4141 | } |
4142 | } |
4143 | - epics::atomic::set(this->exitFlag, 1); |
4144 | - this->exitEvent.signal (); // no access to queue after exitEvent signal |
4145 | -} |
4146 | - |
4147 | -epicsTimer & timerQueueActive::createTimer () |
4148 | -{ |
4149 | - return this->queue.createTimer(); |
4150 | + m_exitFlag = true; |
4151 | + m_exitEvent.signal (); // no access to queue after exitEvent signal |
4152 | } |
4153 | |
4154 | -epicsTimerForC & timerQueueActive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) |
4155 | +epicsTimer & timerQueueActive :: createTimer () |
4156 | { |
4157 | - return this->queue.createTimerForC ( pCallback, pArg ); |
4158 | + return m_queue.createTimer(); |
4159 | } |
4160 | |
4161 | -void timerQueueActive::reschedule () |
4162 | +TimerForC & timerQueueActive :: createTimerForC ( |
4163 | + epicsTimerCallback pCallback, void * pArg ) |
4164 | { |
4165 | - this->rescheduleEvent.signal (); |
4166 | + return m_queue.createTimerForC ( pCallback, pArg ); |
4167 | } |
4168 | |
4169 | -double timerQueueActive::quantum () |
4170 | +void timerQueueActive :: reschedule () |
4171 | { |
4172 | - return this->sleepQuantum; |
4173 | + m_rescheduleEvent.signal (); |
4174 | } |
4175 | |
4176 | -void timerQueueActive::show ( unsigned int level ) const |
4177 | +void timerQueueActive :: show ( unsigned int level ) const |
4178 | { |
4179 | + Guard guard ( const_cast < timerQueue & > ( m_queue ) ); |
4180 | printf ( "EPICS threaded timer queue at %p\n", |
4181 | static_cast <const void *> ( this ) ); |
4182 | if ( level > 0u ) { |
4183 | // specifying level one here avoids recursive |
4184 | // show callback |
4185 | - this->thread.show ( 1u ); |
4186 | - this->queue.show ( level - 1u ); |
4187 | + m_thread.show ( 1u ); |
4188 | + m_queue.show ( guard, level - 1u ); |
4189 | printf ( "reschedule event\n" ); |
4190 | - this->rescheduleEvent.show ( level - 1u ); |
4191 | + m_rescheduleEvent.show ( level - 1u ); |
4192 | printf ( "exit event\n" ); |
4193 | - this->exitEvent.show ( level - 1u ); |
4194 | + m_exitEvent.show ( level - 1u ); |
4195 | printf ( "exitFlag = %c, terminateFlag = %c\n", |
4196 | - epics::atomic::get(this->exitFlag) ? 'T' : 'F', |
4197 | - this->terminateFlag ? 'T' : 'F' ); |
4198 | + m_exitFlag ? 'T' : 'F', |
4199 | + m_terminateFlag ? 'T' : 'F' ); |
4200 | } |
4201 | } |
4202 | |
4203 | -epicsTimerQueue & timerQueueActive::getEpicsTimerQueue () |
4204 | +epicsTimerQueue & timerQueueActive :: getEpicsTimerQueue () |
4205 | { |
4206 | return static_cast < epicsTimerQueue &> ( * this ); |
4207 | } |
4208 | diff --git a/modules/libcom/src/timer/timerQueueActiveMgr.cpp b/modules/libcom/src/timer/timerQueueActiveMgr.cpp |
4209 | index eff2e0c..af122a8 100644 |
4210 | --- a/modules/libcom/src/timer/timerQueueActiveMgr.cpp |
4211 | +++ b/modules/libcom/src/timer/timerQueueActiveMgr.cpp |
4212 | @@ -1,11 +1,12 @@ |
4213 | /*************************************************************************\ |
4214 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
4215 | +* National Laboratory |
4216 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
4217 | * National Laboratory. |
4218 | * Copyright (c) 2002 The Regents of the University of California, as |
4219 | * Operator of Los Alamos National Laboratory. |
4220 | -* EPICS BASE Versions 3.13.7 |
4221 | -* and higher are distributed subject to a Software License Agreement found |
4222 | -* in file LICENSE that is included with this distribution. |
4223 | +* EPICS BASE is distributed subject to a Software License Agreement found |
4224 | +* in file LICENSE that is included with this distribution. |
4225 | \*************************************************************************/ |
4226 | /* |
4227 | * Author Jeffrey O. Hill |
4228 | @@ -13,28 +14,27 @@ |
4229 | * 505 665 1831 |
4230 | */ |
4231 | |
4232 | -#include <limits.h> |
4233 | +#include <climits> |
4234 | |
4235 | #define epicsExportSharedSymbols |
4236 | -#include "epicsGuard.h" |
4237 | #include "timerPrivate.h" |
4238 | |
4239 | -timerQueueActiveMgr::timerQueueActiveMgr () |
4240 | - :mutex(__FILE__, __LINE__) |
4241 | +timerQueueActiveMgr::timerQueueActiveMgr () : |
4242 | + m_mutex ( __FILE__, __LINE__ ) |
4243 | { |
4244 | } |
4245 | |
4246 | timerQueueActiveMgr::~timerQueueActiveMgr () |
4247 | { |
4248 | - epicsGuard < epicsMutex > locker ( this->mutex ); |
4249 | + Guard locker ( m_mutex ); |
4250 | } |
4251 | |
4252 | epicsTimerQueueActiveForC & timerQueueActiveMgr :: |
4253 | - allocate ( RefThis & refThis, bool okToShare, unsigned threadPriority ) |
4254 | + allocate ( bool okToShare, unsigned threadPriority ) |
4255 | { |
4256 | - epicsGuard < epicsMutex > locker ( this->mutex ); |
4257 | + Guard locker ( m_mutex ); |
4258 | if ( okToShare ) { |
4259 | - tsDLIter < epicsTimerQueueActiveForC > iter = this->sharedQueueList.firstIter (); |
4260 | + tsDLIter < epicsTimerQueueActiveForC > iter = m_sharedQueueList.firstIter (); |
4261 | while ( iter.valid () ) { |
4262 | if ( iter->threadPriority () == threadPriority ) { |
4263 | assert ( iter->timerQueueActiveMgrPrivate::referenceCount < UINT_MAX ); |
4264 | @@ -46,10 +46,10 @@ epicsTimerQueueActiveForC & timerQueueActiveMgr :: |
4265 | } |
4266 | |
4267 | epicsTimerQueueActiveForC & queue = |
4268 | - * new epicsTimerQueueActiveForC ( refThis, okToShare, threadPriority ); |
4269 | + * new epicsTimerQueueActiveForC ( okToShare, threadPriority ); |
4270 | queue.timerQueueActiveMgrPrivate::referenceCount = 1u; |
4271 | if ( okToShare ) { |
4272 | - this->sharedQueueList.add ( queue ); |
4273 | + m_sharedQueueList.add ( queue ); |
4274 | } |
4275 | return queue; |
4276 | } |
4277 | @@ -58,20 +58,20 @@ void timerQueueActiveMgr :: |
4278 | release ( epicsTimerQueueActiveForC & queue ) |
4279 | { |
4280 | { |
4281 | - epicsGuard < epicsMutex > locker ( this->mutex ); |
4282 | + Guard locker ( m_mutex ); |
4283 | assert ( queue.timerQueueActiveMgrPrivate::referenceCount > 0u ); |
4284 | queue.timerQueueActiveMgrPrivate::referenceCount--; |
4285 | if ( queue.timerQueueActiveMgrPrivate::referenceCount > 0u ) { |
4286 | return; |
4287 | } |
4288 | else if ( queue.sharingOK () ) { |
4289 | - this->sharedQueueList.remove ( queue ); |
4290 | + m_sharedQueueList.remove ( queue ); |
4291 | } |
4292 | } |
4293 | - // delete only after we release the guard in case the embedded |
4294 | + // delete only after we release the guard in case the embedded |
4295 | // reference is the last one and this object is destroyed |
4296 | // as a side effect |
4297 | - timerQueueActiveMgrPrivate * pPriv = & queue; |
4298 | + timerQueueActiveMgrPrivate * const pPriv = & queue; |
4299 | delete pPriv; |
4300 | } |
4301 | |
4302 | diff --git a/modules/libcom/src/timer/timerQueuePassive.cpp b/modules/libcom/src/timer/timerQueuePassive.cpp |
4303 | index a352c56..5ac3186 100644 |
4304 | --- a/modules/libcom/src/timer/timerQueuePassive.cpp |
4305 | +++ b/modules/libcom/src/timer/timerQueuePassive.cpp |
4306 | @@ -1,11 +1,12 @@ |
4307 | /*************************************************************************\ |
4308 | +* Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
4309 | +* National Laboratory |
4310 | * Copyright (c) 2002 The University of Chicago, as Operator of Argonne |
4311 | * National Laboratory. |
4312 | * Copyright (c) 2002 The Regents of the University of California, as |
4313 | * Operator of Los Alamos National Laboratory. |
4314 | -* EPICS BASE Versions 3.13.7 |
4315 | -* and higher are distributed subject to a Software License Agreement found |
4316 | -* in file LICENSE that is included with this distribution. |
4317 | +* EPICS BASE is distributed subject to a Software License Agreement found |
4318 | +* in file LICENSE that is included with this distribution. |
4319 | \*************************************************************************/ |
4320 | /* |
4321 | * Author Jeffrey O. Hill |
4322 | @@ -21,48 +22,51 @@ |
4323 | // in pool. |
4324 | // |
4325 | |
4326 | -#include <stdio.h> |
4327 | +#include <cstdio> |
4328 | |
4329 | #define epicsExportSharedSymbols |
4330 | #include "timerPrivate.h" |
4331 | |
4332 | -epicsTimerQueuePassive::~epicsTimerQueuePassive () {} |
4333 | - |
4334 | -epicsTimerQueuePassive & epicsTimerQueuePassive::create ( epicsTimerQueueNotify ¬ify ) |
4335 | +epicsTimerQueuePassive & |
4336 | + epicsTimerQueuePassive :: create ( |
4337 | + epicsTimerQueueNotify ¬ify ) |
4338 | { |
4339 | return * new timerQueuePassive ( notify ); |
4340 | } |
4341 | |
4342 | -timerQueuePassive::timerQueuePassive ( epicsTimerQueueNotify ¬ifyIn ) : |
4343 | - queue ( notifyIn ) {} |
4344 | +timerQueuePassive :: timerQueuePassive ( epicsTimerQueueNotify ¬ifyIn ) : |
4345 | + m_queue ( notifyIn ) {} |
4346 | |
4347 | timerQueuePassive::~timerQueuePassive () {} |
4348 | |
4349 | epicsTimer & timerQueuePassive::createTimer () |
4350 | { |
4351 | - return this->queue.createTimer (); |
4352 | + return m_queue.createTimer (); |
4353 | } |
4354 | |
4355 | -epicsTimerForC & timerQueuePassive::createTimerForC ( epicsTimerCallback pCallback, void * pArg ) |
4356 | +TimerForC & timerQueuePassive :: createTimerForC ( |
4357 | + epicsTimerCallback pCallback, void * pArg ) |
4358 | { |
4359 | - return this->queue.createTimerForC ( pCallback, pArg ); |
4360 | + return m_queue.createTimerForC ( pCallback, pArg ); |
4361 | } |
4362 | |
4363 | -double timerQueuePassive::process ( const epicsTime & currentTime ) |
4364 | +double timerQueuePassive :: process ( |
4365 | + const epicsTime & currentTime ) |
4366 | { |
4367 | - return this->queue.process ( currentTime ); |
4368 | + return m_queue.process ( currentTime ); |
4369 | } |
4370 | |
4371 | -void timerQueuePassive::show ( unsigned int level ) const |
4372 | +void timerQueuePassive :: show ( |
4373 | + unsigned int level ) const |
4374 | { |
4375 | printf ( "EPICS non-threaded timer queue at %p\n", |
4376 | static_cast <const void *> ( this ) ); |
4377 | if ( level >=1u ) { |
4378 | - this->queue.show ( level - 1u ); |
4379 | + m_queue.show ( level - 1u ); |
4380 | } |
4381 | } |
4382 | |
4383 | -epicsTimerQueue & timerQueuePassive::getEpicsTimerQueue () |
4384 | +epicsTimerQueue & timerQueuePassive :: getEpicsTimerQueue () |
4385 | { |
4386 | return static_cast < epicsTimerQueue &> ( * this ); |
4387 | } |
4388 | diff --git a/modules/libcom/src/valgrind/epicsMemChk.h b/modules/libcom/src/valgrind/epicsMemChk.h |
4389 | new file mode 100644 |
4390 | index 0000000..a190605 |
4391 | --- /dev/null |
4392 | +++ b/modules/libcom/src/valgrind/epicsMemChk.h |
4393 | @@ -0,0 +1,42 @@ |
4394 | +/*************************************************************************\ |
4395 | + * Copyright (c) 2020 Triad National Security, as operator of Los Alamos |
4396 | + * National Laboratory |
4397 | + * EPICS BASE is distributed subject to a Software License Agreement found |
4398 | + * in file LICENSE that is included with this distribution. |
4399 | +\*************************************************************************/ |
4400 | + |
4401 | +#ifndef epicsMemChk_h |
4402 | +#define epicsMemChk_h |
4403 | + |
4404 | + |
4405 | +#if defined ( __cplusplus ) |
4406 | +extern "C" { |
4407 | +#endif |
4408 | + |
4409 | +#ifdef MEMCHECK |
4410 | +# include "valgrind/memcheck.h" |
4411 | + typedef struct epicsMemChkRedZone { volatile char m_redZone[32]; } epicsMemChkRedZone; |
4412 | +# define VALGRIND_RED_ZONE(memberName) epicsMemChkRedZone memberName; |
4413 | +# define VALGRIND_RED_ZONE_SIZE ( sizeof ( epicsMemChkRedZone ) ) |
4414 | +#else |
4415 | +# define VALGRIND_RED_ZONE(memberName) |
4416 | +# define VALGRIND_RED_ZONE_SIZE 0 |
4417 | +# define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr, _qzz_len) |
4418 | +# define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr, _qzz_len) |
4419 | +# define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr, _qzz_len) |
4420 | +# define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) |
4421 | +# define VALGRIND_CREATE_MEMPOOL_EXT(pool, rzB, is_zeroed, flags) |
4422 | +# define VALGRIND_DESTROY_MEMPOOL(pool) |
4423 | +# define VALGRIND_MEMPOOL_ALLOC(pool, addr, sz) |
4424 | +# define VALGRIND_MEMPOOL_FREE(pool, addr) |
4425 | +# define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) |
4426 | +# define VALGRIND_FREELIKE_BLOCK(addr, rzB) |
4427 | +# define VALGRIND_MEMPOOL_METAPOOL 0 |
4428 | +#endif |
4429 | + |
4430 | +#if defined ( __cplusplus ) |
4431 | +} // end of extern C |
4432 | +#endif |
4433 | + |
4434 | +#endif // ifdef epicsMemChk_h |
4435 | + |
4436 | diff --git a/modules/libcom/src/valgrind/memcheck.h b/modules/libcom/src/valgrind/memcheck.h |
4437 | new file mode 100644 |
4438 | index 0000000..bca7323 |
4439 | --- /dev/null |
4440 | +++ b/modules/libcom/src/valgrind/memcheck.h |
4441 | @@ -0,0 +1,303 @@ |
4442 | + |
4443 | +/* |
4444 | + ---------------------------------------------------------------- |
4445 | + |
4446 | + Notice that the following BSD-style license applies to this one |
4447 | + file (memcheck.h) only. The rest of Valgrind is licensed under the |
4448 | + terms of the GNU General Public License, version 2, unless |
4449 | + otherwise indicated. See the COPYING file in the source |
4450 | + distribution for details. |
4451 | + |
4452 | + ---------------------------------------------------------------- |
4453 | + |
4454 | + This file is part of MemCheck, a heavyweight Valgrind tool for |
4455 | + detecting memory errors. |
4456 | + |
4457 | + Copyright (C) 2000-2017 Julian Seward. All rights reserved. |
4458 | + |
4459 | + Redistribution and use in source and binary forms, with or without |
4460 | + modification, are permitted provided that the following conditions |
4461 | + are met: |
4462 | + |
4463 | + 1. Redistributions of source code must retain the above copyright |
4464 | + notice, this list of conditions and the following disclaimer. |
4465 | + |
4466 | + 2. The origin of this software must not be misrepresented; you must |
4467 | + not claim that you wrote the original software. If you use this |
4468 | + software in a product, an acknowledgment in the product |
4469 | + documentation would be appreciated but is not required. |
4470 | + |
4471 | + 3. Altered source versions must be plainly marked as such, and must |
4472 | + not be misrepresented as being the original software. |
4473 | + |
4474 | + 4. The name of the author may not be used to endorse or promote |
4475 | + products derived from this software without specific prior written |
4476 | + permission. |
4477 | + |
4478 | + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
4479 | + OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
4480 | + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
4481 | + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
4482 | + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
4483 | + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
4484 | + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
4485 | + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
4486 | + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
4487 | + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
4488 | + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
4489 | + |
4490 | + ---------------------------------------------------------------- |
4491 | + |
4492 | + Notice that the above BSD-style license applies to this one file |
4493 | + (memcheck.h) only. The entire rest of Valgrind is licensed under |
4494 | + the terms of the GNU General Public License, version 2. See the |
4495 | + COPYING file in the source distribution for details. |
4496 | + |
4497 | + ---------------------------------------------------------------- |
4498 | +*/ |
4499 | + |
4500 | + |
4501 | +#ifndef __MEMCHECK_H |
4502 | +#define __MEMCHECK_H |
4503 | + |
4504 | + |
4505 | +/* This file is for inclusion into client (your!) code. |
4506 | + |
4507 | + You can use these macros to manipulate and query memory permissions |
4508 | + inside your own programs. |
4509 | + |
4510 | + See comment near the top of valgrind.h on how to use them. |
4511 | +*/ |
4512 | + |
4513 | +#include "valgrind.h" |
4514 | + |
4515 | +/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !! |
4516 | + This enum comprises an ABI exported by Valgrind to programs |
4517 | + which use client requests. DO NOT CHANGE THE ORDER OF THESE |
4518 | + ENTRIES, NOR DELETE ANY -- add new ones at the end. */ |
4519 | +typedef |
4520 | + enum { |
4521 | + VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'), |
4522 | + VG_USERREQ__MAKE_MEM_UNDEFINED, |
4523 | + VG_USERREQ__MAKE_MEM_DEFINED, |
4524 | + VG_USERREQ__DISCARD, |
4525 | + VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, |
4526 | + VG_USERREQ__CHECK_MEM_IS_DEFINED, |
4527 | + VG_USERREQ__DO_LEAK_CHECK, |
4528 | + VG_USERREQ__COUNT_LEAKS, |
4529 | + |
4530 | + VG_USERREQ__GET_VBITS, |
4531 | + VG_USERREQ__SET_VBITS, |
4532 | + |
4533 | + VG_USERREQ__CREATE_BLOCK, |
4534 | + |
4535 | + VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, |
4536 | + |
4537 | + /* Not next to VG_USERREQ__COUNT_LEAKS because it was added later. */ |
4538 | + VG_USERREQ__COUNT_LEAK_BLOCKS, |
4539 | + |
4540 | + VG_USERREQ__ENABLE_ADDR_ERROR_REPORTING_IN_RANGE, |
4541 | + VG_USERREQ__DISABLE_ADDR_ERROR_REPORTING_IN_RANGE, |
4542 | + |
4543 | + /* This is just for memcheck's internal use - don't use it */ |
4544 | + _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR |
4545 | + = VG_USERREQ_TOOL_BASE('M','C') + 256 |
4546 | + } Vg_MemCheckClientRequest; |
4547 | + |
4548 | + |
4549 | + |
4550 | +/* Client-code macros to manipulate the state of memory. */ |
4551 | + |
4552 | +/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */ |
4553 | +#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \ |
4554 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4555 | + VG_USERREQ__MAKE_MEM_NOACCESS, \ |
4556 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4557 | + |
4558 | +/* Similarly, mark memory at _qzz_addr as addressable but undefined |
4559 | + for _qzz_len bytes. */ |
4560 | +#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \ |
4561 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4562 | + VG_USERREQ__MAKE_MEM_UNDEFINED, \ |
4563 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4564 | + |
4565 | +/* Similarly, mark memory at _qzz_addr as addressable and defined |
4566 | + for _qzz_len bytes. */ |
4567 | +#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \ |
4568 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4569 | + VG_USERREQ__MAKE_MEM_DEFINED, \ |
4570 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4571 | + |
4572 | +/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is |
4573 | + not altered: bytes which are addressable are marked as defined, |
4574 | + but those which are not addressable are left unchanged. */ |
4575 | +#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \ |
4576 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4577 | + VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \ |
4578 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4579 | + |
4580 | +/* Create a block-description handle. The description is an ascii |
4581 | + string which is included in any messages pertaining to addresses |
4582 | + within the specified memory range. Has no other effect on the |
4583 | + properties of the memory range. */ |
4584 | +#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \ |
4585 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4586 | + VG_USERREQ__CREATE_BLOCK, \ |
4587 | + (_qzz_addr), (_qzz_len), (_qzz_desc), \ |
4588 | + 0, 0) |
4589 | + |
4590 | +/* Discard a block-description-handle. Returns 1 for an |
4591 | + invalid handle, 0 for a valid handle. */ |
4592 | +#define VALGRIND_DISCARD(_qzz_blkindex) \ |
4593 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4594 | + VG_USERREQ__DISCARD, \ |
4595 | + 0, (_qzz_blkindex), 0, 0, 0) |
4596 | + |
4597 | + |
4598 | +/* Client-code macros to check the state of memory. */ |
4599 | + |
4600 | +/* Check that memory at _qzz_addr is addressable for _qzz_len bytes. |
4601 | + If suitable addressibility is not established, Valgrind prints an |
4602 | + error message and returns the address of the first offending byte. |
4603 | + Otherwise it returns zero. */ |
4604 | +#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \ |
4605 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ |
4606 | + VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, \ |
4607 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4608 | + |
4609 | +/* Check that memory at _qzz_addr is addressable and defined for |
4610 | + _qzz_len bytes. If suitable addressibility and definedness are not |
4611 | + established, Valgrind prints an error message and returns the |
4612 | + address of the first offending byte. Otherwise it returns zero. */ |
4613 | +#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \ |
4614 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ |
4615 | + VG_USERREQ__CHECK_MEM_IS_DEFINED, \ |
4616 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4617 | + |
4618 | +/* Use this macro to force the definedness and addressibility of an |
4619 | + lvalue to be checked. If suitable addressibility and definedness |
4620 | + are not established, Valgrind prints an error message and returns |
4621 | + the address of the first offending byte. Otherwise it returns |
4622 | + zero. */ |
4623 | +#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \ |
4624 | + VALGRIND_CHECK_MEM_IS_DEFINED( \ |
4625 | + (volatile unsigned char *)&(__lvalue), \ |
4626 | + (unsigned long)(sizeof (__lvalue))) |
4627 | + |
4628 | + |
4629 | +/* Do a full memory leak check (like --leak-check=full) mid-execution. */ |
4630 | +#define VALGRIND_DO_LEAK_CHECK \ |
4631 | + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ |
4632 | + 0, 0, 0, 0, 0) |
4633 | + |
4634 | +/* Same as VALGRIND_DO_LEAK_CHECK but only showing the entries for |
4635 | + which there was an increase in leaked bytes or leaked nr of blocks |
4636 | + since the previous leak search. */ |
4637 | +#define VALGRIND_DO_ADDED_LEAK_CHECK \ |
4638 | + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ |
4639 | + 0, 1, 0, 0, 0) |
4640 | + |
4641 | +/* Same as VALGRIND_DO_ADDED_LEAK_CHECK but showing entries with |
4642 | + increased or decreased leaked bytes/blocks since previous leak |
4643 | + search. */ |
4644 | +#define VALGRIND_DO_CHANGED_LEAK_CHECK \ |
4645 | + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ |
4646 | + 0, 2, 0, 0, 0) |
4647 | + |
4648 | +/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */ |
4649 | +#define VALGRIND_DO_QUICK_LEAK_CHECK \ |
4650 | + VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \ |
4651 | + 1, 0, 0, 0, 0) |
4652 | + |
4653 | +/* Return number of leaked, dubious, reachable and suppressed bytes found by |
4654 | + all previous leak checks. They must be lvalues. */ |
4655 | +#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \ |
4656 | + /* For safety on 64-bit platforms we assign the results to private |
4657 | + unsigned long variables, then assign these to the lvalues the user |
4658 | + specified, which works no matter what type 'leaked', 'dubious', etc |
4659 | + are. We also initialise '_qzz_leaked', etc because |
4660 | + VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as |
4661 | + defined. */ \ |
4662 | + { \ |
4663 | + unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \ |
4664 | + unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \ |
4665 | + VALGRIND_DO_CLIENT_REQUEST_STMT( \ |
4666 | + VG_USERREQ__COUNT_LEAKS, \ |
4667 | + &_qzz_leaked, &_qzz_dubious, \ |
4668 | + &_qzz_reachable, &_qzz_suppressed, 0); \ |
4669 | + leaked = _qzz_leaked; \ |
4670 | + dubious = _qzz_dubious; \ |
4671 | + reachable = _qzz_reachable; \ |
4672 | + suppressed = _qzz_suppressed; \ |
4673 | + } |
4674 | + |
4675 | +/* Return number of leaked, dubious, reachable and suppressed bytes found by |
4676 | + all previous leak checks. They must be lvalues. */ |
4677 | +#define VALGRIND_COUNT_LEAK_BLOCKS(leaked, dubious, reachable, suppressed) \ |
4678 | + /* For safety on 64-bit platforms we assign the results to private |
4679 | + unsigned long variables, then assign these to the lvalues the user |
4680 | + specified, which works no matter what type 'leaked', 'dubious', etc |
4681 | + are. We also initialise '_qzz_leaked', etc because |
4682 | + VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as |
4683 | + defined. */ \ |
4684 | + { \ |
4685 | + unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \ |
4686 | + unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \ |
4687 | + VALGRIND_DO_CLIENT_REQUEST_STMT( \ |
4688 | + VG_USERREQ__COUNT_LEAK_BLOCKS, \ |
4689 | + &_qzz_leaked, &_qzz_dubious, \ |
4690 | + &_qzz_reachable, &_qzz_suppressed, 0); \ |
4691 | + leaked = _qzz_leaked; \ |
4692 | + dubious = _qzz_dubious; \ |
4693 | + reachable = _qzz_reachable; \ |
4694 | + suppressed = _qzz_suppressed; \ |
4695 | + } |
4696 | + |
4697 | + |
4698 | +/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it |
4699 | + into the provided zzvbits array. Return values: |
4700 | + 0 if not running on valgrind |
4701 | + 1 success |
4702 | + 2 [previously indicated unaligned arrays; these are now allowed] |
4703 | + 3 if any parts of zzsrc/zzvbits are not addressable. |
4704 | + The metadata is not copied in cases 0, 2 or 3 so it should be |
4705 | + impossible to segfault your system by using this call. |
4706 | +*/ |
4707 | +#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \ |
4708 | + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ |
4709 | + VG_USERREQ__GET_VBITS, \ |
4710 | + (const char*)(zza), \ |
4711 | + (char*)(zzvbits), \ |
4712 | + (zznbytes), 0, 0) |
4713 | + |
4714 | +/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it |
4715 | + from the provided zzvbits array. Return values: |
4716 | + 0 if not running on valgrind |
4717 | + 1 success |
4718 | + 2 [previously indicated unaligned arrays; these are now allowed] |
4719 | + 3 if any parts of zza/zzvbits are not addressable. |
4720 | + The metadata is not copied in cases 0, 2 or 3 so it should be |
4721 | + impossible to segfault your system by using this call. |
4722 | +*/ |
4723 | +#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \ |
4724 | + (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \ |
4725 | + VG_USERREQ__SET_VBITS, \ |
4726 | + (const char*)(zza), \ |
4727 | + (const char*)(zzvbits), \ |
4728 | + (zznbytes), 0, 0 ) |
4729 | + |
4730 | +/* Disable and re-enable reporting of addressing errors in the |
4731 | + specified address range. */ |
4732 | +#define VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE(_qzz_addr,_qzz_len) \ |
4733 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4734 | + VG_USERREQ__DISABLE_ADDR_ERROR_REPORTING_IN_RANGE, \ |
4735 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4736 | + |
4737 | +#define VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(_qzz_addr,_qzz_len) \ |
4738 | + VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \ |
4739 | + VG_USERREQ__ENABLE_ADDR_ERROR_REPORTING_IN_RANGE, \ |
4740 | + (_qzz_addr), (_qzz_len), 0, 0, 0) |
4741 | + |
4742 | +#endif |
4743 | + |
4744 | + |
4745 | diff --git a/modules/libcom/src/valgrind/valgrind.h b/modules/libcom/src/valgrind/valgrind.h |
4746 | old mode 100755 |
4747 | new mode 100644 |
4748 | index c503172..5b26c98 |
4749 | --- a/modules/libcom/src/valgrind/valgrind.h |
4750 | +++ b/modules/libcom/src/valgrind/valgrind.h |
4751 | @@ -12,7 +12,7 @@ |
4752 | This file is part of Valgrind, a dynamic binary instrumentation |
4753 | framework. |
4754 | |
4755 | - Copyright (C) 2000-2013 Julian Seward. All rights reserved. |
4756 | + Copyright (C) 2000-2017 Julian Seward. All rights reserved. |
4757 | |
4758 | Redistribution and use in source and binary forms, with or without |
4759 | modification, are permitted provided that the following conditions |
4760 | @@ -89,10 +89,14 @@ |
4761 | || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6)) |
4762 | */ |
4763 | #define __VALGRIND_MAJOR__ 3 |
4764 | -#define __VALGRIND_MINOR__ 10 |
4765 | +#define __VALGRIND_MINOR__ 16 |
4766 | |
4767 | |
4768 | -#include <stdarg.h> |
4769 | +#if defined ( __cplusplus ) |
4770 | +# include <cstdarg> |
4771 | +#else |
4772 | +# include <stdarg.h> |
4773 | +#endif |
4774 | |
4775 | /* Nb: this file might be included in a file compiled with -ansi. So |
4776 | we can't use C++ style "//" comments nor the "asm" keyword (instead |
4777 | @@ -122,6 +126,8 @@ |
4778 | #undef PLAT_s390x_linux |
4779 | #undef PLAT_mips32_linux |
4780 | #undef PLAT_mips64_linux |
4781 | +#undef PLAT_x86_solaris |
4782 | +#undef PLAT_amd64_solaris |
4783 | |
4784 | |
4785 | #if defined(__APPLE__) && defined(__i386__) |
4786 | @@ -130,14 +136,14 @@ |
4787 | # define PLAT_amd64_darwin 1 |
4788 | #elif (defined(__MINGW32__) && !defined(__MINGW64__)) \ |
4789 | || defined(__CYGWIN32__) \ |
4790 | - || (defined(_WIN32) && defined(_M_IX86) && defined(__GNUC__)) |
4791 | + || (defined(_WIN32) && defined(_M_IX86)) |
4792 | # define PLAT_x86_win32 1 |
4793 | #elif defined(__MINGW64__) \ |
4794 | - || (defined(_WIN64) && defined(_M_X64) && defined(__GNUC__)) |
4795 | + || (defined(_WIN64) && defined(_M_X64)) |
4796 | # define PLAT_amd64_win64 1 |
4797 | #elif defined(__linux__) && defined(__i386__) |
4798 | # define PLAT_x86_linux 1 |
4799 | -#elif defined(__linux__) && defined(__x86_64__) |
4800 | +#elif defined(__linux__) && defined(__x86_64__) && !defined(__ILP32__) |
4801 | # define PLAT_amd64_linux 1 |
4802 | #elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__) |
4803 | # define PLAT_ppc32_linux 1 |
4804 | @@ -157,6 +163,10 @@ |
4805 | # define PLAT_mips64_linux 1 |
4806 | #elif defined(__linux__) && defined(__mips__) && (__mips!=64) |
4807 | # define PLAT_mips32_linux 1 |
4808 | +#elif defined(__sun) && defined(__i386__) |
4809 | +# define PLAT_x86_solaris 1 |
4810 | +#elif defined(__sun) && defined(__x86_64__) |
4811 | +# define PLAT_amd64_solaris 1 |
4812 | #else |
4813 | /* If we're not compiling for our target platform, don't generate |
4814 | any inline asms. */ |
4815 | @@ -244,10 +254,11 @@ |
4816 | inline asm stuff to be useful. |
4817 | */ |
4818 | |
4819 | -/* ------------------------- x86-{linux,darwin} ---------------- */ |
4820 | +/* ----------------- x86-{linux,darwin,solaris} ---------------- */ |
4821 | |
4822 | #if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \ |
4823 | - || (defined(PLAT_x86_win32) && defined(__GNUC__)) |
4824 | + || (defined(PLAT_x86_win32) && defined(__GNUC__)) \ |
4825 | + || defined(PLAT_x86_solaris) |
4826 | |
4827 | typedef |
4828 | struct { |
4829 | @@ -307,7 +318,8 @@ typedef |
4830 | ); \ |
4831 | } while (0) |
4832 | |
4833 | -#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */ |
4834 | +#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) |
4835 | + || PLAT_x86_solaris */ |
4836 | |
4837 | /* ------------------------- x86-Win32 ------------------------- */ |
4838 | |
4839 | @@ -382,14 +394,15 @@ valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request, |
4840 | |
4841 | #endif /* PLAT_x86_win32 */ |
4842 | |
4843 | -/* ------------------------ amd64-{linux,darwin} --------------- */ |
4844 | +/* ----------------- amd64-{linux,darwin,solaris} --------------- */ |
4845 | |
4846 | #if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin) \ |
4847 | + || defined(PLAT_amd64_solaris) \ |
4848 | || (defined(PLAT_amd64_win64) && defined(__GNUC__)) |
4849 | |
4850 | typedef |
4851 | struct { |
4852 | - unsigned long long int nraddr; /* where's the code? */ |
4853 | + unsigned long int nraddr; /* where's the code? */ |
4854 | } |
4855 | OrigFn; |
4856 | |
4857 | @@ -401,14 +414,14 @@ typedef |
4858 | _zzq_default, _zzq_request, \ |
4859 | _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ |
4860 | __extension__ \ |
4861 | - ({ volatile unsigned long long int _zzq_args[6]; \ |
4862 | - volatile unsigned long long int _zzq_result; \ |
4863 | - _zzq_args[0] = (unsigned long long int)(_zzq_request); \ |
4864 | - _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ |
4865 | - _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ |
4866 | - _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ |
4867 | - _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ |
4868 | - _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ |
4869 | + ({ volatile unsigned long int _zzq_args[6]; \ |
4870 | + volatile unsigned long int _zzq_result; \ |
4871 | + _zzq_args[0] = (unsigned long int)(_zzq_request); \ |
4872 | + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ |
4873 | + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ |
4874 | + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ |
4875 | + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ |
4876 | + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ |
4877 | __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ |
4878 | /* %RDX = client_request ( %RAX ) */ \ |
4879 | "xchgq %%rbx,%%rbx" \ |
4880 | @@ -421,7 +434,7 @@ typedef |
4881 | |
4882 | #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ |
4883 | { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ |
4884 | - volatile unsigned long long int __addr; \ |
4885 | + volatile unsigned long int __addr; \ |
4886 | __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ |
4887 | /* %RAX = guest_NRADDR */ \ |
4888 | "xchgq %%rcx,%%rcx" \ |
4889 | @@ -445,7 +458,7 @@ typedef |
4890 | ); \ |
4891 | } while (0) |
4892 | |
4893 | -#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */ |
4894 | +#endif /* PLAT_amd64_linux || PLAT_amd64_darwin || PLAT_amd64_solaris */ |
4895 | |
4896 | /* ------------------------- amd64-Win64 ------------------------- */ |
4897 | |
4898 | @@ -530,8 +543,8 @@ typedef |
4899 | |
4900 | typedef |
4901 | struct { |
4902 | - unsigned long long int nraddr; /* where's the code? */ |
4903 | - unsigned long long int r2; /* what tocptr do we need? */ |
4904 | + unsigned long int nraddr; /* where's the code? */ |
4905 | + unsigned long int r2; /* what tocptr do we need? */ |
4906 | } |
4907 | OrigFn; |
4908 | |
4909 | @@ -544,15 +557,15 @@ typedef |
4910 | _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ |
4911 | \ |
4912 | __extension__ \ |
4913 | - ({ unsigned long long int _zzq_args[6]; \ |
4914 | - unsigned long long int _zzq_result; \ |
4915 | - unsigned long long int* _zzq_ptr; \ |
4916 | - _zzq_args[0] = (unsigned long long int)(_zzq_request); \ |
4917 | - _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ |
4918 | - _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ |
4919 | - _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ |
4920 | - _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ |
4921 | - _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ |
4922 | + ({ unsigned long int _zzq_args[6]; \ |
4923 | + unsigned long int _zzq_result; \ |
4924 | + unsigned long int* _zzq_ptr; \ |
4925 | + _zzq_args[0] = (unsigned long int)(_zzq_request); \ |
4926 | + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ |
4927 | + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ |
4928 | + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ |
4929 | + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ |
4930 | + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ |
4931 | _zzq_ptr = _zzq_args; \ |
4932 | __asm__ volatile("mr 3,%1\n\t" /*default*/ \ |
4933 | "mr 4,%2\n\t" /*ptr*/ \ |
4934 | @@ -568,7 +581,7 @@ typedef |
4935 | |
4936 | #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ |
4937 | { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ |
4938 | - unsigned long long int __addr; \ |
4939 | + unsigned long int __addr; \ |
4940 | __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ |
4941 | /* %R3 = guest_NRADDR */ \ |
4942 | "or 2,2,2\n\t" \ |
4943 | @@ -607,8 +620,8 @@ typedef |
4944 | |
4945 | typedef |
4946 | struct { |
4947 | - unsigned long long int nraddr; /* where's the code? */ |
4948 | - unsigned long long int r2; /* what tocptr do we need? */ |
4949 | + unsigned long int nraddr; /* where's the code? */ |
4950 | + unsigned long int r2; /* what tocptr do we need? */ |
4951 | } |
4952 | OrigFn; |
4953 | |
4954 | @@ -621,15 +634,15 @@ typedef |
4955 | _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ |
4956 | \ |
4957 | __extension__ \ |
4958 | - ({ unsigned long long int _zzq_args[6]; \ |
4959 | - unsigned long long int _zzq_result; \ |
4960 | - unsigned long long int* _zzq_ptr; \ |
4961 | - _zzq_args[0] = (unsigned long long int)(_zzq_request); \ |
4962 | - _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \ |
4963 | - _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \ |
4964 | - _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \ |
4965 | - _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \ |
4966 | - _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \ |
4967 | + ({ unsigned long int _zzq_args[6]; \ |
4968 | + unsigned long int _zzq_result; \ |
4969 | + unsigned long int* _zzq_ptr; \ |
4970 | + _zzq_args[0] = (unsigned long int)(_zzq_request); \ |
4971 | + _zzq_args[1] = (unsigned long int)(_zzq_arg1); \ |
4972 | + _zzq_args[2] = (unsigned long int)(_zzq_arg2); \ |
4973 | + _zzq_args[3] = (unsigned long int)(_zzq_arg3); \ |
4974 | + _zzq_args[4] = (unsigned long int)(_zzq_arg4); \ |
4975 | + _zzq_args[5] = (unsigned long int)(_zzq_arg5); \ |
4976 | _zzq_ptr = _zzq_args; \ |
4977 | __asm__ volatile("mr 3,%1\n\t" /*default*/ \ |
4978 | "mr 4,%2\n\t" /*ptr*/ \ |
4979 | @@ -645,7 +658,7 @@ typedef |
4980 | |
4981 | #define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \ |
4982 | { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \ |
4983 | - unsigned long long int __addr; \ |
4984 | + unsigned long int __addr; \ |
4985 | __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \ |
4986 | /* %R3 = guest_NRADDR */ \ |
4987 | "or 2,2,2\n\t" \ |
4988 | @@ -754,7 +767,7 @@ typedef |
4989 | |
4990 | typedef |
4991 | struct { |
4992 | - unsigned long long int nraddr; /* where's the code? */ |
4993 | + unsigned long int nraddr; /* where's the code? */ |
4994 | } |
4995 | OrigFn; |
4996 | |
4997 | @@ -767,14 +780,14 @@ typedef |
4998 | _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \ |
4999 | \ |
5000 | __extension__ \ |
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.