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