OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimImageChainMtAdaptor.cpp
Go to the documentation of this file.
1 //*************************************************************************************************
2 // OSSIM
3 //
4 // License: LGPL -- See LICENSE.txt file in the top level directory for more details.
5 //
6 // Author: Oscar Kramer
7 //
8 // This class supports multi-threading of image chain getTile() requests and associated chain
9 // updating. It accepts an original ossimImageChain object which is then cloned (replicated).
10 // One replicant per thread is created (counting the original as replicant[0]), with all replicants
11 // sharing a common input handler (if directed) so that disk access is serialized and thread-safe.
12 //
13 //*************************************************************************************************
14 // $Id$
15 
22 #include <iterator>
23 
24 static const char* NUM_THREADS_KW = "ossimImageChainMtAdaptor.num_threads";
25 static const char* ORIGINAL_SOURCE_ID_KW = "ossimImageChainMtAdaptor.original_source_id";
26 
27 //*************************************************************************************************
28 // Constructor.
29 //*************************************************************************************************
31 : m_numThreads (0),
32  d_useSharedHandlers(false),
33  d_debugEnabled(false),
34  d_cacheTileSize(1024),
35  d_useCache(false)
36 {
37  //###### DEBUG ############
39  //d_useSharedHandlers = mt_debug->chainSharedHandlers;
41  //###### END DEBUG ############
42 }
43 
44 //*************************************************************************************************
45 // Constructor with original image chain provided. This source becomes the first clone in the list.
46 //*************************************************************************************************
48  ossim_uint32 num_threads_req, bool use_shared_handlers, bool use_cache, ossim_uint32 cache_tile_size)
49 : m_numThreads (0),
50  d_useSharedHandlers(false),
51  d_debugEnabled(false),
52  d_cacheTileSize(1024),
53  d_useCache(false)
54 {
55  //###### DEBUG ############
57  //d_useSharedHandlers = mt_debug->chainSharedHandlers;
59  //###### END DEBUG ############
60 
61  setNumberOfThreads(num_threads_req);
62  setUseSharedHandlers(use_shared_handlers);
63  setUseCache(use_cache);
64  setCacheTileSize(cache_tile_size);
65  setOriginalChain(original);
66 }
67 
68 //*************************************************************************************************
69 // Destructor.
70 //*************************************************************************************************
72 {
75  m_sharedHandlers[0]->disconnectAllOutputs();
76  m_clones.clear();
77  m_chainContainers.clear();
79  {
80  m_sharedHandlers[0]->close();
81  m_sharedHandlers[0] = 0;
82  m_sharedHandlers.clear();
83  }
84 }
85 
86 //*************************************************************************************************
89 //*************************************************************************************************
91 {
92  if (m_numThreads == num_threads)
93  return;
94 
95  // Determine number of cores/threads to set up:
96  if (num_threads > 0)
97  {
98  m_numThreads = num_threads;
99  }
100  else
101  {
102  // Look in ossim preferences if arg is provided above.
103  ossimString value = ossimPreferences::instance()->findPreference("ossim_threads");
104  if ( !value.empty() )
105  m_numThreads = value.toUInt32();
106  }
107 
108  // If there is a valid original chain, we can perform the replication:
109  if (!m_chainContainers.empty())
110  replicate();
111 }
112 
114 {
115  d_useSharedHandlers = use_shared_handlers;
116 }
117 
119 {
120  d_cacheTileSize = cache_tile_size;
121 }
122 
124 {
125  d_useCache = use_cache;
126 }
127 
128 //*************************************************************************************************
130 //*************************************************************************************************
132 {
133  if (original == NULL)
134  return;
135 
136  ossimImageSource* first_source = original->getFirstSource();
137  if (first_source == NULL)
138  return;
139 
140  // Assign the adaptee and put it in the first position of the clones list:
141  m_adaptedChain = original;
142  m_clones.clear();
143  m_clones.push_back(first_source);
144 
145  m_chainContainers.clear();
148 
149  // If we know the number of threads, we can begin replicating:
150  if (m_numThreads > 1)
151  replicate();
152 }
153 
154 //*************************************************************************************************
155 // Creates clones of the original and pushes them onto the clone list.
156 //
157 // Need to consider the possibility that the original chain is a combiner with multiple image
158 // handler inputs, and also the possibility that one handler is wired to multiple input chains
159 // before the combiner. In order to handle this, we will save the connection information for each
160 // input handler encountered so that we can later reproduce it using a shared handler (if requested)
161 //*************************************************************************************************
163 {
164  if ((m_clones.empty()) || (m_numThreads == 0))
165  return false;
166 
167  // Don't need to replicate if only one thread is being requested. This is not an error though:
168  if (m_numThreads == 1)
169  return true;
170 
171  // If the handlers are to be shared, need to isolate them from the original chain and replace
172  // them with a "hollow adaptor" (i.e., a handler adaptor without the adaptee set yet:
173  m_sharedHandlers.clear();
175  {
176  if (m_chainContainers.empty())
177  return false;
178 
179  // Collect all image handlers in original chain for possible sharing with all clones:
180  ossimTypeNameVisitor visitor (ossimString("ossimImageHandler"));
181  m_chainContainers[0]->accept(visitor);
182  ossimRefPtr<ossimImageHandler> handler = 0;
183  ossimRefPtr<ossimImageHandlerMtAdaptor> handler_adaptor = 0;
184  ossimRefPtr<ossimConnectableObject> output_connection = 0;
185  ossim_uint32 handler_idx = 0;
186 
187  // Loop over all image handlers found in the original chain. Each handler's connection info
188  // is stored and the handler is removed temporarily from the original chain.
189  while (1)
190  {
191  // Fetch a handler from the chain and wrap it with a handler adaptor:
192  handler = visitor.getObjectAs<ossimImageHandler>(handler_idx++);
193  if (!handler)
194  break; // Only exit point of while loop
195 
196  handler_adaptor = new ossimImageHandlerMtAdaptor(handler.get(), d_useCache, d_cacheTileSize);
197  m_sharedHandlers.push_back(handler_adaptor);
198 
199  // Change ownership:
200  m_chainContainers[0]->removeChild(handler.get());
201  handler->changeOwner(this);
202  }
203 
204  // If no handler was found, we can't continue.
205  if (m_sharedHandlers.empty())
206  return false;
207  }
208 
209  // Fetch the state of this and the original chain. This KWL will be used for creating replicas
210  // via the loadState. The saveState essentially bootstraps the replication task:
211  ossimKeywordlist kwl;
212  bool succeeded = saveState(kwl);
213 
214  // The original chain may have had the handlers temporarily removed for the saveState in support
215  // of shared handlers. If so, need to restore them now:
217  succeeded = connectSharedHandlers(0);
218 
219  // Finally, initialize THIS chain with the original chain's state. This call will also create
220  // the clones:
221  if (succeeded)
222  succeeded = loadState(kwl);
223 
224  return succeeded;
225 }
226 
227 //*************************************************************************************************
228 // Deletes instances of all replicas from the clone list and leaves only the original.
229 //*************************************************************************************************
231 {
232  if (m_clones.size() > 1)
233  {
234  std::vector< ossimRefPtr<ossimImageSource> >::iterator first_copy = m_clones.begin();
235  first_copy++;
236  m_clones.erase(first_copy, m_clones.end());
237  }
238  m_numThreads = 1;
239 }
240 
241 //*************************************************************************************************
242 // Saves the state of the original chain along with number of clones present.
243 //*************************************************************************************************
244 bool ossimImageChainMtAdaptor::saveState(ossimKeywordlist& kwl, const char* prefix) const
245 {
246  if (m_chainContainers.empty())
247  return false;
248 
249  kwl.add(prefix, NUM_THREADS_KW, m_numThreads);
250  kwl.add(prefix, ORIGINAL_SOURCE_ID_KW, m_clones[0]->getId().getId());
251 
252  bool rtn_state = m_chainContainers[0]->saveState(kwl, prefix);
253 
254  if (d_debugEnabled)
255  kwl.write("ossimImageChainMtAdaptor.kwl");
256 
257  return rtn_state;
258 }
259 
260 //*************************************************************************************************
261 // Fetches the state of the original chain and regenerates the clones. Special handling is required
262 // when the image handlers are to be shared among all clones.
263 //*************************************************************************************************
264 bool ossimImageChainMtAdaptor::loadState(const ossimKeywordlist& kwl, const char* prefix)
265 {
266  bool succeeded;
267 
268  // Reset this object:
269  deleteReplicas();
270 
271  // Fetch this object's data members before moving onto original chain:
272  ossimString value = kwl.find(prefix, NUM_THREADS_KW);
273  if (value.empty())
274  return false;
275  m_numThreads = value.toUInt32();
276  if (m_numThreads == 0)
277  return false;
278 
279  // The chain ID needs to be read from KWL:
280  ossimId orig_source_id (ossimId::INVALID_ID);
281  value = kwl.find(prefix, ORIGINAL_SOURCE_ID_KW);
282  if (value.empty())
283  return false;
284  orig_source_id.setId(value.toInt64());
285 
286  // This loadState may be called for the purpose of replicating the existing original, or it can
287  // be intended as an adapter to a yet-to-be-instantiated original chain. Check if we already
288  // have a valid original chain:
289  ossimConnectableObject* candidate = 0;
290  ossimImageSource* original_source = 0;
291  if (!m_adaptedChain.valid() || m_chainContainers.empty())
292  {
293  m_chainContainers.clear();
295  m_chainContainers[0]->loadState(kwl, prefix);
296 
297  // Need to instantiate a new original. This is a bootstrap for a full initialization of this
298  // object. We'll need to replicate the clones afterwards:
299  ossimIdVisitor visitor (orig_source_id);
300  m_chainContainers[0]->accept(visitor);
301  candidate = visitor.getObject();
302  original_source = dynamic_cast<ossimImageSource*>(candidate);
303  if (original_source == NULL)
304  return false;
305  m_clones.push_back(original_source); // original is always in first position of clones list
306 
307  // The original "chain" is morphed into a chain with a single child (original first source).
308  // This source is the one maintaining the connection to the rest of the sources in the real
309  // processing chain:
311  m_adaptedChain->add(original_source);
312 
313  // Now that we have an original chain, Recursive code to replicate clones:
314  succeeded = replicate();
315  if (!succeeded)
316  return false;
317  }
318 
319  // We may be done:
320  if (m_numThreads == 1)
321  return true;
322 
323  // In preparation for multi-threading jobs, loop to instantiate all clone chains. The container
324  // class is used to perform a deep copy of the original chain with all connections established.
325  // It would have been cleaner to just use the ossimImageChain::dup() but that method was not
326  // traversing the full chain, resulting in missing input sources:
327  succeeded = true;
328  for (ossim_uint32 i=1; (i<m_numThreads) && succeeded; ++i)
329  {
330  // Use original container's kwl to dup clone container, and pull out our chain of interest:
332  m_chainContainers[i]->loadState(kwl, prefix);
333 
334  // Special handling required if the handlers are being shared. In this case, the handler had
335  // been removed from the original chain, so connections need to be identified and made:
337  {
338  succeeded = connectSharedHandlers(i);
339  if (!succeeded)
340  return false;
341  }
342 
343  // Find the first (right-most) source in the chain and store it in the clone list. Need to
344  // Modify all IDs
345  ossimIdVisitor visitor (orig_source_id);
346  m_chainContainers[i]->accept(visitor);
347  candidate = visitor.getObject();
348  m_chainContainers[i]->makeUniqueIds();
349  ossimRefPtr<ossimImageSource> clone_source = dynamic_cast<ossimImageSource*>(candidate);
350  if (!clone_source)
351  return false;
352  m_clones.push_back(clone_source);
353  }
354 
355  return succeeded;
356 }
357 
358 //*************************************************************************************************
360 //*************************************************************************************************
362 {
363  for (size_t i=0; i<m_clones.size(); ++i)
364  m_clones[i]->initialize();
365 }
366 
367 //*************************************************************************************************
368 // Intercept this getTile because it should never be called directly. The tile request must go
369 // to the specific chain clone.
370 //*************************************************************************************************
372  ossim_uint32 resLevel)
373 {
374  ossimNotify(ossimNotifyLevel_WARN)<<"ossimImageChainMtAdaptor::getTile() -- This method "
375  "Should never be called directly. The tile request must go to the specific chain clone. "
376  "Returning a tile using the original chain's getTile (not threaded)..."<<endl;
377 
378  if (!m_adaptedChain.valid())
379  return ossimRefPtr<ossimImageData>(0);
380 
381  return m_adaptedChain->getTile(tileRect, resLevel);
382 }
383 
384 //*************************************************************************************************
385 // Manages reconnecting shared image handlers to an image chain after its creation.
386 // This is in support of shared image handlers. Returns TRUE if successful.
387 //*************************************************************************************************
389 {
390  if ((size_t)chain_index >= m_chainContainers.size())
391  return false;
392 
393  // Loop over each adapted handler in our shared handler list:
394  SharedHandlerList::iterator handler = m_sharedHandlers.begin();
395  while (handler != m_sharedHandlers.end())
396  {
397  // Fetch all objects connected to this adapted handler. The list will point to objects in
398  // m_chainContainers[0], the original chain. The new chain's objects share the same ID's as
399  // the original chain for the moment. So we can search for the output connection
400  // in the new chain using the ID of the corresponding object in the original chain:
401  ConnectableObjectList handler_connections = (*handler)->getOutputList();
402  ConnectableObjectList::iterator output_connection = handler_connections.begin();
403  // BUG HERE AFTER UPGRADING FROM 1.8.14 to 1.8.20 - only grabbing the first output connection
404  //while (output_connection != handler_connections.end())
405  //{
406  ossimId obj_id = (*output_connection)->getId();
407  ossimIdVisitor visitor (obj_id);
408  m_chainContainers[chain_index]->accept(visitor);
409 
410  // Get the pointer to the actual output object that needs to be connected to the shared
411  // handler:
412  ossimConnectableObject* output_obj = visitor.getObject();
413  if (output_obj == NULL)
414  return false; // Should never happen
415  output_obj->connectMyInputTo((*handler).get(), true, true);
416 
417  output_connection++;
418  //}
419  handler++;
420  }
421  return true;
422 }
423 
424 
425 //*************************************************************************************************
426 // Adapts base class method for accessing connectables in the original chain.
427 //*************************************************************************************************
429 {
430  // If there is no original chain defined, then just return our own blank list:
431  if (!m_adaptedChain.valid())
432  return theImageChainList;
433 
434  return m_adaptedChain->imageChainList();
435 }
436 
437 //*************************************************************************************************
438 // Adapts base class method for accessing connectables in the original chain.
439 //*************************************************************************************************
442 {
443  // If there is no original chain defined, then just return our own blank list:
444  if (!m_adaptedChain.valid())
445  return theImageChainList;
446 
447  return m_adaptedChain->imageChainList();
448 }
449 
450 //*************************************************************************************************
453 //*************************************************************************************************
455 {
456  ossimNotify(ossimNotifyLevel_WARN)<<"ossimImageChainMtAdaptor::processEvent() -- "
457  "NOT YET IMPLEMENTED"<<endl;
458 }
459 
460 //*************************************************************************************************
462 {
463  if (index < (ossim_uint32) m_clones.size())
464  return m_clones[index].get();
465 
466  return 0;
467 }
468 
469 //*************************************************************************************************
472 //*************************************************************************************************
474  bool makeInputConnection,
475  bool createEventFlag)
476 {
477  // Make output connection for each clone source:
478  std::vector< ossimRefPtr<ossimImageSource> >::iterator clone_source = m_clones.begin();
479  while (clone_source != m_clones.end())
480  {
481  (*clone_source)->connectMyOutputTo(outputObject, false, false);
482  clone_source++;
483  }
484 
485  // Now make master connection including making input connection on outputObject and firing event:
486  return ossimConnectableObject::connectMyOutputTo(outputObject,
487  makeInputConnection,
488  createEventFlag);
489 }
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=NULL) const
Adapts call to original chain so that adapter-specific keywords can be added to KWL.
ossimRefPtr< ossimImageChain > m_adaptedChain
This is the adaptee image chain.
virtual ossimRefPtr< ossimImageData > getTile(const ossimIrect &tileRect, ossim_uint32 resLevel=0)
Within the image chain will pass the head of the list.
Intended mainly to provide a mechanism for mutex-locking access to a shared resource during a getTile...
virtual ossimImageSource * getFirstSource()
Return the first source which is the one that first receives the getTile request. ...
Represents serializable keyword/value map.
std::vector< ossimRefPtr< ossimImageSource > > m_clones
List of replicated child chains&#39; first source (the one receiving the getTile call).
bool valid() const
Definition: ossimRefPtr.h:75
const char * find(const char *key) const
static const ossim_int64 INVALID_ID
Definition: ossimId.h:83
ossim_uint32 toUInt32() const
void setOriginalChain(ossimImageChain *original)
Alternate way of specifying the original chain being adapted for multi-threading. ...
void setNumberOfThreads(ossim_uint32 num_threads)
Alternate way of specifying number of threads to support.
ossim_int64 getId() const
Definition: ossimId.h:29
virtual bool write(const char *file, const char *comment=0) const
Methods to dump the ossimKeywordlist to a file on disk.
bool replicate()
Creates clones of original and pushes them onto the clone list. Returns TRUE if successful.
virtual ossim_int32 connectMyOutputTo(ossimConnectableObject *outputObject, bool makeInputConnection=true, bool createEventFlag=true)
Overrides base class implementation in order to make the connection to each clone.
void add(const char *prefix, const ossimKeywordlist &kwl, bool overwrite=true)
std::vector< ossimRefPtr< ossimConnectableObject > > ConnectableObjectList
virtual void changeOwner(ossimObject *owner)
Permits changing the object&#39;s owner.
const char * findPreference(const char *key) const
const ossimId & getId() const
Will allow us to get this object&#39;s id.
void setUseSharedHandlers(bool use_shared_handlers)
virtual ossimConnectableObject::ConnectableObjectList & imageChainList()
These access methods greatly facilitate the implementation of an image chain adaptor class...
virtual ossimConnectableObject::ConnectableObjectList & imageChainList()
These overriding access methods greatly facilitate the implementation of an image chain adapter class...
bool chainDebugEnabled
Definition: ossimMtDebug.h:33
unsigned int ossim_uint32
virtual void processEvent(ossimEvent &event)
Adapts the image chain event handler.
virtual bool add(ossimConnectableObject *source)
Will return true or false if an image source was added to the chain.
virtual ossim_int32 connectMyInputTo(ossimConnectableObject *inputObject, bool makeOutputConnection=true, bool createEventFlag=true)
Will try to connect this objects input to the passed in object.
void deleteReplicas()
Deletes instances of all replicas from the clone list and leaves only the original.
ossimConnectableObject * getObject()
static ossimPreferences * instance()
For debugging purposes. To be removed with final release:
Definition: ossimMtDebug.h:7
virtual void initialize()
Adapts call to original chain so that all clones are initialized.
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=NULL)
Adapts call to original chain so that clones are replicated after original&#39;s loadState.
virtual ossimRefPtr< ossimImageData > getTile(const ossimIrect &tileRect, ossim_uint32 resLevel=0)
Intercepts the getTile because it should never be called directly.
virtual ossim_int32 connectMyOutputTo(ossimConnectableObject *outputObject, bool makeInputConnection=true, bool createEventFlag=true)
Will try to connect this objects output to the passed in object.
void setId(ossim_int64 id)
Definition: ossimId.h:28
T * getObjectAs(ossim_uint32 idx=0)
Definition: ossimVisitor.h:64
This class defines an abstract Handler which all image handlers(loaders) should derive from...
bool connectSharedHandlers(ossim_uint32 index)
Manages reconnecting shared image handlers to a cloned image chain (identified by index) after its cr...
ossimConnectableObject::ConnectableObjectList theImageChainList
This will hold a sequence of image sources.
bool empty() const
Definition: ossimString.h:411
ossim_int64 toInt64() const
ossimImageSource * getClone(ossim_uint32 index)
Returns pointer to a specific clone image chain, or NULL if index exceeds the max available...
virtual bool fillContainer(ossimConnectableContainer &container)
Inserts this object and all of its children and inputs into the container provided.
void setCacheTileSize(ossim_uint32 cache_tile_size)
static ossimMtDebug * instance()
Definition: ossimMtDebug.h:24
std::vector< ossimRefPtr< ossimConnectableContainer > > m_chainContainers
The container objects must be kept alive in this vector while their child chains in m_clones are used...
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
int ossim_int32
virtual bool removeListener(ossimListener *listener)