Isis 3 Programmer Reference
CubeDataThread.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include "IsisDebug.h"
8 #include "CubeDataThread.h"
9 
10 #include <QApplication>
11 #include <QEventLoop>
12 #include <QMap>
13 #include <QMutex>
14 #include <QPair>
15 #include <QReadWriteLock>
16 #include <QString>
17 
18 #include <iomanip>
19 #include <iostream>
20 
21 #include "Brick.h"
22 #include "Cube.h"
23 #include "FileName.h"
24 #include "IException.h"
25 #include "IString.h"
26 #include "UniversalGroundMap.h"
27 
28 namespace Isis {
36  p_managedCubes = NULL;
37  p_managedData = NULL;
38  p_threadSafeMutex = NULL;
39  p_managedDataSources = NULL;
40 
43  p_threadSafeMutex = new QMutex();
45 
48  p_currentId = 1; // start with ID == 1
49  p_stopping = false;
50 
51  // Start this thread's event loop and make it so the slots are contained
52  // within this thread (this class automatically runs in its own thread)
53  start();
54  moveToThread(this);
55 
56  }
57 
58 
67  // Shutdown the event loop(s)
68  QThread::exit(0);
69 
70  p_stopping = true;
71 
72  while (!isFinished()) {
73  QThread::yieldCurrentThread();
74  }
75 
76  // Destroy the bricks still in memory
77  if (p_managedData) {
78  for (int i = p_managedData->size() - 1; i >= 0; i--) {
79  delete (*p_managedData)[i].first;
80  delete (*p_managedData)[i].second;
81  p_managedData->removeAt(i);
82  }
83 
84  delete p_managedData;
85  p_managedData = NULL;
86  }
87 
88  // Destroy the cubes still in memory
89  if (p_managedCubes) {
90  for (int i = p_managedCubes->size() - 1; i >= 0; i--) {
91  if ((p_managedCubes->end() - 1)->first) // only delete if we own it!
92  delete (p_managedCubes->end() - 1).value().second;
93  p_managedCubes->erase(p_managedCubes->end() - 1);
94  }
95  }
96 
97  // Destroy the mutex that controls access to the cubes
98  if (p_threadSafeMutex) {
99  delete p_threadSafeMutex;
100  p_threadSafeMutex = NULL;
101  }
102 
103  // Destroy the data sources vector
104  if (p_managedDataSources) {
105  delete p_managedDataSources;
106  p_managedDataSources = NULL;
107  }
108  }
109 
125  int CubeDataThread::AddCube(const FileName &fileName,
126  bool mustOpenReadWrite) {
127  Cube *newCube = new Cube();
128 
129  try {
130  newCube->open(fileName.expanded(), "rw");
131  }
132  catch (IException &e) {
133  if (!mustOpenReadWrite) {
134  newCube->open(fileName.expanded(), "r");
135  }
136  else {
137  throw;
138  }
139  }
140 
141  p_threadSafeMutex->lock();
142 
143  int newId = p_currentId;
144  p_currentId++;
145 
146  QPair< bool, Cube * > newEntry;
147  newEntry.first = true; // we own this!
148  newEntry.second = newCube;
149  p_managedCubes->insert(newId, newEntry);
150 
151  p_threadSafeMutex->unlock();
152 
153  return newId;
154  }
155 
169  p_threadSafeMutex->lock();
170 
171  int newId = p_currentId;
172  p_currentId++;
173 
174  QPair< bool, Cube * > newEntry;
175  newEntry.first = false; // we don't own this!
176  newEntry.second = cube;
177  p_managedCubes->insert(newId, newEntry);
178 
179  p_threadSafeMutex->unlock();
180 
181  return newId;
182  }
183 
184 
192  void CubeDataThread::RemoveCube(int cubeId) {
193  p_threadSafeMutex->lock();
194 
195  QMap< int, QPair< bool, Cube * > >::iterator i;
196  i = p_managedCubes->find(cubeId);
197 
198  if (i == p_managedCubes->end()) {
199  p_threadSafeMutex->unlock();
200  QString msg = "CubeDataThread::RemoveCube failed because cube ID [";
201  msg += QString::number(cubeId);
202  msg += "] not found";
203  throw IException(IException::Programmer, msg, _FILEINFO_);
204  }
205 
206  if (p_managedDataSources->contains(cubeId)) {
207  p_threadSafeMutex->unlock();
208  QString msg = "CubeDataThread::RemoveCube failed cube ID [";
209  msg += QString::number(cubeId);
210  msg += "] has requested Bricks";
211  throw IException(IException::Programmer, msg, _FILEINFO_);
212  }
213 
214  // if we have ownership of the cube then me must delete it
215  if (i.value().first)
216  delete i.value().second;
217  i.value().second = NULL;
218 
219  p_managedCubes->remove(i.key());
220 
221  p_threadSafeMutex->unlock();
222 
223  }
224 
225 
232  }
233 
240  }
241 
258  void CubeDataThread::GetCubeData(int cubeId, int ss, int sl, int es, int el,
259  int band, void *caller, bool sharedLock) {
260 
261  Brick *requestedBrick = NULL;
262 
263  p_threadSafeMutex->lock();
264  requestedBrick = new Brick(*p_managedCubes->value(cubeId).second, es
265  - ss + 1, el - sl + 1, 1);
266  requestedBrick->SetBasePosition(ss, sl, band);
267  p_threadSafeMutex->unlock();
268 
269  // See if we already have this brick
270  int instance = 0;
271  int exactIndex = -1;
272  bool exactMatch = false;
273  int index = OverlapIndex(requestedBrick, cubeId, instance, exactMatch);
274 
275  // while overlaps are found
276  while (index != -1) {
277  if (sharedLock) {
278  // make sure we can get read locks on exact overlaps
279  // We need to try to get the lock to verify partial overlaps not
280  // write locked and only keep read locks on exact matches.
281  AcquireLock((*p_managedData)[index].first, true);
282  if (!exactMatch) {
283  (*p_managedData)[index].first->unlock();
284  }
285  }
286  else {
287  AcquireLock((*p_managedData)[index].first, false);
288  // we arent actually writing to this, but now we know we can delete it
289  (*p_managedData)[index].first->unlock();
290 
291  exactMatch = false;
292 
293  // destroy things that overlap but arent the same when asking to write
294  if (FreeBrick(index)) {
295  instance--;
296  }
297  }
298 
299  if (exactMatch) {
300  exactIndex = index;
301  }
302 
303  instance++;
304  index = OverlapIndex(requestedBrick, cubeId, instance, exactMatch);
305  }
306 
307  if(p_stopping) return;
308 
309  if (exactIndex == -1) {
310  p_threadSafeMutex->lock();
311 
312  p_managedCubes->value(cubeId).second->read(*requestedBrick);
313 
314  QPair< QReadWriteLock *, Brick * > managedDataEntry;
315 
316  managedDataEntry.first = new QReadWriteLock();
317 
318  AcquireLock(managedDataEntry.first, sharedLock);
319 
320  managedDataEntry.second = requestedBrick;
321 
322  p_managedData->append(managedDataEntry);
323  p_managedDataSources->append(cubeId);
324 
325  exactIndex = p_managedData->size() - 1;
326 
327  p_threadSafeMutex->unlock();
328  }
329 
330  if (el - sl + 1 != (*p_managedData)[exactIndex].second->LineDimension())
331  {
332  //abort();
333  }
334 
335  if (sharedLock) {
336  emit ReadReady(caller, cubeId, (*p_managedData)[exactIndex].second);
337  }
338  else {
339  emit ReadWriteReady(caller, cubeId, (*p_managedData)[exactIndex].second);
340  }
341  }
342 
343 
351  int CubeDataThread::FindCubeId(const Cube * cubeToFind) const {
352  QMapIterator< int, QPair< bool, Cube * > > i(*p_managedCubes);
353  while (i.hasNext()) {
354  i.next();
355  if (i.value().second == cubeToFind)
356  return i.key();
357  }
359  "Cube does not exist in this CubeDataThread", _FILEINFO_);
360  }
361 
362 
371  void CubeDataThread::AcquireLock(QReadWriteLock *lockObject, bool readLock) {
372  // This method can be called recursively (sorta). In the "recursive"
373  // cases p_currentLockWaiting is > 0. This guarantees that locks are
374  // aquired in the reverse order as they are requested, which isn't
375  // particularly helpful, but it is consistent :)
376 
377  if (readLock) {
378  while (!lockObject->tryLockForRead()) {
379  // while we can't get the lock, allow other processing to happen for
380  // brief periods of time
381  //
382  // Give time for things to happen in other threads
383  QThread::yieldCurrentThread();
384 
386  qApp->sendPostedEvents(this, 0);
388 
389  if(p_stopping) return;
390  }
391  }
392  else {
393  while (!lockObject->tryLockForWrite()) {
394  // while we can't get the lock, allow other processing to happen for
395  // brief periods of time
396  //
397  // Give time for things to happen in other threads
398  QThread::yieldCurrentThread();
399 
401  qApp->sendPostedEvents(this, 0);
403 
404  if(p_stopping) return;
405  }
406  }
407  }
408 
427  void CubeDataThread::ReadCube(int cubeId, int startSample, int startLine,
428  int endSample, int endLine, int band,
429  void *caller) {
430 
431  if(!p_managedCubes->contains(cubeId)) {
432  IString msg = "cube ID [";
433  msg += IString(cubeId);
434  msg += "] is not a valid cube ID";
435  throw IException(IException::Programmer, msg, _FILEINFO_);
436  }
437 
438  GetCubeData(cubeId, startSample, startLine, endSample, endLine, band,
439  caller, true);
440  }
441 
462  void CubeDataThread::ReadWriteCube(int cubeId, int startSample,
463  int startLine, int endSample, int endLine,
464  int band, void *caller) {
465  if(!p_managedCubes->contains(cubeId)) {
466  IString msg = "cube ID [";
467  msg += IString(cubeId);
468  msg += "] is not a valid cube ID";
469  throw IException(IException::Programmer, msg, _FILEINFO_);
470  }
471 
472  GetCubeData(cubeId, startSample, startLine, endSample, endLine, band,
473  caller, false);
474  }
475 
489  int CubeDataThread::OverlapIndex(const Brick *overlapping, int cubeId,
490  int instanceNum, bool &exact) {
491  exact = false;
492 
493  // Start with extracting the range of the input (search) brick
494  int startSample = overlapping->Sample(0);
495  int endSample = overlapping->Sample(overlapping->size() - 1);
496  int startLine = overlapping->Line(0);
497  int endLine = overlapping->Line(overlapping->size() - 1);
498  int startBand = overlapping->Band(0);
499  int endBand = overlapping->Band(overlapping->size() - 1);
500 
501  // Now let's search for overlaps
502  ASSERT(p_managedData->size() == p_managedDataSources->size());
503  for (int knownBrick = 0; knownBrick < p_managedData->size(); knownBrick++) {
504  int sourceCube = (*p_managedDataSources)[knownBrick];
505 
506  // Ignore other cubes; they can't overlap
507  if (sourceCube != cubeId)
508  continue;
509 
510  QPair< QReadWriteLock *, Brick * > &managedBrick =
511  (*p_managedData)[knownBrick];
512 
513  Brick &brick = *managedBrick.second;
514 
515  // Get the range of this brick we've found in memory to see if any overlap
516  // exists
517  int compareSampStart = brick.Sample(0);
518  int compareSampEnd = brick.Sample(brick.size() - 1);
519  int compareLineStart = brick.Line(0);
520  int compareLineEnd = brick.Line(brick.size() - 1);
521  int compareBandStart = brick.Band(0);
522  int compareBandEnd = brick.Band(brick.size() - 1);
523 
524  bool overlap = false;
525 
526  // sample start is inside our sample range
527  if (compareSampStart >= startSample && compareSampStart <= endSample) {
528  overlap = true;
529  }
530 
531  // sample end is inside our sample range
532  if (compareSampEnd >= startSample && compareSampEnd <= endSample) {
533  overlap = true;
534  }
535 
536  // line start is in our line range
537  if (compareLineStart >= startLine && compareLineStart <= endLine) {
538  overlap = true;
539  }
540 
541  // line end is in our line range
542  if (compareLineEnd >= startLine && compareLineEnd <= endLine) {
543  overlap = true;
544  }
545 
546  // band start is in our line range
547  if (compareBandStart >= startBand && compareBandStart <= endBand) {
548  overlap = true;
549  }
550 
551  // band end is in our line range
552  if (compareBandEnd >= startBand && compareBandEnd <= endBand) {
553  overlap = true;
554  }
555 
556  exact = false;
557  if (compareSampStart == startSample && compareSampEnd == endSample
558  && compareLineStart == startLine && compareLineEnd == endLine
559  && compareBandStart == startBand && compareBandEnd == endBand) {
560  exact = true;
561  }
562 
563  // If we have overlap, and we're at the requested instance of overlap,
564  // return it.
565  if (overlap) {
566  instanceNum--;
567 
568  if (instanceNum < 0) {
569  return knownBrick;
570  }
571  }
572  }
573 
574  // None found at this instance
575  return -1;
576  }
577 
587  void CubeDataThread::DoneWithData(int cubeId, const Isis::Brick *brickDone) {
588  ASSERT(brickDone != NULL);
589  int instance = 0;
590  bool exactMatch = false;
591  bool writeLock = false;
592 
593  int index = OverlapIndex(brickDone, cubeId, instance, exactMatch);
594  ASSERT(p_managedData->size() == p_managedDataSources->size());
595  while (index != -1) {
596  // If this isn't the data they're finished with, we don't care about it
597  if (!exactMatch) {
598  instance++;
599  index = OverlapIndex(brickDone, cubeId, instance, exactMatch);
600  continue;
601  }
602 
603  // Test if we had a write lock (tryLockForRead will fail). If we had a
604  // write lock make note of it.
605  if (!(*p_managedData)[index].first->tryLockForRead()) {
606  if (writeLock) {
607  IString msg = "Overlapping data had write locks";
608  throw IException(IException::Programmer, msg, _FILEINFO_);
609  }
610 
611  writeLock = true;
612  }
613  // A read lock was in place, undo the lock we just made
614  else {
615  if (writeLock) {
616  IString msg = "Overlapping data had write locks";
617  throw IException(IException::Programmer, msg, _FILEINFO_);
618  }
619 
620  // Unlock the lock we just made
621  (*p_managedData)[index].first->unlock();
622  }
623 
624  // If we had a write lock we need to write the data to the file and
625  // notify others of the change if we have listeners.
626  if (writeLock) {
627  p_threadSafeMutex->lock();
628  Brick cpy(*brickDone);
629  p_managedCubes->value(cubeId).second->write(cpy);
630  p_threadSafeMutex->unlock();
631 
632  // Unlock the existing lock
633  (*p_managedData)[index].first->unlock();
634 
635  // No listeners? Remove this entry
636  if (p_numChangeListeners == 0) {
637  if (FreeBrick(index)) {
638  // We've freed the one and only match, nobody wants to know about
639  // it, so we're done
640  break;
641  }
642  }
643  // We have listeners, lock the data the appropriate number of times and
644  // then emit the BrickChanged with a pointer
645  else {
646  // notify others of this change
647  for (int i = 0; i < p_numChangeListeners; i++) {
648  AcquireLock((*p_managedData)[index].first, true);
649  }
650  emit BrickChanged((*p_managedDataSources)[index],
651  (*p_managedData)[index].second);
652  }
653  }
654  // if we had a read lock and no longer have any locks, remove data from
655  // list
656  else {
657  // We had the one and only (hopefully!) exact match, let's free it if
658  // we can get a write lock and be done.
659  // Free original read lock
660  (*p_managedData)[index].first->unlock();
661 
662  if ((*p_managedData)[index].first->tryLockForWrite()) {
663  (*p_managedData)[index].first->unlock();
664  FreeBrick(index);
665  }
666 
667  break;
668  }
669 
670  instance++;
671  index = OverlapIndex(brickDone, cubeId, instance, exactMatch);
672  }
673 
674  ASSERT(p_managedData->size() == p_managedDataSources->size());
675  }
676 
684  bool CubeDataThread::FreeBrick(int brickIndex) {
685  ASSERT(p_managedData->size() == p_managedDataSources->size());
686 
687  // make sure brick is not still being used!
688  if (!(*p_managedData)[brickIndex].first->tryLockForWrite()) {
690  "CubeDataThread::FreeBrick called on a locked brick",
691  _FILEINFO_);
692  }
693  else {
694  (*p_managedData)[brickIndex].first->unlock();
695  }
696 
697  // make sure no one is looking through p_managedData in order to delete it
698  p_threadSafeMutex->lock();
699 
700  if (p_currentLocksWaiting == 0) {
701  delete (*p_managedData)[brickIndex].first;
702  delete (*p_managedData)[brickIndex].second;
703 
704  p_managedData->removeAt(brickIndex);
705  p_managedDataSources->removeAt(brickIndex);
706 
707  // Try to free any leftover bricks too
708  for (int i = 0; i < p_managedData->size(); i++) {
709  if ((*p_managedData)[i].first->tryLockForWrite()) {
710  delete (*p_managedData)[i].first;
711  delete (*p_managedData)[i].second;
712  p_managedData->removeAt(i);
713  p_managedDataSources->removeAt(i);
714  i--;
715  }
716  }
717 
718  p_threadSafeMutex->unlock();
719  return true;
720  }
721 
722  p_threadSafeMutex->unlock();
723 
724  // no actual free was done
725  return false;
726  }
727 
735  p_threadSafeMutex->lock();
736  int numBricksInMemory = p_managedData->size();
737  p_threadSafeMutex->unlock();
738 
739  return numBricksInMemory;
740  }
741 
751  if (!p_managedCubes->contains(cubeId)) {
753  "Invalid Cube ID [" + IString(cubeId) + "]",
754  _FILEINFO_);
755  }
756 
757  return new UniversalGroundMap(*(*p_managedCubes)[cubeId].second);
758  }
759 
767  const Cube *CubeDataThread::GetCube(int cubeId) const {
768  if (!p_managedCubes->contains(cubeId)) {
770  "Invalid Cube ID [" + IString(cubeId) + "]",
771  _FILEINFO_);
772  }
773 
774  return (*p_managedCubes)[cubeId].second;
775  }
776 }
Isis::CubeDataThread::p_currentLocksWaiting
unsigned int p_currentLocksWaiting
Number of locks being attempted that re-entered the event loop.
Definition: CubeDataThread.h:217
Isis::Brick::SetBasePosition
void SetBasePosition(const int start_sample, const int start_line, const int start_band)
This method is used to set the base position of the shape buffer.
Definition: Brick.h:120
Isis::CubeDataThread::~CubeDataThread
virtual ~CubeDataThread()
This class is a self-contained thread, so normally it would be bad to simply delete it.
Definition: CubeDataThread.cpp:66
Isis::CubeDataThread::FreeBrick
bool FreeBrick(int brickIndex)
This is used internally to delete bricks when possible.
Definition: CubeDataThread.cpp:684
Isis::UniversalGroundMap
Universal Ground Map.
Definition: UniversalGroundMap.h:69
Isis::CubeDataThread::p_stopping
bool p_stopping
This is set to help the shutdown process when deleted.
Definition: CubeDataThread.h:211
Isis::CubeDataThread::DoneWithData
void DoneWithData(int, const Isis::Brick *)
When done processing with a brick (reading or writing) this slot needs to be signalled to free locks ...
Definition: CubeDataThread.cpp:587
Isis::CubeDataThread::CubeDataThread
CubeDataThread()
This constructs a CubeDataThread().
Definition: CubeDataThread.cpp:35
QList
This is free and unencumbered software released into the public domain.
Definition: BoxcarCachingAlgorithm.h:13
Isis::CubeDataThread::ReadWriteCube
void ReadWriteCube(int cubeId, int startSample, int startLine, int endSample, int endLine, int band, void *caller)
This slot should be connected to and upon receiving a signal it will begin the necessary cube I/O to ...
Definition: CubeDataThread.cpp:462
Isis::FileName
File name manipulation and expansion.
Definition: FileName.h:100
Isis::CubeDataThread::p_managedCubes
QMap< int, QPair< bool, Cube * > > * p_managedCubes
This is a list of the opened cubes.
Definition: CubeDataThread.h:171
Isis::CubeDataThread::p_currentId
unsigned int p_currentId
This is the unique id counter for cubes.
Definition: CubeDataThread.h:208
Isis::CubeDataThread::OverlapIndex
int OverlapIndex(const Brick *initial, int cubeId, int instanceNum, bool &exact)
This is a searching method used to identify overlapping data already in memory.
Definition: CubeDataThread.cpp:489
Isis::CubeDataThread::AcquireLock
void AcquireLock(QReadWriteLock *lockObject, bool readLock)
This method is exclusively used to acquire locks.
Definition: CubeDataThread.cpp:371
Isis::Brick
Buffer for containing a three dimensional section of an image.
Definition: Brick.h:45
Isis::CubeDataThread::FindCubeId
int FindCubeId(const Cube *) const
Given a Cube pointer, return the cube ID associated with it.
Definition: CubeDataThread.cpp:351
Isis::CubeDataThread::AddChangeListener
void AddChangeListener()
You must call this method after connecting to the BrickChanged signal, otherwise you are not guarante...
Definition: CubeDataThread.cpp:230
Isis::CubeDataThread::BrickChanged
void BrickChanged(int cubeId, const Isis::Brick *data)
DO NOT CONNECT TO THIS SIGNAL WITHOUT CALLING AddChangeListener().
Isis::CubeDataThread::GetCube
const Cube * GetCube(int cubeId) const
This returns a constant pointer to a Cube at the given Cube ID.
Definition: CubeDataThread.cpp:767
Isis::CubeDataThread::AddCube
int AddCube(const FileName &fileName, bool mustOpenReadWrite=false)
This method is designed to be callable from any thread before data is requested, though no known side...
Definition: CubeDataThread.cpp:125
Isis::FileName::expanded
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:196
Isis::CubeDataThread::p_managedDataSources
QList< int > * p_managedDataSources
This is the associated cube ID with each brick.
Definition: CubeDataThread.h:202
Isis::CubeDataThread::ReadReady
void ReadReady(void *requester, int cubeId, const Isis::Brick *data)
This signal will be emitted when ReadCube has finished processing.
Isis::CubeDataThread::BricksInMemory
int BricksInMemory()
This is a helper method for both testing/debugging and general information that provides the current ...
Definition: CubeDataThread.cpp:734
Isis::CubeDataThread::GetUniversalGroundMap
UniversalGroundMap * GetUniversalGroundMap(int cubeId) const
This returns a new Universal Ground Map given a Cube ID.
Definition: CubeDataThread.cpp:750
Isis::CubeDataThread::p_threadSafeMutex
QMutex * p_threadSafeMutex
This locks the member variable p_managedCubes and p_managedData itself.
Definition: CubeDataThread.h:174
Isis::Cube
IO Handler for Isis Cubes.
Definition: Cube.h:167
Isis::CubeDataThread::ReadWriteReady
void ReadWriteReady(void *requester, int cubeId, Isis::Brick *data)
This signal will be emitted when ReadWriteCube has finished processing.
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::CubeDataThread::p_numChangeListeners
int p_numChangeListeners
This is the number of shaded locks to put on a brick when changes made.
Definition: CubeDataThread.h:205
Isis::Buffer::Band
int Band(const int index=0) const
Returns the band position associated with a shape buffer index.
Definition: Buffer.cpp:162
Isis::IException::Programmer
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition: IException.h:146
Isis::CubeDataThread::RemoveChangeListener
void RemoveChangeListener()
You must call this method after disconnecting from the BrickChanged signal, otherwise bricks cannot b...
Definition: CubeDataThread.cpp:238
Isis::CubeDataThread::RemoveCube
void RemoveCube(int cubeId)
Removes a cube from this lock manager.
Definition: CubeDataThread.cpp:192
QPair
This is free and unencumbered software released into the public domain.
Definition: CubeIoHandler.h:23
QMap
This is free and unencumbered software released into the public domain.
Definition: CubeIoHandler.h:22
Isis::Buffer::size
int size() const
Returns the total number of pixels in the shape buffer.
Definition: Buffer.h:97
Isis::CubeDataThread::ReadCube
void ReadCube(int cubeId, int startSample, int startLine, int endSample, int endLine, int band, void *caller)
This slot should be connected to and upon receiving a signal it will begin the necessary cube I/O to ...
Definition: CubeDataThread.cpp:427
Isis::CubeDataThread::GetCubeData
void GetCubeData(int cubeId, int ss, int sl, int es, int el, int band, void *caller, bool sharedLock)
This helper method reads in cube data and handles the locking of similar bricks appropriately.
Definition: CubeDataThread.cpp:258
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
Isis::Buffer::Sample
int Sample(const int index=0) const
Returns the sample position associated with a shape buffer index.
Definition: Buffer.cpp:127
Isis::Cube::open
void open(const QString &cfile, QString access="r")
This method will open an isis cube for reading or reading/writing.
Definition: Cube.cpp:627
Isis::Buffer::Line
int Line(const int index=0) const
Returns the line position associated with a shape buffer index.
Definition: Buffer.cpp:145
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16
Isis::CubeDataThread::p_managedData
QList< QPair< QReadWriteLock *, Brick * > > * p_managedData
This is a list of bricks in memory and their locks.
Definition: CubeDataThread.h:199