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