Panda3D
Loading...
Searching...
No Matches
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 */
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 */
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 */
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 */
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 */
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...
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.
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...
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
set_target
Changes the destination of future datagrams written by the BamWriter.
Definition bamWriter.h:94
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.
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...
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.