Hello all:
I think I have finally tracked down some of the issues with our
application, but I wanted to write to ask if anyone can see any others
and comment on one issue I think may need a fix/workaround in OpenSG.
For purposes of this discussion, our application functions as follows:
- There is a primordial thread P that takes care of all rendering from
a single scene graph
- There are 2 background threads (BG1, BG2) that load data from disk
and periodically re-sync with the primordial thread to connect up the
data they loaded.
- All threads share the same aspect
- No threads make changes to the same FC at the same time. (BG1 and
BG2 create data, P connects that data to the scene graph)
- References to FCs are held using MTRecPtr's
1) The first question is, what issues do the experts on the list see
with this type of arrangement. Our simplistic thinking had been that
as long as we don't write to the same FC at the same time in the same
thread we would be ok. Now that we are into it further, we are
worried about a couple of specific issues.
* Change lists: As discussed in another posting, we think that the
change list should be thread safe and that as long as we call
commitChanges() we should be good.
* Aspect store: Each FC has an aspect store. If two threads read
from an FC in parallel, is it possible to corrupt the aspect store?
(we have seen a few crashes on testing size of the aspect store of an
fc)
* FCFactory: All new field containers get registered with the
field container factory. Is the registration process thread safe?
* Other thread safety issues: Is there anywhere else we are not
thinking off that may have some race conditions when sharing the same
aspect across multiple threads?
2) XxxPtr creation is not thread safe
We think this one is killing us. To put it more concretely, imagine a
scenario like this...
---- P ------
ChunkMaterialMTRecPtr mat = OSG::ChunkMaterial::create()
....
SharedDataObject.setSharedMaterial(mat);
---- BG1 ----------
GeomMTRecPtr geom = ...
ChunkMaterialMTRecPtr mat = SharedDataObject.getSharedMaterial();
geom->setMaterial(mat)
---- BG2 ----------
GeomMTRecPtr geom = ...
ChunkMaterialMTRecPtr mat = SharedDataObject.getSharedMaterial();
geom->setMaterial(mat)
There is a race condition here on the reference counting. More
specifically, if multiple ChunkMaterialMTRecPtr's are created to the
same FC at close to the same time, the reference count can get out of
sync (we think). This can lead to dangling references and/or access
to destroyed objects.
This may seem like a no-brainer issue, just don't do it. But it gets
more complex then that. For example this issue makes it so the simple
geom utilities in OpenSG are not thread safe because they call
getDefaultMaterial, which in turn creates RecPtrs that can get out of
sync.
More generally, the problem is that RefPtrs can't be treated the same
as C++ primitive types. They can not be read from in parallel and
stay consistent. The thing that really tripped us up is that we are
used to using the boost shared_ptr implementation and it does allow
for this degreee of thread safety. They explain in much better then I
can:
http://www.boost.org/doc/libs/1_38_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety
(copied here for easy access)
------------------------------------------
Thread Safety
shared_ptr objects offer the same level of thread safety as built-in
types. A shared_ptr instance can be "read" (accessed using only const
operations) simultaneously by multiple threads. Different shared_ptr
instances can be "written to" (accessed using mutable operations such
as operator= or reset) simultaneosly by multiple threads (even when
these instances are copies, and share the same reference count
underneath.)
Any other simultaneous accesses result in undefined behavior.
Examples:
shared_ptr<int> p(new int(42));
//--- Example 1 ---
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
//--- Example 2 ---
// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
//--- Example 3 ---
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
//--- Example 4 ---
// thread A
p3 = p2; // reads p2, writes p3
// thread B
// p2 goes out of scope: undefined, the destructor is considered a
"write access"
//--- Example 5 ---
// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
Starting with Boost release 1.33.0, shared_ptr uses a lock-free
implementation on the following platforms:
* GNU GCC on x86 or x86-64;
* GNU GCC on IA64;
* Metrowerks CodeWarrior on PowerPC;
* GNU GCC on PowerPC;
* Windows.
------------------------------------------
As you can see, boost goes to great lengths to enable this type of
use. What I am trying to determine is can OpenSG do the same thing.
I really don't know, but IMHO it would be very very nice if it could.
I think it would protect users from this type of nasty bug and it
would also allow the Ptr types to be used in a way that is consistent
with boost (and the future TR1).
Thoughts?
-Allen
------------------------------------------------------------------------------
Apps built with the Adobe(R) Flex(R) framework and Flex Builder(TM) are
powering Web 2.0 with engaging, cross-platform capabilities. Quickly and
easily build your RIAs with Flex Builder, the Eclipse(TM)based development
software that enables intelligent coding and step-through debugging.
Download the free 60 day trial. http://p.sf.net/sfu/www-adobe-com
_______________________________________________
Opensg-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensg-users