1abcabd5294831aad4889ebf78e7ea81cba509d0
[blender-staging.git] / intern / cycles / util / util_guarded_allocator.h
1 /*
2  * Copyright 2011-2015 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef __UTIL_GUARDED_ALLOCATOR_H__
18 #define __UTIL_GUARDED_ALLOCATOR_H__
19
20 #include <cstddef>
21 #include <memory>
22
23 #include "util/util_debug.h"
24 #include "util/util_types.h"
25
26 #ifdef WITH_BLENDER_GUARDEDALLOC
27 #  include "../../guardedalloc/MEM_guardedalloc.h"
28 #endif
29
30 CCL_NAMESPACE_BEGIN
31
32 /* Internal use only. */
33 void util_guarded_mem_alloc(size_t n);
34 void util_guarded_mem_free(size_t n);
35
36 /* Guarded allocator for the use with STL. */
37 template <typename T>
38 class GuardedAllocator {
39 public:
40         typedef size_t size_type;
41         typedef ptrdiff_t difference_type;
42         typedef T *pointer;
43         typedef const T *const_pointer;
44         typedef T& reference;
45         typedef const T& const_reference;
46         typedef T value_type;
47
48         GuardedAllocator() {}
49         GuardedAllocator(const GuardedAllocator&) {}
50
51         T *allocate(size_t n, const void *hint = 0)
52         {
53                 (void)hint;
54                 size_t size = n * sizeof(T);
55                 util_guarded_mem_alloc(size);
56                 if(n == 0) {
57                         return NULL;
58                 }
59                 T *mem;
60 #ifdef WITH_BLENDER_GUARDEDALLOC
61                 /* C++ standard requires allocation functions to allocate memory suitably
62                  * aligned for any standard type. This is 16 bytes for 64 bit platform as
63                  * far as i concerned. We might over-align on 32bit here, but that should
64                  * be all safe actually.
65                  */
66                 mem = (T*)MEM_mallocN_aligned(size, 16, "Cycles Alloc");
67 #else
68                 mem = (T*)malloc(size);
69 #endif
70                 if(mem == NULL) {
71                         throw std::bad_alloc();
72                 }
73                 return mem;
74         }
75
76         void deallocate(T *p, size_t n)
77         {
78                 util_guarded_mem_free(n * sizeof(T));
79                 if(p != NULL) {
80 #ifdef WITH_BLENDER_GUARDEDALLOC
81                         MEM_freeN(p);
82 #else
83                         free(p);
84 #endif
85                 }
86         }
87
88         T *address(T& x) const
89         {
90                 return &x;
91         }
92
93         const T *address(const T& x) const
94         {
95                 return &x;
96         }
97
98         GuardedAllocator<T>& operator=(const GuardedAllocator&)
99         {
100                 return *this;
101         }
102
103         void construct(T *p, const T& val)
104         {
105                 if(p != NULL) {
106                         new ((T *)p) T(val);
107                 }
108         }
109
110         void destroy(T *p)
111         {
112                 p->~T();
113         }
114
115         size_t max_size() const
116         {
117                 return size_t(-1);
118         }
119
120         template <class U>
121         struct rebind {
122                 typedef GuardedAllocator<U> other;
123         };
124
125         template <class U>
126         GuardedAllocator(const GuardedAllocator<U>&) {}
127
128         template <class U>
129         GuardedAllocator& operator=(const GuardedAllocator<U>&) { return *this; }
130
131         inline bool operator==(GuardedAllocator const& /*other*/) const { return true; }
132         inline bool operator!=(GuardedAllocator const& other) const { return !operator==(other); }
133
134 #ifdef _MSC_VER
135         /* Welcome to the black magic here.
136          *
137          * The issue is that MSVC C++ allocates container proxy on any
138          * vector initialization, including static vectors which don't
139          * have any data yet. This leads to several issues:
140          *
141          * - Static objects initialization fiasco (global_stats from
142          *   util_stats.h might not be initialized yet).
143          * - If main() function changes allocator type (for example,
144          *   this might happen with `blender --debug-memory`) nobody
145          *   will know how to convert already allocated memory to a new
146          *   guarded allocator.
147          *
148          * Here we work this around by making it so container proxy does
149          * not use guarded allocation. A bit fragile, unfortunately.
150          */
151         template<>
152         struct rebind<std::_Container_proxy> {
153                 typedef std::allocator<std::_Container_proxy> other;
154         };
155
156         operator std::allocator<std::_Container_proxy>() const
157         {
158                 return std::allocator<std::_Container_proxy>();
159         }
160 #endif
161 };
162
163 /* Get memory usage and peak from the guarded STL allocator. */
164 size_t util_guarded_get_mem_used(void);
165 size_t util_guarded_get_mem_peak(void);
166
167 /* Call given function and keep track if it runs out of memory.
168  *
169  * If it does run out f memory, stop execution and set progress
170  * to do a global cancel.
171  *
172  * It's not fully robust, but good enough to catch obvious issues
173  * when running out of memory.
174  */
175 #define MEM_GUARDED_CALL(progress, func, ...) \
176         do { \
177                 try { \
178                         (func)(__VA_ARGS__); \
179                 } \
180                 catch (std::bad_alloc&) { \
181                         fprintf(stderr, "Error: run out of memory!\n"); \
182                         fflush(stderr); \
183                         (progress)->set_error("Out of memory"); \
184                 } \
185         } while(false)
186
187 CCL_NAMESPACE_END
188
189 #endif  /* __UTIL_GUARDED_ALLOCATOR_H__ */