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