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  */
53 void TypedWritable::
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  */
61 void TypedWritable::
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  */
91 bool TypedWritable::
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  */
102 void TypedWritable::
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  */
112 void TypedWritable::
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  */
135 bool TypedWritable::
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  */
189 bool TypedWritable::
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 }
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Pointer get_ptr(const Pointer &var)
Atomically retrieves the snapshot value of the indicated variable.
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
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:110
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
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:224
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 ...
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 init()
Initializes the BamReader prior to reading any objects from its source.
Definition: bamReader.cxx:85
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().
set_source
Changes the source of future datagrams for this BamReader.
Definition: bamReader.h:151
A trivial implementation for atomic adjustments for systems that don't require multiprogramming,...
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void ref() const
Explicitly increments the reference count.
This class can be used to write a series of datagrams into a memory buffer.
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...
bool read_header(std::string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
A class to retrieve the individual data elements previously stored in a Datagram.
set_target
Changes the destination of future datagrams written by the BamWriter.
Definition: bamWriter.h:91
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
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:38
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:147
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool unref() const
Explicitly decrements the reference count.
void swap_data(vector_uchar &other)
Swaps the data in the internal buffer with that of the other buffer.