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
21TypeHandle TypedWritable::_type_handle;
22TypedWritable* const TypedWritable::Null = nullptr;
23
24/**
25 *
26 */
27TypedWritable::
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 */
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 */
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 */
136encode_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 */
266void TypedWritable::
267add_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 */
290void TypedWritable::
291remove_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}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A trivial implementation for atomic adjustments for systems that don't require multiprogramming,...
static Pointer get_ptr(const Pointer &var)
Atomically retrieves the snapshot value of the indicated variable.
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
bool init()
Initializes the BamReader prior to reading any objects from its source.
Definition: bamReader.cxx:85
TypedWritable * read_object()
Reads a single object from the Bam file.
Definition: bamReader.cxx:224
set_source
Changes the source of future datagrams for this BamReader.
Definition: bamReader.h:154
This is the fundamental interface for writing binary objects to a Bam file, to be extracted later by ...
Definition: bamWriter.h:63
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
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:147
set_target
Changes the destination of future datagrams written by the BamWriter.
Definition: bamWriter.h:91
This class can be used to write a series of datagrams into a memory buffer.
bool read_header(std::string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
void swap_data(vector_uchar &other)
Swaps the data in the internal buffer with that of the other buffer.
A class to retrieve the individual data elements previously stored in a Datagram.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
A base class for all things that want to be reference-counted.
void ref() const
Explicitly increments the reference count.
virtual bool unref() const
Explicitly decrements the reference count.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
virtual void finalize(BamReader *manager)
Called by the BamReader to perform any final actions needed for setting up the object after all objec...
virtual bool require_fully_complete() const
Some objects require all of their nested pointers to have been completed before the objects themselve...
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 ReferenceCount * as_reference_count()
Returns the pointer cast to a ReferenceCount pointer, if it is in fact of that type.
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 ...
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...
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 write_datagram(BamWriter *manager, Datagram &dg)
Writes the contents of this object to the datagram for shipping out to a Bam file.
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().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.