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