1 /*===========================================================================
2 *
3 *                            PUBLIC DOMAIN NOTICE
4 *               National Center for Biotechnology Information
5 *
6 *  This software/database is a "United States Government Work" under the
7 *  terms of the United States Copyright Act.  It was written as part of
8 *  the author's official duties as a United States Government employee and
9 *  thus cannot be copyrighted.  This software/database is freely available
10 *  to the public for use. The National Library of Medicine and the U.S.
11 *  Government have not placed any restriction on its use or reproduction.
12 *
13 *  Although all reasonable efforts have been taken to ensure the accuracy
14 *  and reliability of the software and data, the NLM and the U.S.
15 *  Government do not and cannot warrant the performance or results that
16 *  may be obtained by using this software or data. The NLM and the U.S.
17 *  Government disclaim all warranties, express or implied, including
18 *  warranties of performance, merchantability or fitness for any particular
19 *  purpose.
20 *
21 *  Please cite the author in any work or product based on this material.
22 *
23 * ===========================================================================
24 *
25 */
26 
27 #include "pmemmgr.hpp"
28 #include <kfc/memory.hpp>
29 #include <kfc/callstk.hpp>
30 #include <kfc/caps.hpp>
31 #include <kfc/rsrc.hpp>
32 
33 #include <stdlib.h>
34 #include <string.h>
35 
36 #if UNIX
37 #include <sys/resource.h>
38 #endif
39 
40 namespace vdb3
41 {
42 
43     const caps_t CONST_MEM_CAPS
44         = CAP_PROP_READ
45         | CAP_READ
46         | CAP_SUBRANGE
47         | CAP_CAST
48         ;
49 
50     const caps_t NEW_MEM_CAPS
51         = CONST_MEM_CAPS
52         | CAP_WRITE
53         | CAP_RESIZE
54         ;
55 
56 
57     /*------------------------------------------------------------------
58      * const_memory_t
59      *  an object representing a range of address space
60      */
61     class const_memory_t : implements MemoryItf
62     {
63     public:
64 
65         virtual void resize ( const bytes_t & new_size, bool clear );
66         const_memory_t ( const void * ptr, const bytes_t & size );
67         ~ const_memory_t ();
68 
69     protected:
70 
71         virtual void * get_mapped_memory ( bytes_t * size ) const;
72 
73         const void * ptr;
74         bytes_t size;
75     };
76 
resize(const bytes_t & new_size,bool clear)77     void const_memory_t :: resize ( const bytes_t & new_size, bool clear )
78     {
79         FUNC_ENTRY ();
80         CONST_THROW ( xc_internal_err, "should never be called" );
81     }
82 
const_memory_t(const void * _ptr,const bytes_t & _size)83     const_memory_t :: const_memory_t ( const void * _ptr, const bytes_t & _size )
84         : ptr ( _ptr )
85         , size ( _size )
86     {
87     }
88 
~const_memory_t()89     const_memory_t :: ~ const_memory_t ()
90     {
91         ptr = 0;
92         size = ( U64 ) 0;
93     }
94 
get_mapped_memory(bytes_t * _size) const95     void * const_memory_t :: get_mapped_memory ( bytes_t * _size ) const
96     {
97         if ( _size != 0 )
98             * _size = size;
99         return ( void * ) ptr;
100     }
101 
102 
103     /*------------------------------------------------------------------
104      * Memory
105      *  an object representing a range of address space
106      */
107     class Memory : public const_memory_t
108     {
109     public:
110 
111         virtual void resize ( const bytes_t & new_size, bool clear );
112         Memory ( void * ptr, const bytes_t & size );
113         ~ Memory ();
114     };
115 
resize(const bytes_t & new_size,bool clear)116     void Memory :: resize ( const bytes_t & new_size, bool clear )
117     {
118         FUNC_ENTRY ();
119 
120         MemMgrItf * mmgr = get_mmgr ();
121         assert ( mmgr != 0 );
122 
123         assert ( ptr != 0 );
124         assert ( new_size != size );
125 
126         ptr = mmgr -> _resize ( ( void * ) ptr, size, new_size, clear );
127         size = new_size;
128     }
129 
Memory(void * _ptr,const bytes_t & _size)130     Memory :: Memory ( void * _ptr, const bytes_t & _size )
131         : const_memory_t ( _ptr, _size )
132     {
133     }
134 
~Memory()135     Memory :: ~ Memory ()
136     {
137         FUNC_ENTRY ();
138 
139         MemMgrItf * mmgr = get_mmgr ();
140         assert ( mmgr != 0 );
141 
142         mmgr -> _free ( ( void * ) ptr, size );
143     }
144 
145 
146     /*------------------------------------------------------------------
147      * PrimordMemMgr
148      */
149 
make_primordial()150     MemMgr PrimordMemMgr :: make_primordial ()
151     {
152         assert ( callstk != 0 );
153         FUNC_ENTRY ();
154 
155         // allow for the very first call
156         static atomic_t < I32 > latch = 0;
157         if ( latch . test_and_set ( 0, 1 ) != 0 )
158         {
159             const char msg [] = "primordial memory manager exists";
160             if ( rsrc == 0 )
161                 throw msg;
162             CONST_THROW ( xc_program_state_violation, msg );
163         }
164 
165         // determine system memory quota
166         size_t quota = ~ ( size_t ) 0;
167 #if UNIX
168         struct rlimit rlim;
169         int status = getrlimit ( RLIMIT_AS, & rlim );
170         if ( status == 0 )
171             quota = rlim . rlim_cur;
172 #endif
173 
174         // allocate the object memory
175         PrimordMemMgr * obj;
176         obj = ( PrimordMemMgr * ) calloc ( 1, sizeof * obj );
177         if ( obj == 0 )
178             throw "out of memory allocating primordial memory manager";
179 
180         // construct the primordial mmgr
181         new ( obj ) PrimordMemMgr ( quota, quota - sizeof * obj );
182 
183         // embed self into object
184         // DO NOT STORE DUPLICATE
185         // as this would introduce a cycle
186         obj -> mmgr = obj;
187         obj -> obj_size = sizeof * obj;
188 
189         // create the reference
190         return obj -> make_mmgr_ref ( obj, CAP_RDWR | CAP_ALLOC );
191     }
192 
193 
194     /* alloc
195      */
alloc(const bytes_t & size,bool clear)196     Mem PrimordMemMgr :: alloc ( const bytes_t & size, bool clear )
197     {
198         FUNC_ENTRY ();
199 
200         // allocate a raw block
201         void * block = ( size == ( U64 ) 0 ) ? 0 : _alloc ( size, clear );
202 
203         // create the memory object
204         Memory * obj = new Memory ( block, size );
205 
206         // create the reference
207         return make_mem_ref ( obj, obj, NEW_MEM_CAPS );
208     }
209 
make_const(const void * ptr,const bytes_t & size)210     Mem PrimordMemMgr :: make_const ( const void * ptr, const bytes_t & size )
211     {
212         FUNC_ENTRY ();
213 
214         // create the const memory object
215         const_memory_t * obj = new const_memory_t ( ptr, size );
216 
217         // create the reference
218         return make_mem_ref ( obj, obj, CONST_MEM_CAPS );
219     }
220 
_alloc(const bytes_t & size,bool clear)221     void * PrimordMemMgr :: _alloc ( const bytes_t & size, bool clear )
222     {
223         FUNC_ENTRY ();
224 
225         size_t bytes = size;
226 
227         // allocate from quota
228         if ( avail . read_and_sub_ge ( size, size ) < size )
229             THROW ( xc_mem_quota, "memory quota exhausted allocating %lu bytes", ( U64 ) size );
230 
231         // allocate using process memory manager
232         void * ptr = clear ? calloc ( 1, bytes ) : malloc ( bytes );
233         if ( ptr == 0 )
234         {
235             // return bytes to quota
236             avail += bytes;
237 
238             // failure
239             THROW ( xc_no_mem, "process memory exhausted allocating %zu bytes", bytes );
240         }
241 
242        return ptr;
243 
244     }
245 
_resize(void * old_ptr,const bytes_t & old_size,const bytes_t & new_size,bool clear)246     void * PrimordMemMgr :: _resize ( void * old_ptr, const bytes_t & old_size, const bytes_t & new_size, bool clear )
247     {
248         FUNC_ENTRY ();
249 
250         // nothing to do if there is no size change
251         if ( old_size == new_size )
252             return old_ptr;
253 
254         // check for quota
255         if ( ( avail + old_size ) < new_size )
256             THROW ( xc_mem_quota, "memory quota exhausted reallocating %lu to %lu bytes", ( U64 ) old_size, ( U64 ) new_size );
257 
258         size_t new_bytes = new_size;
259 
260         // not supposed to be called with bad values
261         assert ( old_ptr != 0 || old_size == ( U64 ) 0 );
262 
263         // reallocate using process memory manager
264         // alternatively, we could always allocate and copy, destroying old
265         void * new_ptr = realloc ( old_ptr, new_bytes );
266         if ( new_ptr == 0 )
267             THROW ( xc_no_mem, "process memory exhausted reallocating %lu to %lu bytes", ( U64 ) old_size, ( U64 ) new_size );
268 
269         // update bytes remaining
270         if ( old_size > new_size )
271             avail += old_size - new_size;
272         else
273         {
274             avail -= new_size - old_size;
275 
276             // clear extended area
277             if ( clear )
278                 memset ( & ( ( char* ) new_ptr ) [ old_size ], 0, new_size - old_size );
279         }
280 
281         return new_ptr;
282     }
283 
_free(void * ptr,const bytes_t & size)284     void PrimordMemMgr :: _free ( void * ptr, const bytes_t & size )
285     {
286         // not supposed to be called with bad values
287         assert ( ptr != 0 || size == ( U64 ) 0 );
288 
289         // return to the process memory manager
290         // alternatively, could clear before free
291         free ( ptr );
292 
293         // update bytes remaining
294         if ( ( void * ) this != ptr )
295             avail += size;
296     }
297 
PrimordMemMgr(const bytes_t & q,const bytes_t & a)298     PrimordMemMgr :: PrimordMemMgr ( const bytes_t & q, const bytes_t & a )
299         : quota ( q )
300         , avail ( a )
301     {
302     }
303 
~PrimordMemMgr()304     PrimordMemMgr :: ~ PrimordMemMgr ()
305     {
306         // TBD - can test if avail != quota
307         quota = 0;
308         avail = 0;
309     }
310 
operator delete(void * ptr)311     void PrimordMemMgr :: operator delete ( void * ptr )
312     {
313         free ( ptr );
314     }
315 }
316