Panda3D
typedWritable.cxx
1 // Filename: typedWritable.cxx
2 // Created by: jason (08Jun00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "typedWritable.h"
16 #include "bamWriter.h"
17 #include "bamReader.h"
18 #include "datagramOutputFile.h"
19 #include "datagramInputFile.h"
20 #include "lightMutexHolder.h"
21 #include "bam.h"
22 
23 TypeHandle TypedWritable::_type_handle;
24 TypedWritable* const TypedWritable::Null = (TypedWritable*)0L;
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: TypedWritable::Destructor
28 // Access: Public, Virtual
29 // Description:
30 ////////////////////////////////////////////////////////////////////
31 TypedWritable::
32 ~TypedWritable() {
33  // Remove the object pointer from the BamWriters that reference it.
34  BamWriterLink *link;
35  do {
36  link = (BamWriterLink *)AtomicAdjust::get_ptr(_bam_writers);
37  if (link == (BamWriterLink *)NULL) {
38  // List is unlocked and empty - no writers to remove.
39  return;
40  }
41  link = (BamWriterLink *)(((uintptr_t)link) & ~(uintptr_t)0x1);
42  } while (link != AtomicAdjust::
43  compare_and_exchange_ptr(_bam_writers, (void *)link, (void *)NULL));
44 
45  while (link != (BamWriterLink *)NULL) {
46  BamWriterLink *next_link = link->_next;
47  link->_writer->object_destructs(this);
48  delete link;
49  link = next_link;
50  }
51 }
52 
53 ////////////////////////////////////////////////////////////////////
54 // Function: TypedWritable::write_datagram
55 // Access: Public, Virtual
56 // Description: Writes the contents of this object to the datagram
57 // for shipping out to a Bam file.
58 ////////////////////////////////////////////////////////////////////
59 void TypedWritable::
61 }
62 
63 ////////////////////////////////////////////////////////////////////
64 // Function: TypedWritable::update_bam_nested
65 // Access: Public, Virtual
66 // Description: Called by the BamWriter when this object has not
67 // itself been modified recently, but it should check
68 // its nested objects for updates.
69 ////////////////////////////////////////////////////////////////////
70 void TypedWritable::
72 }
73 
74 ////////////////////////////////////////////////////////////////////
75 // Function: TypedWritable::complete_pointers
76 // Access: Public, Virtual
77 // Description: Receives an array of pointers, one for each time
78 // manager->read_pointer() was called in fillin().
79 // Returns the number of pointers processed.
80 //
81 // This is the callback function that is made by the
82 // BamReader at some later point, after all of the
83 // required pointers have been filled in. It is
84 // necessary because there might be forward references
85 // in a bam file; when we call read_pointer() in
86 // fillin(), the object may not have been read from the
87 // file yet, so we do not have a pointer available at
88 // that time. Thus, instead of returning a pointer,
89 // read_pointer() simply reserves a later callback.
90 // This function provides that callback. The calling
91 // object is responsible for keeping track of the number
92 // of times it called read_pointer() and extracting the
93 // same number of pointers out of the supplied vector,
94 // and storing them appropriately within the object.
95 ////////////////////////////////////////////////////////////////////
98  return 0;
99 }
100 
101 ////////////////////////////////////////////////////////////////////
102 // Function: TypedWritable::require_fully_complete
103 // Access: Public, Virtual
104 // Description: Some objects require all of their nested pointers to
105 // have been completed before the objects themselves can
106 // be completed. If this is the case, override this
107 // method to return true, and be careful with circular
108 // references (which would make the object unreadable
109 // from a bam file).
110 ////////////////////////////////////////////////////////////////////
111 bool TypedWritable::
113  return false;
114 }
115 
116 ////////////////////////////////////////////////////////////////////
117 // Function: TypedWritable::fillin
118 // Access: Public, Virtual
119 // Description: This internal function is intended to be called by
120 // each class's make_from_bam() method to read in all of
121 // the relevant data from the BamFile for the new
122 // object. It is also called directly by the BamReader
123 // to re-read the data for an object that has been
124 // placed on the stream for an update.
125 ////////////////////////////////////////////////////////////////////
126 void TypedWritable::
128 }
129 
130 
131 ////////////////////////////////////////////////////////////////////
132 // Function: TypedWritable::finalize
133 // Access: Public, Virtual
134 // Description: Called by the BamReader to perform any final actions
135 // needed for setting up the object after all objects
136 // have been read and all pointers have been completed.
137 ////////////////////////////////////////////////////////////////////
138 void TypedWritable::
140 }
141 
142 ////////////////////////////////////////////////////////////////////
143 // Function: TypedWritable::as_reference_count
144 // Access: Public, Virtual
145 // Description: Returns the pointer cast to a ReferenceCount pointer,
146 // if it is in fact of that type.
147 ////////////////////////////////////////////////////////////////////
150  return NULL;
151 }
152 
153 ////////////////////////////////////////////////////////////////////
154 // Function: TypedWritable::encode_to_bam_stream
155 // Access: Published
156 // Description: Converts the TypedWritable object into a single
157 // stream of data using a BamWriter, and stores that
158 // data in the indicated string. Returns true on
159 // success, false on failure.
160 //
161 // This is a convenience method particularly useful for
162 // cases when you are only serializing a single object.
163 // If you have many objects to process, it is more
164 // efficient to use the same BamWriter to serialize all
165 // of them together.
166 ////////////////////////////////////////////////////////////////////
167 bool TypedWritable::
168 encode_to_bam_stream(string &data, BamWriter *writer) const {
169  data.clear();
170  ostringstream stream;
171 
172  // We use nested scoping to ensure the destructors get called in the
173  // right order.
174  {
175  DatagramOutputFile dout;
176  if (!dout.open(stream)) {
177  return false;
178  }
179 
180  if (writer == NULL) {
181  // Create our own writer.
182 
183  if (!dout.write_header(_bam_header)) {
184  return false;
185  }
186 
187  BamWriter writer(&dout);
188  if (!writer.init()) {
189  return false;
190  }
191 
192  if (!writer.write_object(this)) {
193  return false;
194  }
195  } else {
196  // Use the existing writer.
197  writer->set_target(&dout);
198  bool result = writer->write_object(this);
199  writer->set_target(NULL);
200  if (!result) {
201  return false;
202  }
203  }
204  }
205 
206  data = stream.str();
207  return true;
208 }
209 
210 ////////////////////////////////////////////////////////////////////
211 // Function: TypedWritable::decode_raw_from_bam_stream
212 // Access: Published, Static
213 // Description: Reads the string created by a previous call to
214 // encode_to_bam_stream(), and extracts the single
215 // object on that string. Returns true on success,
216 // false on on error.
217 //
218 // This variant sets the TypedWritable and
219 // ReferenceCount pointers separately; both are pointers
220 // to the same object. The reference count is not
221 // incremented; it is the caller's responsibility to
222 // manage the reference count.
223 //
224 // Note that this method cannot be used to retrieve
225 // objects that do not inherit from ReferenceCount,
226 // because these objects cannot persist beyond the
227 // lifetime of the BamReader that reads them. To
228 // retrieve these objects from a bam stream, you must
229 // construct a BamReader directly.
230 //
231 // If you happen to know that the particular object in
232 // question inherits from TypedWritableReferenceCount or
233 // PandaNode, consider calling the variant of
234 // decode_from_bam_stream() defined for those methods,
235 // which presents a simpler interface.
236 ////////////////////////////////////////////////////////////////////
237 bool TypedWritable::
239  const string &data, BamReader *reader) {
240  istringstream stream(data);
241 
242  DatagramInputFile din;
243  if (!din.open(stream)) {
244  return false;
245  }
246 
247  if (reader == NULL) {
248  // Create a local reader.
249  string head;
250  if (!din.read_header(head, _bam_header.size())) {
251  return false;
252  }
253 
254  if (head != _bam_header) {
255  return false;
256  }
257 
258  BamReader reader(&din);
259  if (!reader.init()) {
260  return false;
261  }
262 
263  if (!reader.read_object(ptr, ref_ptr)) {
264  return false;
265  }
266 
267  if (!reader.resolve()) {
268  return false;
269  }
270 
271  if (ref_ptr == NULL) {
272  // Can't support non-reference-counted objects.
273  return false;
274  }
275 
276  // Protect the pointer from accidental deletion when the BamReader
277  // goes away.
278  ref_ptr->ref();
279 
280  } else {
281  // Use the existing reader.
282  reader->set_source(&din);
283  if (!reader->read_object(ptr, ref_ptr)) {
284  reader->set_source(NULL);
285  return false;
286  }
287 
288  if (!reader->resolve()) {
289  reader->set_source(NULL);
290  return false;
291  }
292 
293  if (ref_ptr == NULL) {
294  // Can't support non-reference-counted objects.
295  reader->set_source(NULL);
296  return false;
297  }
298 
299  // This BamReader isn't going away, but we have to balance the
300  // unref() below.
301  ref_ptr->ref();
302  reader->set_source(NULL);
303  }
304 
305 
306  // Now decrement the ref count, without deleting the object. This
307  // may reduce the reference count to zero, but that's OK--we trust
308  // the caller to manage the reference count from this point on.
309  ref_ptr->unref();
310  return true;
311 }
312 
313 ////////////////////////////////////////////////////////////////////
314 // Function: TypedWritable::add_bam_writer
315 // Access: Private
316 // Description: Called by the BamWriter to add itself to this
317 // TypedWritable's list of BamWriters, so that it can
318 // receive notification whenever this object destructs.
319 // This method may be safely called from any thread.
320 ////////////////////////////////////////////////////////////////////
321 void TypedWritable::
322 add_bam_writer(BamWriter *writer) {
323  nassertv(writer != (BamWriter *)NULL);
324 
325  BamWriterLink *begin;
326  BamWriterLink *new_link = new BamWriterLink;
327  new_link->_writer = writer;
328 
329  // Assert that we got at least a 2-byte aligned pointer from new.
330  nassertv(((uintptr_t)new_link & (uintptr_t)0x1) == 0);
331 
332  // This spins if the lower bit is 1, ie. if the pointer is locked.
333  do {
334  begin = (BamWriterLink *)AtomicAdjust::get_ptr(_bam_writers);
335  begin = (BamWriterLink *)(((uintptr_t)begin) & ~(uintptr_t)0x1);
336  new_link->_next = begin;
337  } while (begin != AtomicAdjust::
338  compare_and_exchange_ptr(_bam_writers, (void *)begin, (void *)new_link));
339 }
340 
341 ////////////////////////////////////////////////////////////////////
342 // Function: TypedWritable::remove_bam_writer
343 // Access: Private
344 // Description: The converse of add_bam_writer.
345 // This method may be safely called from any thread.
346 ////////////////////////////////////////////////////////////////////
347 void TypedWritable::
348 remove_bam_writer(BamWriter *writer) {
349  nassertv(writer != (BamWriter *)NULL);
350 
351  BamWriterLink *begin;
352 
353  // Grab the head pointer and lock it in one atomic operation.
354  // We lock it by tagging the pointer.
355  do {
356  begin = (BamWriterLink *)AtomicAdjust::get_ptr(_bam_writers);
357  begin = (BamWriterLink *)(((uintptr_t)begin) & ~(uintptr_t)0x1);
358  if (begin == NULL) {
359  // The list is empty, nothing to remove.
360  return;
361  }
362  } while (begin != AtomicAdjust::
363  compare_and_exchange_ptr(_bam_writers, (void *)begin,
364  (void *)((uintptr_t)begin | (uintptr_t)0x1)));
365 
366  // Find the writer in the list.
367  BamWriterLink *prev_link = (BamWriterLink *)NULL;
368  BamWriterLink *link = begin;
369 
370  while (link != NULL && link->_writer != writer) {
371  prev_link = link;
372  link = link->_next;
373  }
374 
375  if (link == (BamWriterLink *)NULL) {
376  // Not found. Just unlock and leave.
377  _bam_writers = (void *)begin;
378  return;
379  }
380 
381  if (prev_link == (BamWriterLink *)NULL) {
382  // It's the first link. Replace and unlock in one atomic op.
383  _bam_writers = (void *)link->_next;
384  } else {
385  prev_link->_next = link->_next;
386  _bam_writers = (void *)begin; // Unlock
387  }
388 
389  delete link;
390 }
void set_source(DatagramGenerator *source)
Changes the source of future datagrams for this BamReader.
Definition: bamReader.cxx:75
static Pointer get_ptr(const Pointer &var)
Atomically retrieves the snapshot value of the indicated variable.
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
virtual ReferenceCount * as_reference_count()
Returns the pointer cast to a ReferenceCount pointer, if it is in fact of that type.
This is the fundamental interface for extracting binary objects from a Bam file, as generated by a Ba...
Definition: bamReader.h:122
bool resolve()
This may be called at any time during processing of the Bam file to resolve all the known pointers so...
Definition: bamReader.cxx:359
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:249
This class can be used to read a binary file that consists of an arbitrary header followed by a numbe...
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:37
bool write_header(const string &header)
Writes a sequence of bytes to the beginning of the datagram file.
void set_target(DatagramSink *target)
Changes the destination of future datagrams written by the BamWriter.
Definition: bamWriter.cxx:76
static bool decode_raw_from_bam_stream(TypedWritable *&ptr, ReferenceCount *&ref_ptr, const string &data, BamReader *reader=NULL)
Reads the string created by a previous call to encode_to_bam_stream(), and extracts the single object...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:73
bool open(const FileReference *file)
Opens the indicated filename for reading.
bool read_header(string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
virtual void fillin(DatagramIterator &scan, BamReader *manager)
This internal function is intended to be called by each class's make_from_bam() method to read in all...
virtual void write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
bool open(const FileReference *file)
Opens the indicated filename for writing.
bool init()
Initializes the BamReader prior to reading any objects from its source.
Definition: bamReader.cxx:94
virtual int complete_pointers(TypedWritable **p_list, BamReader *manager)
Receives an array of pointers, one for each time manager->read_pointer() was called in fillin()...
A trivial implementation for atomic adjustments for systems that don't require multiprogramming, and therefore don't require special atomic operations.
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
Definition: bamWriter.cxx:152
string encode_to_bam_stream() const
Converts the TypedWritable object into a single stream of data using a BamWriter, and returns that da...
Definition: typedWritable.I:86
void ref() const
Explicitly increments the reference count.
This class can be used to write a binary file that consists of an arbitrary header followed by a numb...
A base class for all things that want to be reference-counted.
virtual bool require_fully_complete() const
Some objects require all of their nested pointers to have been completed before the objects themselve...
A class to retrieve the individual data elements previously stored in a Datagram. ...
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:85
virtual void update_bam_nested(BamWriter *manager)
Called by the BamWriter when this object has not itself been modified recently, but it should check i...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:98
virtual bool unref() const
Explicitly decrements the reference count.