Line data Source code
1 :
2 : /* Copyright (c) 2005-2016, Stefan Eilemann <eile@equalizergraphics.com>
3 : * Daniel Nachbaur <danielnachbaur@gmail.com>
4 : *
5 : * This file is part of Collage <https://github.com/Eyescale/Collage>
6 : *
7 : * This library is free software; you can redistribute it and/or modify it under
8 : * the terms of the GNU Lesser General Public License version 2.1 as published
9 : * by the Free Software Foundation.
10 : *
11 : * This library is distributed in the hope that it will be useful, but WITHOUT
12 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 : * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14 : * details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public License
17 : * along with this library; if not, write to the Free Software Foundation, Inc.,
18 : * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 : */
20 :
21 : #ifndef CO_OBJECT_H
22 : #define CO_OBJECT_H
23 :
24 : #include <co/dispatcher.h> // base class
25 : #include <co/localNode.h> // used in RefPtr
26 : #include <co/types.h> // for Nodes
27 : #include <co/version.h> // used as default parameter
28 : #include <lunchbox/bitOperation.h> // byteswap inline impl
29 :
30 : namespace co
31 : {
32 : namespace detail { class Object; }
33 : class ObjectCM;
34 : typedef lunchbox::RefPtr< ObjectCM > ObjectCMPtr;
35 :
36 : # define CO_COMMIT_NEXT LB_UNDEFINED_UINT32 //!< the next commit incarnation
37 :
38 : /**
39 : * A distributed object.
40 : *
41 : * Please refer to the Equalizer Programming Guide and examples on how to
42 : * develop and use distributed objects. The Serializable implements a typical
43 : * common use case based on the basic Object.
44 : */
45 : class Object : public Dispatcher
46 : {
47 : public:
48 : /** Object change handling characteristics, see Programming Guide */
49 : enum ChangeType
50 : {
51 : NONE, //!< @internal
52 : STATIC, //!< non-versioned, unbuffered, static object.
53 : INSTANCE, //!< use only instance data
54 : DELTA, //!< use pack/unpack delta
55 : UNBUFFERED //!< versioned, but don't retain versions
56 : };
57 :
58 : /** Destruct the distributed object. @version 1.0 */
59 : CO_API virtual ~Object();
60 :
61 : /** @name Data Access */
62 : //@{
63 : /**
64 : * @return true if the object is attached (mapped or registered).
65 : * @version 1.0
66 : */
67 : CO_API bool isAttached() const;
68 :
69 : /** @return the local node to which this object is attached. @version 1.0 */
70 : CO_API LocalNodePtr getLocalNode();
71 :
72 : /**
73 : * Set the object's unique identifier.
74 : *
75 : * Only to be called on unattached objects. The application has to
76 : * ensure the uniqueness of the identifier in the peer-to-peer node
77 : * network. By default, each object has an identifier guaranteed to be
78 : * unique. During mapping, the identifier of the object will be
79 : * overwritten with the identifier of the master object.
80 : * @version 1.0
81 : */
82 : CO_API void setID( const uint128_t& identifier );
83 :
84 : /** @return the object's unique identifier. @version 1.0 */
85 : CO_API const uint128_t& getID() const;
86 :
87 : /** @return the node-wide unique object instance identifier. @version 1.0 */
88 : CO_API uint32_t getInstanceID() const;
89 :
90 : /** @internal @return if this object keeps instance data buffers. */
91 : CO_API bool isBuffered() const;
92 :
93 : /**
94 : * @return true if this instance is a registered master version.
95 : * @version 1.0
96 : */
97 : CO_API bool isMaster() const;
98 : //@}
99 :
100 : /** @name Versioning */
101 : //@{
102 : /** @return how the changes are to be handled. @version 1.0 */
103 12 : virtual ChangeType getChangeType() const { return STATIC; }
104 :
105 : /**
106 : * Limit the number of queued versions on slave instances.
107 : *
108 : * Changing the return value of this method causes the master instance
109 : * to block during commit() if any slave instance has reached the
110 : * maximum number of queued versions. The method is called on the slave
111 : * instance. Multiple slave instances may use different values.
112 : *
113 : * Changing the return value at runtime, that is, after the slave
114 : * instance has been mapped is unsupported and causes undefined
115 : * behavior.
116 : *
117 : * Not supported on master instances for slave object commits. Open an
118 : * issue if you need this.
119 : *
120 : * @return the number of queued versions a slave instance may have.
121 : * @version 1.0
122 : */
123 149 : virtual uint64_t getMaxVersions() const
124 149 : { return std::numeric_limits< uint64_t >::max(); }
125 :
126 : /**
127 : * Return the compressor to be used for data transmission.
128 : *
129 : * This default implementation uses a heuristic to choose the best lossless
130 : * compressor in terms of speed and compression ratio. The application may
131 : * override this method to deactivate compression by returning the default
132 : * pression::Data::CompressorInfo or to select object-specific compressors.
133 : * @version 1.6
134 : */
135 : CO_API virtual CompressorInfo chooseCompressor() const;
136 :
137 : /**
138 : * Return if this object needs a commit.
139 : *
140 : * This function is used for optimization, to detect early that no
141 : * commit is needed. If it returns true, pack() or getInstanceData()
142 : * will be executed. The serialization methods can still decide to not
143 : * write any data, upon which no new version will be created. If it
144 : * returns false, commit() will exit early.
145 : *
146 : * @return true if a commit is needed.
147 : * @version 1.0
148 : */
149 114 : virtual bool isDirty() const { return true; }
150 :
151 : /**
152 : * Push the instance data of the object to the given nodes.
153 : *
154 : * Used to push object data from a Node, instead of pulling it during
155 : * mapping. Does not establish any mapping, that is, the receiving side
156 : * will typically use LocalNode::mapObject with VERSION_NONE to
157 : * establish a slave mapping. LocalNode::objectPush() is called once for
158 : * each node in the nodes list.
159 : *
160 : * Buffered objects do not reserialize their instance data. Multicast
161 : * connections are preferred, that is, for a set of nodes sharing one
162 : * multicast connection the data is only send once.
163 : *
164 : * @param groupID An identifier to group a set of push operations.
165 : * @param objectType A per-push identifier.
166 : * @param nodes The vector of nodes to push to.
167 : * @version 1.0
168 : */
169 : CO_API void push( const uint128_t& groupID, const uint128_t& objectType,
170 : const Nodes& nodes );
171 :
172 : /**
173 : * Commit a new version of this object.
174 : *
175 : * Objects using the change type STATIC can not be committed.
176 : *
177 : * Master instances will increment new versions continuously, starting at
178 : * VERSION_FIRST. If the object has not changed, no new version will
179 : * be generated, that is, the current version is returned. The high
180 : * value of the returned version will always be 0.
181 : *
182 : * Slave objects can be committed, but have certain caveats for
183 : * serialization. Please refer to the Equalizer Programming Guide for
184 : * details. Slave object commits will return a random version on a
185 : * successful commit, or VERSION_NONE if the object has not changed since
186 : * the last commit. The high value of a successful slave commit will never
187 : * be 0.
188 : *
189 : * The incarnation count is meaningful for buffered master objects. The
190 : * commit implementation will keep all instance data committed with an
191 : * incarnation count newer than <code>current_incarnation -
192 : * getAutoObsolete()</code>. By default, each call to commit creates a new
193 : * incarnation, retaining the data from last getAutoObsolete() commit
194 : * calls. When the application wishes to auto obsolete by another metric
195 : * than commit calls, it has to consistently provide a corresponding
196 : * incarnation counter. Buffers with a higher incarnation count than the
197 : * current are discarded. A typical use case is to tie the auto obsoletion
198 : * to an application-specific frame loop. Decreasing the incarnation counter
199 : * will lead to undefined results.
200 : *
201 : * @param incarnation the commit incarnation for auto obsoletion.
202 : * @return the new head version (master) or commit id (slave).
203 : * @version 1.0
204 : */
205 : CO_API virtual uint128_t commit( const uint32_t incarnation =
206 : CO_COMMIT_NEXT );
207 :
208 : /**
209 : * Automatically obsolete old versions.
210 : *
211 : * The versions for the last count incarnations are retained for the
212 : * buffered object types INSTANCE and DELTA.
213 : *
214 : * @param count the number of incarnations to retain.
215 : * @version 1.0
216 : */
217 : CO_API void setAutoObsolete( const uint32_t count );
218 :
219 : /** @return get the number of retained incarnations. @version 1.0 */
220 : CO_API uint32_t getAutoObsolete() const;
221 :
222 : /**
223 : * Sync to a given version.
224 : *
225 : * Objects using the change type STATIC can not be synced.
226 : *
227 : * Syncing to VERSION_HEAD syncs all received versions, does not block and
228 : * always returns true. Syncing to VERSION_NEXT applies one new version,
229 : * potentially blocking. Syncing to VERSION_NONE does nothing.
230 : *
231 : * Slave objects can be synced to VERSION_HEAD, VERSION_NEXT or to any past
232 : * or future version generated by a commit on the master instance. Syncing
233 : * to a concrete version applies all pending versions up to this version and
234 : * potentially blocks if given a future version.
235 : *
236 : * Master objects can only be synced to VERSION_HEAD, VERSION_NEXT or to
237 : * any version generated by a commit on a slave instance. Syncing to a
238 : * concrete version applies only this version and potentially blocks.
239 : *
240 : * The different sync semantics for concrete versions originate from the
241 : * fact that master versions are continous and ordered, while slave
242 : * versions are random and unordered.
243 : *
244 : * This function is not thread safe, that is, calling sync()
245 : * simultaneously on the same object from multiple threads has to be
246 : * protected by the caller using a mutex.
247 : *
248 : * @param version the version to synchronize (see above).
249 : * @return the last version applied.
250 : * @version 1.0
251 : */
252 : CO_API virtual uint128_t sync( const uint128_t& version = VERSION_HEAD );
253 :
254 : /** @return the latest available (head) version. @version 1.0 */
255 : CO_API uint128_t getHeadVersion() const;
256 :
257 : /** @return the currently synchronized version. @version 1.0 */
258 : CO_API uint128_t getVersion() const;
259 :
260 : /**
261 : * Notification that a new head version was received by a slave object.
262 : *
263 : * The notification is called from the receiver thread, which is different
264 : * from the node main thread. The object cannot be sync()'ed from this
265 : * notification, as this might lead to deadlocks and synchronization issues
266 : * with the application thread changing the object. It should signal the
267 : * application, which then takes the appropriate action. Do not perform any
268 : * potentially blocking operations from this method.
269 : *
270 : * @param version The new head version.
271 : * @version 1.0
272 : */
273 : CO_API virtual void notifyNewHeadVersion( const uint128_t& version );
274 :
275 : /**
276 : * Notification that a new version was received by a master object.
277 : * The same constraints as for notifyNewHeadVersion() apply.
278 : * @version 1.0
279 : */
280 0 : virtual void notifyNewVersion() {}
281 : //@}
282 :
283 : /** @name Serialization methods for instantiation and versioning. */
284 : //@{
285 : /**
286 : * Serialize all instance information of this distributed object.
287 : *
288 : * @param os The output stream.
289 : * @version 1.0
290 : */
291 2 : virtual void getInstanceData( DataOStream& os LB_UNUSED ) {}
292 :
293 : /**
294 : * Deserialize the instance data.
295 : *
296 : * This method is called during object mapping to populate slave
297 : * instances with the master object's data.
298 : *
299 : * @param is the input stream.
300 : * @version 1.0
301 : */
302 3 : virtual void applyInstanceData( DataIStream& is LB_UNUSED ) {}
303 :
304 : /**
305 : * Serialize the modifications since the last call to commit().
306 : *
307 : * No new version will be created if no data is written to the output
308 : * stream.
309 : *
310 : * @param os the output stream.
311 : * @version 1.0
312 : */
313 2 : virtual void pack( DataOStream& os ) { getInstanceData( os ); }
314 :
315 : /**
316 : * Deserialize a change.
317 : *
318 : * @param is the input data stream.
319 : * @version 1.0
320 : */
321 2 : virtual void unpack( DataIStream& is ) { applyInstanceData( is ); }
322 : //@}
323 :
324 : /** @name Messaging API */
325 : //@{
326 : /**
327 : * Send a command with optional data to object instance(s) on another
328 : * node.
329 : *
330 : * The returned command can be used to pass additional data. The data
331 : * will be send after the command object is destroyed, aka when it is
332 : * running out of scope.
333 : *
334 : * @param node the node where to send the command to.
335 : * @param cmd the object command to execute.
336 : * @param instanceID the object instance(s) which should handle the command.
337 : * @return the command object to pass additional data to.
338 : * @version 1.0
339 : */
340 : CO_API ObjectOCommand send( NodePtr node, const uint32_t cmd,
341 : const uint32_t instanceID = CO_INSTANCE_ALL );
342 : //@}
343 :
344 : /** @name Notifications */
345 : /**
346 : * Notify that this object will be registered or mapped.
347 : *
348 : * The method is called from the thread initiating the registration or
349 : * mapping, before the operation is executed.
350 : * @version 1.0
351 : */
352 63 : virtual void notifyAttach() {}
353 :
354 : /**
355 : * Notify that this object has been registered or mapped.
356 : *
357 : * The method is called from the thread initiating the registration or
358 : * mapping, after the operation has been completed successfully.
359 : * @sa isMaster()
360 : * @version 1.0
361 : */
362 61 : virtual void notifyAttached() {}
363 :
364 : /**
365 : * Notify that this object will be deregistered or unmapped.
366 : *
367 : * The method is called from the thread initiating the deregistration or
368 : * unmapping, before the operation is executed.
369 : * @sa isMaster()
370 : * @version 1.0
371 : */
372 : CO_API virtual void notifyDetach();
373 :
374 : /**
375 : * Notify that this object has been deregistered or unmapped.
376 : *
377 : * The method is called from the thread initiating the deregistration or
378 : * unmapping, after the operation has been executed.
379 : * @version 1.0
380 : */
381 62 : virtual void notifyDetached() {}
382 : //@}
383 :
384 : /** @internal */
385 : //@{
386 : /** @internal @return the master object instance identifier. */
387 : uint32_t getMasterInstanceID() const;
388 :
389 : /** @internal @return the master object instance identifier. */
390 : CO_API NodePtr getMasterNode();
391 :
392 : /** @internal */
393 : CO_API void removeSlave( NodePtr node, const uint32_t instanceID );
394 : CO_API void removeSlaves( NodePtr node ); //!< @internal
395 : void setMasterNode( NodePtr node ); //!< @internal
396 : /** @internal */
397 : void addInstanceDatas( const ObjectDataIStreamDeque&, const uint128_t&);
398 :
399 : /**
400 : * @internal
401 : * Setup the change manager.
402 : *
403 : * @param type the type of the change manager.
404 : * @param master true if this object is the master.
405 : * @param localNode the node the object will be attached to.
406 : * @param masterInstanceID the instance identifier of the master object,
407 : * used when master == false.
408 : */
409 : void setupChangeManager( const Object::ChangeType type,
410 : const bool master, LocalNodePtr localNode,
411 : const uint32_t masterInstanceID );
412 :
413 : /** @internal Called when object is attached from the receiver thread. */
414 : CO_API virtual void attach( const uint128_t& id,
415 : const uint32_t instanceID );
416 : /**
417 : * @internal Called when the object is detached from the local node from the
418 : * receiver thread.
419 : */
420 : CO_API virtual void detach();
421 :
422 : /** @internal Transfer the attachment from the given object. */
423 : void transfer( Object* from );
424 :
425 : void applyMapData( const uint128_t& version ); //!< @internal
426 : void sendInstanceData( const Nodes& nodes ); //!< @internal
427 : //@}
428 :
429 : protected:
430 : /** Construct a new distributed object. @version 1.0 */
431 : CO_API Object();
432 :
433 : /** NOP assignment operator. @version 1.0 */
434 : Object& operator = ( const Object& ) { return *this; }
435 :
436 : /** Copy construct a new, unattached object. @version 1.0 */
437 : CO_API explicit Object( const Object& );
438 :
439 : private:
440 : detail::Object* const impl_;
441 : void _setChangeManager( ObjectCMPtr cm );
442 :
443 : ObjectCMPtr _getChangeManager();
444 : friend class ObjectStore;
445 :
446 126 : LB_TS_VAR( _thread );
447 : };
448 :
449 : /** Output information about the object to the given stream. @version 1.0 */
450 : CO_API std::ostream& operator << ( std::ostream&, const Object& );
451 :
452 : /** Output object change type in human-readably form. @version 1.0 */
453 : CO_API std::ostream& operator << ( std::ostream&, const Object::ChangeType& );
454 : }
455 :
456 : namespace lunchbox
457 : {
458 0 : template<> inline void byteswap( co::Object::ChangeType& value )
459 0 : { byteswap( reinterpret_cast< uint32_t& >( value )); }
460 : }
461 : #endif // CO_OBJECT_H
|