Skip to content

Commit

Permalink
Merge pull request #1224 from girder/refactor-style-locking
Browse files Browse the repository at this point in the history
Simply style locking.
  • Loading branch information
manthey authored Jun 28, 2023
2 parents b389675 + b157930 commit 58731ec
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 64 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
### Improvements
- Harden reading some nc files ([#1218](../../pull/1218))
- Increase the cache used for opening directories in the pylibtiff source ([#1221](../../pull/1221))
- Refactor style locking to increase parallelism ([#1224](../../pull/1224))

### Changes
- Clean up some old slideatlas url references ([#1223](../../pull/1223))

### Bug Fixes
- Pass style to histogram endpoint as json ([#1220](../../pull/1220))
Expand Down
5 changes: 3 additions & 2 deletions large_image/cache_util/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,11 +204,12 @@ def __call__(cls, *args, **kwargs): # noqa - N805
subkwargs.pop('style')
subresult = cls(*args, **subkwargs)
result = subresult.__class__.__new__(subresult.__class__)
with subresult._styleLock:
with subresult._sourceLock:
result.__dict__ = subresult.__dict__.copy()
result._styleLock = threading.RLock()
result._sourceLock = threading.RLock()
result._setStyle(kwargs['style'])
result._classkey = key
result._unstyledInstance = subresult
# for pickling
result._initValues = (args, kwargs.copy())
with cacheLock:
Expand Down
93 changes: 32 additions & 61 deletions large_image/tilesource/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def __init__(self, encoding='JPEG', jpegQuality=95, jpegSubsampling=0,
self.levels = None
self.sizeX = None
self.sizeY = None
self._styleLock = threading.RLock()
self._sourceLock = threading.RLock()
self._dtype = None
self._bandCount = None

Expand Down Expand Up @@ -232,27 +232,23 @@ def style(self):

@property
def dtype(self):
if not self._dtype:
with self._styleLock:
if not hasattr(self, '_skipStyle'):
self._setSkipStyle(True)
try:
sample, format = self.getRegion(
region=dict(left=0, top=0, width=1, height=1),
format=TILE_FORMAT_NUMPY)
self._dtype = sample.dtype
self._bandCount = sample.shape[-1] if len(sample.shape) == 3 else 1
finally:
self._setSkipStyle(False)
else:
return None

with self._sourceLock:
if not self._dtype:
self._dtype = 'check'
sample, _ = getattr(self, '_unstyledInstance', self).getRegion(
region=dict(left=0, top=0, width=1, height=1),
format=TILE_FORMAT_NUMPY)
self._dtype = sample.dtype
self._bandCount = len(
getattr(getattr(self, '_unstyledInstance', self), '_bandInfo', []))
if not self._bandCount:
self._bandCount = sample.shape[-1] if len(sample.shape) == 3 else 1
return self._dtype

@property
def bandCount(self):
if not self._bandCount:
if not self.dtype:
if not self._dtype or str(self._dtype) == 'check':
return None
return self._bandCount

Expand Down Expand Up @@ -1123,21 +1119,17 @@ def _scanForMinMax(self, dtype, frame=None, analysisSize=1024, onlyMinMax=True,
:param onlyMinMax: if True, only find the min and max. If False, get
the entire histogram.
"""
self._setSkipStyle(True)
try:
self._bandRanges[frame] = self.histogram(
dtype=dtype,
onlyMinMax=onlyMinMax,
output={'maxWidth': min(self.sizeX, analysisSize),
'maxHeight': min(self.sizeY, analysisSize)},
resample=False,
frame=frame, **kwargs)
if self._bandRanges[frame]:
self.logger.info('Style range is %r' % {
k: v for k, v in self._bandRanges[frame].items() if k in {
'min', 'max', 'mean', 'stdev'}})
finally:
self._setSkipStyle(False)
self._bandRanges[frame] = getattr(self, '_unstyledInstance', self).histogram(
dtype=dtype,
onlyMinMax=onlyMinMax,
output={'maxWidth': min(self.sizeX, analysisSize),
'maxHeight': min(self.sizeY, analysisSize)},
resample=False,
frame=frame, **kwargs)
if self._bandRanges[frame]:
self.logger.info('Style range is %r' % {
k: v for k, v in self._bandRanges[frame].items() if k in {
'min', 'max', 'mean', 'stdev'}})

def _validateMinMaxValue(self, value, frame, dtype):
"""
Expand Down Expand Up @@ -1392,21 +1384,6 @@ def _applyICCProfile(self, sc, frame):
self.logger.exception('Failed to apply ICC profile')
return sc.iccimage

def _setSkipStyle(self, setSkip=False):
if not hasattr(self, '_classkey'):
self._classkey = self.getState()
if setSkip:
self._unlocked_classkey = self._classkey
if hasattr(self, 'cache_lock'):
with self.cache_lock:
self._classkeyLock = self._styleLock
self._skipStyle = True
# Divert the tile cache while querying unstyled tiles
self._classkey = self._unstyledClassKey()
else:
del self._skipStyle
self._classkey = self._unlocked_classkey

def _applyStyle(self, image, style, x, y, z, frame=None): # noqa
"""
Apply a style to a numpy image.
Expand Down Expand Up @@ -1450,14 +1427,11 @@ def _applyStyle(self, image, style, x, y, z, frame=None): # noqa
else:
frame = entry['frame'] if entry.get('frame') is not None else (
sc.mainFrame + entry['framedelta'])
self._setSkipStyle(True)
try:
image = self.getTile(x, y, z, frame=frame, numpyAllowed=True)
image = image[:sc.mainImage.shape[0],
:sc.mainImage.shape[1],
:sc.mainImage.shape[2]]
finally:
self._setSkipStyle(False)
image = getattr(self, '_unstyledInstance', self).getTile(
x, y, z, frame=frame, numpyAllowed=True)
image = image[:sc.mainImage.shape[0],
:sc.mainImage.shape[1],
:sc.mainImage.shape[2]]
if (isinstance(entry.get('band'), int) and
entry['band'] >= 1 and entry['band'] <= image.shape[2]):
sc.bandidx = entry['band'] - 1
Expand Down Expand Up @@ -1566,9 +1540,7 @@ def _outputTileNumpyStyle(self, tile, applyStyle, x, y, z, frame=None):
"""
tile, mode = _imageToNumpy(tile)
if applyStyle and (getattr(self, 'style', None) or hasattr(self, '_iccprofiles')):
with self._styleLock:
if not getattr(self, '_skipStyle', False):
tile = self._applyStyle(tile, getattr(self, 'style', None), x, y, z, frame)
tile = self._applyStyle(tile, getattr(self, 'style', None), x, y, z, frame)
if tile.shape[0] != self.tileHeight or tile.shape[1] != self.tileWidth:
extend = numpy.zeros(
(self.tileHeight, self.tileWidth, tile.shape[2]),
Expand Down Expand Up @@ -1611,7 +1583,7 @@ def _outputTile(self, tile, tileEncoding, x, y, z, pilImageAllowed=False,
not isEdge and (not applyStyle or not hasStyle)):
return tile

if self._dtype is None:
if self._dtype is None or str(self._dtype) == 'check':
if tileEncoding == TILE_FORMAT_NUMPY:
self._dtype = tile.dtype
self._bandCount = tile.shape[-1] if len(tile.shape) == 3 else 1
Expand Down Expand Up @@ -1854,8 +1826,7 @@ def _getFrame(self, frame=None, **kwargs):
:returns: an integer frame number.
"""
frame = int(frame or 0)
if (not getattr(self, '_skipStyle', None) and
hasattr(self, '_style') and 'bands' in self.style and
if (hasattr(self, '_style') and 'bands' in self.style and
len(self.style['bands']) and
all(entry.get('frame') is not None for entry in self.style['bands'])):
frame = int(self.style['bands'][0]['frame'])
Expand Down
3 changes: 2 additions & 1 deletion sources/rasterio/large_image_source_rasterio/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,8 @@ def getBandInformation(self, statistics=True, dataset=None, **kwargs):
infoSet[i] = {k: v for k, v in info.items() if v not in (None, '')}

# set the value to cache if needed
cache is False or getattr(self, '_bandInfo', infoSet)
if cache:
self._bandInfo = infoSet

return infoSet

Expand Down

0 comments on commit 58731ec

Please sign in to comment.