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 SoAuditorList SoAuditorList.h Inventor/lists/SoAuditorList.h
35 \brief The SoAuditorList class is used to keep track of auditors for certain object classes.
36
37 \ingroup general
38
39 This class is mainly for internal use (from SoBase) and it should
40 not be necessary to be familiar with it for "ordinary" Coin use.
41 */
42
43
44 #include <Inventor/fields/SoField.h>
45 #include <Inventor/fields/SoFieldContainer.h>
46 #include <Inventor/sensors/SoDataSensor.h>
47 #if COIN_DEBUG
48 #include <Inventor/errors/SoDebugError.h>
49 #endif // COIN_DEBUG
50
51 #ifdef HAVE_CONFIG_H
52 #include <config.h>
53 #endif // HAVE_CONFIG_H
54
55 #ifdef COIN_THREADSAFE
56 #include "threads/recmutexp.h"
57 // we need this lock to avoid that auditors are added/removed by one
58 // thread while another thread is notifying
59 #define NOTIFY_LOCK (void) cc_recmutex_internal_notify_lock()
60 #define NOTIFY_UNLOCK (void) cc_recmutex_internal_notify_unlock()
61 #else // COIN_THREADSAFE
62 #define NOTIFY_LOCK
63 #define NOTIFY_UNLOCK
64 #endif // !COIN_THREADSAFE
65
66 /*!
67 Default constructor.
68 */
SoAuditorList(void)69 SoAuditorList::SoAuditorList(void)
70 : SbPList(8)
71 {
72 }
73
74 /*!
75 Destructor.
76 */
~SoAuditorList()77 SoAuditorList::~SoAuditorList()
78 {
79 }
80
81 /*!
82 Append an \a auditor of \a type to the list.
83 */
84 void
append(void * const auditor,const SoNotRec::Type type)85 SoAuditorList::append(void * const auditor, const SoNotRec::Type type)
86 {
87 NOTIFY_LOCK;
88 SbPList::append(auditor);
89 SbPList::append((void *)type);
90 NOTIFY_UNLOCK;
91 }
92
93 /*!
94 Set \a auditor pointer and auditor \a type in list at \a index.
95 */
96 void
set(const int index,void * const auditor,const SoNotRec::Type type)97 SoAuditorList::set(const int index,
98 void * const auditor, const SoNotRec::Type type)
99 {
100 NOTIFY_LOCK;
101 assert(index >= 0 && index < this->getLength());
102
103 SbPList::set(index * 2, auditor);
104 SbPList::set(index * 2 + 1, (void *)type);
105 NOTIFY_UNLOCK;
106 }
107
108 /*!
109 Returns number of elements in list.
110 */
111 int
getLength(void) const112 SoAuditorList::getLength(void) const
113 {
114 return SbPList::getLength() / 2;
115 }
116
117 /*!
118 Find \a auditor of \a type in list and return index. Returns -1 if
119 \a auditor is not in the list.
120 */
121 int
find(void * const auditor,const SoNotRec::Type type) const122 SoAuditorList::find(void * const auditor, const SoNotRec::Type type) const
123 {
124 const int num = this->getLength();
125 for (int i = 0; i < num; i++) {
126 if (this->getObject(i) == auditor && this->getType(i) == type)
127 return i;
128 }
129 return -1;
130 }
131
132 /*!
133 Returns auditor pointer at \a index.
134 */
135 void *
getObject(const int index) const136 SoAuditorList::getObject(const int index) const
137 {
138 return SbPList::operator[](index * 2);
139 }
140
141 /*!
142 Returns auditor type at \a index.
143 */
144 SoNotRec::Type
getType(const int index) const145 SoAuditorList::getType(const int index) const
146 {
147 const uintptr_t tmp = (uintptr_t)(SbPList::operator[](index*2+1));
148 return (SoNotRec::Type)tmp;
149 }
150
151 /*!
152 Remove auditor at \a index.
153 */
154 void
remove(const int index)155 SoAuditorList::remove(const int index)
156 {
157 NOTIFY_LOCK;
158 assert(index >= 0 && index < this->getLength());
159 SbPList::remove(index * 2); // ptr
160 SbPList::remove(index * 2); // type
161 NOTIFY_UNLOCK;
162 }
163
164 /*!
165 Remove \a auditor of \a type from list.
166 */
167 void
remove(void * const auditor,const SoNotRec::Type type)168 SoAuditorList::remove(void * const auditor, const SoNotRec::Type type)
169 {
170 this->remove(this->find(auditor, type));
171 }
172
173 /*!
174 Send notification to all our auditors.
175 */
176 void
notify(SoNotList * l)177 SoAuditorList::notify(SoNotList * l)
178 {
179 const int num = this->getLength();
180 if (num == 1) { // fast path for common case
181 this->doNotify(l, this->getObject(0), this->getType(0));
182 }
183 // handle multiple auditors by copying the list, in case any one of
184 // the notifications we're sending out changes the list
185 // mid-traversal (that's also why we take special care of the
186 // 1-auditor case above -- so we don't have to copy the list for the
187 // common case)
188 else if (num > 1) {
189 // FIXME: should perhaps use a more general mechanism to detect when
190 // to ignore notification? (In SoFieldContainer::notify() -- based
191 // on SoNotList::getTimeStamp()?) 20000304 mortene.
192 SbPList notified(num);
193
194 for (int i = 0; i < num; i++) {
195 void * auditor = this->getObject(i);
196 if (notified.find(auditor) == -1) {
197 // use a copy of 'l', since the notification list might change
198 // when auditors are notified
199 SoNotList listcopy(l);
200 this->doNotify(&listcopy, auditor, this->getType(i));
201 notified.append(auditor);
202 }
203 }
204
205 // FIXME: it should be possible for the application programmer to
206 // do this (it is for instance useful and tempting to do it upon
207 // changes in engines). pederb, 2001-11-06
208 assert(num == this->getLength() &&
209 "auditors can not be removed during the notification loop");
210 }
211 }
212
213 //
214 // Private method used to propagate 'l' to the 'auditor' of type 'type'
215 //
216 void
doNotify(SoNotList * l,const void * auditor,const SoNotRec::Type type)217 SoAuditorList::doNotify(SoNotList * l, const void * auditor, const SoNotRec::Type type)
218 {
219 l->setLastType(type);
220
221 switch (type) {
222 case SoNotRec::CONTAINER:
223 case SoNotRec::PARENT:
224 {
225 SoFieldContainer * obj = (SoFieldContainer *)auditor;
226 obj->notify(l);
227 }
228 break;
229
230 case SoNotRec::SENSOR:
231 {
232 SoDataSensor * obj = (SoDataSensor *)auditor;
233 #if COIN_DEBUG && 0 // debug
234 SoDebugError::postInfo("SoAuditorList::notify",
235 "notify and schedule sensor: %p", obj);
236 #endif // debug
237 // don't schedule the sensor here. The sensor instance will do
238 // that in notify() (it might also choose _not_ to schedule),
239 obj->notify(l);
240 }
241 break;
242
243 case SoNotRec::FIELD:
244 case SoNotRec::ENGINE:
245 {
246 // We used to check whether or not the fields was already
247 // dirty before we transmitted the notification
248 // message. This is _not_ correct (the dirty flag is
249 // conceptually only relevant for whether or not to do
250 // re-evaluation), so don't try to "optimize" the
251 // notification mechanism by re-introducing that "feature".
252 // :^/
253 ((SoField *)auditor)->notify(l);
254 }
255 break;
256
257 default:
258 assert(0 && "Unknown auditor type");
259 }
260 }
261
262 #undef NOTIFY_LOCK
263 #undef NOTIFY_UNLOCK
264