1 //**************************************************************************************************
2 // OSSIM -- Open Source Software Image Map
3 //
4 // LICENSE: See top level LICENSE.txt file.
5 //
6 // AUTHOR: Oscar Kramer
7 //
8 //! Intended mainly to provide a mechanism for mutex-locking access to a shared resource during
9 //! a getTile operation on an ossimImageHandler. This is needed for multi-threaded implementation.
10 //
11 //**************************************************************************************************
12 // $Id$
13 #include <ossim/parallel/ossimImageHandlerMtAdaptor.h>
14 #include <ossim/imaging/ossimImageHandlerRegistry.h>
15 // #include <ossim/parallel/ossimMtDebug.h>
16 #include <ossim/base/ossimCommon.h>
17 #include <ossim/base/ossimTimer.h>
18 #include <ossim/base/ossimTrace.h>
19 #include <cstdio>
20 #include <ctime>
21 // #include <sys/time.h>
22
23 using namespace std;
24
25 RTTI_DEF1(ossimImageHandlerMtAdaptor, "ossimImageHandlerMtAdaptor", ossimImageHandler);
26
27 const char* ossimImageHandlerMtAdaptor::ADAPTEE_ID_KW = "adaptee_id";
28 static ossimTrace traceDebug = ossimTrace("ossimImageHandlerMtAdaptor:debug");
29
30 //**************************************************************************************************
31 // Constructor
ossimImageChainMtAdaptor()32 //**************************************************************************************************
33 ossimImageHandlerMtAdaptor::ossimImageHandlerMtAdaptor(ossimImageHandler* adaptee, bool use_cache, ossim_uint32 cache_tile_size)
34 : d_getTileT (0),
35 d_cacheTileSize(1024),
36 m_adaptedHandler (0),
37 m_cache (0),
38 d_useCache (false),
39 d_useFauxTile (false)
40 {
41 //###### DEBUG ############
42 // ossimMtDebug* mt_debug = ossimMtDebug::instance();
43 //d_useCache = mt_debug->handlerCacheEnabled;
44 //d_useFauxTile = mt_debug->handlerUseFauxTile;
45 //###### END DEBUG ############
46
47 setUseCache(use_cache);
48 setCacheTileSize(cache_tile_size);
49 setAdaptee(adaptee);
50 }
51
52 //**************************************************************************************************
53 // Destructor
54 //**************************************************************************************************
55 ossimImageHandlerMtAdaptor::~ossimImageHandlerMtAdaptor()
56 {
57 m_adaptedHandler = 0;
58 m_cache = 0;
59 d_fauxTile = 0;
60 }
61
62 //**************************************************************************************************
63 //! Sets the handler being adapted.
64 //**************************************************************************************************
65 void ossimImageHandlerMtAdaptor::setAdaptee(ossimImageHandler* handler)
66 {
67 m_adaptedHandler = handler;
68 if (handler == NULL)
69 return;
70
71 // Fetch the adaptee's output list and make it our own:
72 ConnectableObjectList output_list = handler->getOutputList();
~ossimImageChainMtAdaptor()73
74 if (d_useCache)
75 {
76 // Create the cache and connect this adaptor as its output:
77 m_cache = new ossimCacheTileSource;
78 m_cache->setTileSize(ossimIpt(d_cacheTileSize, d_cacheTileSize));
79 m_cache->connectMyOutputTo(this, true, false);
80 m_cache->changeOwner(this);
81 //m_cache->connectMyOutputTo(this, true, false);
82 handler->disconnectMyOutputs(output_list, true, false);
83 handler->connectMyOutputTo(m_cache.get(), true, true);
84 }
85 else
86 {
87 handler->disconnectMyOutputs(output_list, true, false);
88 handler->connectMyOutputTo(this, true, false);
89 }
90
91 // Finally connect the adaptee's outputs to this and fire connection events:
setNumberOfThreads(ossim_uint32 num_threads)92 connectMyOutputTo(output_list, true, true);
93 handler->changeOwner(this);
94
95 if (d_useFauxTile)
96 {
97 d_fauxTile = (ossimImageData*) handler->getTile(ossimIpt(0,0), 0)->dup();
98 //d_fauxTile = new ossimImageData(this,
99 // handler->getOutputScalarType(),
100 // handler->getNumberOfOutputBands(),
101 // handler->getTileWidth(),
102 // handler->getTileHeight());
103 //d_fauxTile->fill(128.0);
104 }
105 }
106
107 //**************************************************************************************************
108 //! Only an ossimImageHandler is allowed as input here.
109 //**************************************************************************************************
110 bool ossimImageHandlerMtAdaptor::canConnectMyInputTo(ossim_int32 inputIndex,
111 const ossimConnectableObject* obj) const
112 {
113 const ossimImageHandler* h = dynamic_cast<const ossimImageHandler*>(obj);
114 if ((inputIndex == 0) && (h != NULL))
setUseSharedHandlers(bool use_shared_handlers)115 return true;
116 return false;
117 }
118
119
setCacheTileSize(ossim_uint32 cache_tile_size)120 //**************************************************************************************************
121 //! Intercepts the getTile call intended for the adaptee and sets a mutex lock around the
122 //! adaptee's getTile call.
123 //**************************************************************************************************
124 ossimRefPtr<ossimImageData>
125 ossimImageHandlerMtAdaptor::getTile(const ossimIpt& origin, ossim_uint32 rLevel)
126 {
127 if (!m_adaptedHandler.valid())
128 return NULL;
129
130 // Establish tile rect to call overloaded getTile(tile_rect):
131 ossim_uint32 h = m_adaptedHandler->getTileHeight();
132 ossim_uint32 w = m_adaptedHandler->getTileWidth();
133 ossimIpt lr (origin.x + w - 1, origin.y + h - 1);
134 ossimIrect tile_rect (origin, lr);
135
136 // Need to unlock to prevent freezing in the called getTile():
137 return getTile(tile_rect, rLevel);
138 }
139
140 //**************************************************************************************************
141 //! Intercepts the getTile call intended for the adaptee and sets a mutex lock around the
142 //! adaptee's getTile call.
143 //**************************************************************************************************
144 ossimRefPtr<ossimImageData>
145 ossimImageHandlerMtAdaptor::getTile(const ossimIrect& tile_rect, ossim_uint32 rLevel)
146 {
147 if (traceDebug())
148 {
149 std::cout << "TILE: " << tile_rect << std::endl;
150 }
151
152 if (d_useFauxTile)
153 {
154 ossimRefPtr<ossimImageData> ftile = new ossimImageData(*(d_fauxTile.get()));
155 ftile->setOrigin(tile_rect.ul());
156 return ftile;
157 }
158
159 if (!m_adaptedHandler.valid())
160 return NULL;
161
162 // The sole purpose of the adapter is this mutex lock around the actual handler getTile:
163 //std::lock_guard<std::mutex> lock(m_mutex);
replicate()164
165 ossimRefPtr<ossimImageData> tile = new ossimImageData();
166 ossimRefPtr<ossimImageData> temp_tile = 0;
167 double dt = ossimTimer::instance()->time_s();
168
169 //writeTime();
170 if (traceDebug())
171 {
172 std::cout << "WAIT LOCK: " << tile_rect << std::endl;
173 }
174 std::lock_guard<std::mutex> lock(m_mutex);
175
176 if (traceDebug())
177 {
178 std::cout << "START LOCK: " << tile_rect << std::endl;
179 }
180
181 if (d_useCache)
182 temp_tile = m_cache->getTile(tile_rect, rLevel);
183 else
184 temp_tile = m_adaptedHandler->getTile(tile_rect, rLevel);
185 d_getTileT += ossimTimer::instance()->time_s() - dt;
186
187 // We make our own instance of a tile and copy the adaptee's returned tile to it. This avoids
188 // the product tile from changing while being processed up the chain. The adaptee's tile can
189 // change as soon as the mutex lock is released:
190
191 if (temp_tile.valid())
192 *tile = *(temp_tile.get());
193 else
194 tile = NULL;
195
196 //writeTime();
197 if (traceDebug())
198 {
199 std::cout << "END LOCK: " << tile_rect << std::endl;
200 }
201 if (traceDebug())
202 {
203 std::cout << "END TILE: " << tile_rect << std::endl;
204 }
205
206 return tile;
207 }
208
209 //**************************************************************************************************
210 //! Intercepts the getTile call intended for the adaptee and sets a mutex lock around the
211 //! adaptee's getTile call.
212 //**************************************************************************************************
213 bool ossimImageHandlerMtAdaptor::getTile(ossimImageData* tile, ossim_uint32 rLevel)
214 {
215 if ((!m_adaptedHandler.valid()) || (tile == NULL))
216 return false;
217
218 // The sole purpose of the adapter is this mutex lock around the actual handler getTile:
219 std::lock_guard<std::mutex> lock(m_mutex);
220
221 // This is effectively a copy of ossimImageSource::getTile(ossimImageData*). It is reimplemented
222 // here to save two additional function calls:
223 tile->ref();
224 bool status = true;
225 ossimIrect tile_rect = tile->getImageRectangle();
226
227 ossimRefPtr<ossimImageData> temp_tile = 0;
228 if (d_useCache)
229 temp_tile = m_cache->getTile(tile_rect, rLevel);
230 else
231 temp_tile = m_adaptedHandler->getTile(tile_rect, rLevel);
deleteReplicas()232
233 if (temp_tile.valid())
234 *tile = *(temp_tile.get());
235 else
236 status = false;
237 tile->unref();
238
239 return status;
240 }
241
242 //**************************************************************************************************
243 //! Method to save the state of an object to a keyword list.
244 //! Return true if ok or false on error.
245 //**************************************************************************************************
saveState(ossimKeywordlist & kwl,const char * prefix) const246 bool ossimImageHandlerMtAdaptor::saveState(ossimKeywordlist& kwl, const char* prefix)const
247 {
248 if (!m_adaptedHandler.valid())
249 return false;
250
251 // Skip the ossimImageHandler::saveState() since it is not necessary here:
252 ossimImageSource::saveState(kwl, prefix);
253
254 kwl.add(prefix, ADAPTEE_ID_KW, m_adaptedHandler->getId().getId());
255
256 return true;
257 }
258
259 //**************************************************************************************************
260 //! Method to the load (recreate) the state of an object from a keyword
261 //! list. Return true if ok or false on error.
262 //**************************************************************************************************
263 bool ossimImageHandlerMtAdaptor::loadState(const ossimKeywordlist& kwl, const char* prefix)
264 {
265 m_adaptedHandler = 0;
loadState(const ossimKeywordlist & kwl,const char * prefix)266
267 // Skip the ossimImageHandler::loadState() since it is not necessary here:
268 if (!ossimImageSource::loadState(kwl, prefix))
269 return false;
270
271 // The adaptee's ID at least will be in the KWL:
272 ossimString value = kwl.find(prefix, ADAPTEE_ID_KW);
273 if (value.empty())
274 return false;
275
276 return true;
277 }
278
279 //**************************************************************************************************
280 // The following are virtuals in the base class. Implemented here as pass-through to adaptee
281 //**************************************************************************************************
282 ossim_uint32 ossimImageHandlerMtAdaptor::getNumberOfInputBands() const
283 {
284 if (m_adaptedHandler.valid())
285 return m_adaptedHandler->getNumberOfInputBands();
286 return 0;
287 }
288
289 bool ossimImageHandlerMtAdaptor::isOpen() const
290 {
291 if (m_adaptedHandler.valid())
292 return m_adaptedHandler->isOpen();
293 return false;
294 }
295
296 bool ossimImageHandlerMtAdaptor::open()
297 {
298 if (m_adaptedHandler.valid())
299 return m_adaptedHandler->open();
300 return false;
301 }
302
303 ossim_uint32 ossimImageHandlerMtAdaptor::getNumberOfLines(ossim_uint32 resLevel) const
304 {
305 if (m_adaptedHandler.valid())
306 return m_adaptedHandler->getNumberOfLines(resLevel);
307 return 0;
308 }
309
310 ossim_uint32 ossimImageHandlerMtAdaptor::getNumberOfSamples(ossim_uint32 resLevel) const
311 {
312 if (m_adaptedHandler.valid())
313 return m_adaptedHandler->getNumberOfSamples(resLevel);
314 return 0;
315 }
316
317 ossim_uint32 ossimImageHandlerMtAdaptor::getImageTileWidth() const
318 {
319 if (m_adaptedHandler.valid())
320 return m_adaptedHandler->getImageTileWidth();
321 return 0;
322 }
323
324 ossim_uint32 ossimImageHandlerMtAdaptor::getImageTileHeight() const
325 {
326 if (m_adaptedHandler.valid())
327 return m_adaptedHandler->getImageTileHeight();
328 return 0;
329 }
330
331 ossimString ossimImageHandlerMtAdaptor::getLongName() const
332 {
333 if (m_adaptedHandler.valid())
334 return m_adaptedHandler->getLongName();
335 return ossimString();
336 }
337
338 ossimString ossimImageHandlerMtAdaptor::getShortName() const
339 {
340 if (m_adaptedHandler.valid())
341 return m_adaptedHandler->getShortName();
342 return ossimString();
343 }
344
345 void ossimImageHandlerMtAdaptor::close()
346 {
347 removeListener((ossimConnectableObjectListener*)this);
348 this->disconnectAllOutputs();
349 m_cache = 0;
350 if (m_adaptedHandler.valid())
351 {
352 m_adaptedHandler->closeOverview();
353 m_adaptedHandler->close();
354 }
355 d_fauxTile = 0;
356 }
357
358 ossim_uint32 ossimImageHandlerMtAdaptor::getNumberOfOutputBands() const
359 {
360 if (m_adaptedHandler.valid())
361 return m_adaptedHandler->getNumberOfOutputBands();
362 return 0;
initialize()363 }
364
365 void ossimImageHandlerMtAdaptor::setUseCache(bool use_cache)
366 {
367 d_useCache = use_cache;
368 }
369
370 void ossimImageHandlerMtAdaptor::setCacheTileSize(ossim_uint32 cache_tile_size)
371 {
372 d_cacheTileSize = cache_tile_size;
getTile(const ossimIrect & tileRect,ossim_uint32 resLevel)373 }
374
375 ossim_uint32 ossimImageHandlerMtAdaptor::getNumberOfDecimationLevels() const
376 {
377 if (m_adaptedHandler.valid())
378 return m_adaptedHandler->getNumberOfDecimationLevels();
379 return 0;
380 }
381
382 void ossimImageHandlerMtAdaptor::writeTime() const
383 {
384 #if 0 /* not portable (drb) */
385 struct timeval tv;
386 struct timezone tz;
387 struct tm *tm;
388 gettimeofday(&tv, &tz);
389 tm=localtime(&tv.tv_sec);
connectSharedHandlers(ossim_uint32 chain_index)390 printf("%d:%02d:%02d.%ld ", tm->tm_hour, tm->tm_min,tm->tm_sec,tv.tv_usec);
391 #endif
392 // Sorry no usecs...
393 time_t rawTime = (time_t)ossim::getTime();
394 char buf[9];
395 strftime(buf, 9, "%H:%M:%S", gmtime(&rawTime));
396 cerr << buf << std::endl;
397 }
398
399 ossimScalarType ossimImageHandlerMtAdaptor::getOutputScalarType() const
400 {
401 if (m_adaptedHandler.valid())
402 return m_adaptedHandler->getOutputScalarType();
403 return OSSIM_SCALAR_UNKNOWN;
404 }
405
406 ossim_uint32 ossimImageHandlerMtAdaptor::getTileWidth() const
407 {
408 if (m_adaptedHandler.valid())
409 return m_adaptedHandler->getTileWidth();
410 return 0;
411 }
412
413 ossim_uint32 ossimImageHandlerMtAdaptor::getTileHeight() const
414 {
415 if (m_adaptedHandler.valid())
416 return m_adaptedHandler->getTileHeight();
417 return 0;
418 }
419
420 ossim_float64 ossimImageHandlerMtAdaptor::getMinPixelValue(ossim_uint32 band) const
421 {
422 if (m_adaptedHandler.valid())
423 return m_adaptedHandler->getMinPixelValue(band);
424 return 0.0;
425 }
426
427 ossim_float64 ossimImageHandlerMtAdaptor::getMaxPixelValue(ossim_uint32 band) const
428 {
429 if (m_adaptedHandler.valid())
imageChainList()430 return m_adaptedHandler->getMaxPixelValue(band);
431 return 0.0;
432 }
433
434 ossim_float64 ossimImageHandlerMtAdaptor::getNullPixelValue(ossim_uint32 band) const
435 {
436 if (m_adaptedHandler.valid())
437 return m_adaptedHandler->getNullPixelValue(band);
438 return 0.0;
439 }
440
441