1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 /*!
34 \class SoContextHandler SoContextHandler.h Inventor/misc/SoContextHandler.h
35 \brief The SoContextHandler class is for now to be treated as an internal class.
36
37 \ingroup general
38
39 \since Coin 2.0
40 */
41
42 // FIXME: should be documented and be part of the Doxygen API doc,
43 // since it's a public class (and possibly useful
44 // externally). 20030225 mortene.
45 //
46 // UPDATE: there also a function in SoGLCacheContextElement which
47 // looks like it might do the same thing: scheduleDeleteCallback()..?
48 // Investigate.
49 //
50 // 20040723 mortene.
51
52 //
53 // UPDATE: No, scheduleDeleteCallback() will not do the same thing.
54 // It's only used for deleting SoGLDisplayLists.
55 //
56 // 20050209 pederb
57 //
58
59 // *************************************************************************
60
61 /*! \file SoContextHandler.h */
62 #include <Inventor/misc/SoContextHandler.h>
63
64 #include <cstdlib>
65
66 #include <Inventor/errors/SoDebugError.h>
67 #include <Inventor/lists/SbList.h>
68
69 #include "misc/SbHash.h"
70 #include "threads/threadsutilp.h"
71 #include "tidbitsp.h"
72 #include "glue/glp.h"
73
74 // *************************************************************************
75
76 class socontexthandler_cbitem {
77 public:
socontexthandler_cbitem(void)78 socontexthandler_cbitem(void) : func(NULL), closure(NULL), idx(0) { }
79
operator ==(const socontexthandler_cbitem & theother)80 int operator==(const socontexthandler_cbitem & theother) {
81 return
82 this->func == theother.func &&
83 this->closure == theother.closure;
84 }
85
operator unsigned long(void) const86 operator unsigned long(void) const {
87 unsigned long key = 0;
88 // create an xor key
89 const unsigned char * ptr = (const unsigned char *) this;
90
91 // a bit hackish. Stop xor'ing at idx
92 const unsigned char * stop = (const unsigned char*) &this->idx;
93
94 const ptrdiff_t size = stop - ptr;
95
96 for (int i = 0; i < size; i++) {
97 int shift = (i%4) * 8;
98 key ^= (ptr[i]<<shift);
99 }
100 return key;
101 }
102
103 SoContextHandler::ContextDestructionCB * func;
104 void * closure;
105
106 // this must be last!!
107 int idx;
108 };
109
110 // "extern C" wrapper is needed with the OSF1/cxx compiler (probably a
111 // bug in the compiler, but it doesn't seem to hurt to do this
112 // anyway).
113 extern "C" { static int
socontexthandler_qsortcb(const void * p0,const void * p1)114 socontexthandler_qsortcb(const void * p0, const void * p1)
115 {
116 socontexthandler_cbitem * i0 = (socontexthandler_cbitem *) p0;
117 socontexthandler_cbitem * i1 = (socontexthandler_cbitem *) p1;
118
119 return int(i0->idx) - int(i1->idx);
120 }
121 }
122
123 // *************************************************************************
124
125 static SbHash<socontexthandler_cbitem, uint32_t> * socontexthandler_hashlist;
126 static uint32_t socontexthandler_idx = 0;
127 static void * socontexthandler_mutex;
128
129 // *************************************************************************
130
131 static void
socontexthandler_cleanup(void)132 socontexthandler_cleanup(void)
133 {
134 #if COIN_DEBUG
135 const int len = socontexthandler_hashlist ?
136 socontexthandler_hashlist->getNumElements() : 0;
137 if (len > 0) {
138 // Can't use SoDebugError here, as SoError et al might have been
139 // "cleaned up" already.
140 (void)printf("Coin debug: socontexthandler_cleanup(): %d context-bound "
141 "resources not free'd before exit.\n", len);
142 }
143 #endif // COIN_DEBUG
144 delete socontexthandler_hashlist;
145 socontexthandler_hashlist = NULL;
146 socontexthandler_idx = 0;
147 CC_MUTEX_DESTRUCT(socontexthandler_mutex);
148 }
149
150 // *************************************************************************
151
152 /*!
153 This method \e must be called by client code which destructs a
154 context, to guarantee that there are no memory leaks upon context
155 destruction.
156
157 This will take care of correctly freeing context-bound resources,
158 like OpenGL texture objects and display lists.
159
160 Before calling this function, the context \e must be made current.
161
162 Note that if you are using one of the standard GUI-binding libraries from
163 Kongsberg Oil & Gas Technologies, this is taken care of automatically for
164 contexts for canvases set up by SoQt, SoWin, etc.
165 */
166 void
destructingContext(uint32_t contextid)167 SoContextHandler::destructingContext(uint32_t contextid)
168 {
169 CC_MUTEX_CONSTRUCT(socontexthandler_mutex);
170 CC_MUTEX_LOCK(socontexthandler_mutex);
171 if (socontexthandler_hashlist == NULL) {
172 CC_MUTEX_UNLOCK(socontexthandler_mutex);
173 return;
174 }
175
176 SbList <socontexthandler_cbitem> listcopy;
177 for(
178 SbHash<socontexthandler_cbitem, uint32_t>::const_iterator iter =
179 socontexthandler_hashlist->const_begin();
180 iter!=socontexthandler_hashlist->const_end();
181 ++iter
182 ) {
183 listcopy.append(iter->key);
184 }
185
186 CC_MUTEX_UNLOCK(socontexthandler_mutex);
187
188 qsort((void*) listcopy.getArrayPtr(),
189 listcopy.getLength(),
190 sizeof(socontexthandler_cbitem),
191 socontexthandler_qsortcb);
192
193 // process callbacks FILO-style so that callbacks registered first
194 // are called last. HACK WARNING: SoGLCacheContextElement will add a
195 // callback in initClass(). It's quite important that this callback
196 // is called after all other callbacks (since the other callbacks
197 // might schedule destruction of GL resources through the methods in
198 // SoGLCacheContextElement). This criteria is met as it is now,
199 // since it's the only callback added while initializing Coin
200 // (SoDB::init()).
201
202 // FIXME: We should probably add a new method in
203 // SoGLCacheContextElement which this class can call after all the
204 // regular callbacks though. pederb, 2004-10-27
205 for (int i = listcopy.getLength()-1; i >= 0; i--) {
206 const socontexthandler_cbitem & item = listcopy[i];
207 item.func(contextid, item.closure);
208 }
209
210 // tell glglue that this context is dead
211 coin_glglue_destruct(contextid);
212 }
213
214 // *************************************************************************
215
216 /*!
217 Add a callback which will be called every time a GL context is
218 destructed. The callback should delete all GL resources tied to that
219 context.
220
221 All nodes/classes that allocate GL resources should set up a callback
222 like this. Add the callback in the constructor of the node/class,
223 and remove it in the destructor.
224
225 \sa removeContextDestructionCallback()
226 */
227 void
addContextDestructionCallback(ContextDestructionCB * func,void * closure)228 SoContextHandler::addContextDestructionCallback(ContextDestructionCB * func,
229 void * closure)
230 {
231 CC_MUTEX_CONSTRUCT(socontexthandler_mutex);
232 CC_MUTEX_LOCK(socontexthandler_mutex);
233 if (socontexthandler_hashlist == NULL) {
234 socontexthandler_hashlist = new SbHash<socontexthandler_cbitem, uint32_t> (64);
235 // make this callback trigger after the SoGLCacheContext cleanup function
236 // by setting priority to -1
237 coin_atexit((coin_atexit_f *)socontexthandler_cleanup, CC_ATEXIT_NORMAL_LOWPRIORITY);
238 }
239 socontexthandler_cbitem item;
240 item.func = func;
241 item.closure = closure;
242 item.idx = socontexthandler_idx++;
243 (*socontexthandler_hashlist)[item] = item.idx;
244 CC_MUTEX_UNLOCK(socontexthandler_mutex);
245 }
246
247 /*!
248 Remove a context destruction callback.
249
250 \sa addContextDestructionCallback()
251 */
252 void
removeContextDestructionCallback(ContextDestructionCB * func,void * closure)253 SoContextHandler::removeContextDestructionCallback(ContextDestructionCB * func, void * closure)
254 {
255 assert(socontexthandler_hashlist);
256
257 socontexthandler_cbitem item;
258 item.func = func;
259 item.closure = closure;
260
261 CC_MUTEX_LOCK(socontexthandler_mutex);
262 size_t didremove = socontexthandler_hashlist->erase(item);
263 assert(didremove);
264 CC_MUTEX_UNLOCK(socontexthandler_mutex);
265 }
266
267 // *************************************************************************
268