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