Guard against empty array read in step update task
							parent
							
								
									6f2c2b46d5
								
							
						
					
					
						commit
						8bf9ebc55c
					
				| 
						 | 
				
			
			@ -272,9 +272,8 @@ class ShmArray:
 | 
			
		|||
            return end
 | 
			
		||||
 | 
			
		||||
        except ValueError as err:
 | 
			
		||||
            # shoudl raise if diff detected
 | 
			
		||||
            # should raise if diff detected
 | 
			
		||||
            self.diff_err_fields(data)
 | 
			
		||||
 | 
			
		||||
            raise err
 | 
			
		||||
 | 
			
		||||
    def diff_err_fields(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,18 +61,19 @@ log = get_logger(__name__)
 | 
			
		|||
_quote_throttle_rate: int = 58  # Hz
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_fsp_chart(
 | 
			
		||||
    chart: ChartPlotWidget,
 | 
			
		||||
    shm: ShmArray,
 | 
			
		||||
    graphics_name: str,
 | 
			
		||||
    array_key: Optional[str],
 | 
			
		||||
def try_read(array: np.ndarray) -> Optional[np.ndarray]:
 | 
			
		||||
    '''
 | 
			
		||||
    Try to read the last row from a shared mem array or ``None``
 | 
			
		||||
    if the array read returns a zero-length array result.
 | 
			
		||||
 | 
			
		||||
) -> None:
 | 
			
		||||
 | 
			
		||||
    array = shm.array
 | 
			
		||||
    Can be used to check for backfilling race conditions where an array
 | 
			
		||||
    is currently being (re-)written by a writer actor but the reader is
 | 
			
		||||
    unaware and reads during the window where the first and last indexes
 | 
			
		||||
    are being updated.
 | 
			
		||||
 | 
			
		||||
    '''
 | 
			
		||||
    try:
 | 
			
		||||
        last_row = array[-1]
 | 
			
		||||
        return array[-1]
 | 
			
		||||
    except IndexError:
 | 
			
		||||
        # XXX: race condition with backfilling shm.
 | 
			
		||||
        #
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +86,23 @@ def update_fsp_chart(
 | 
			
		|||
        # signal that a prepend is taking place and this consumer can
 | 
			
		||||
        # respond (eg. redrawing graphics) accordingly.
 | 
			
		||||
        log.warning(f'Read-race on shm array: {graphics_name}@{shm.token}')
 | 
			
		||||
 | 
			
		||||
    # the array read was emtpy
 | 
			
		||||
    return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_fsp_chart(
 | 
			
		||||
    chart: ChartPlotWidget,
 | 
			
		||||
    shm: ShmArray,
 | 
			
		||||
    graphics_name: str,
 | 
			
		||||
    array_key: Optional[str],
 | 
			
		||||
 | 
			
		||||
) -> None:
 | 
			
		||||
 | 
			
		||||
    array = shm.array
 | 
			
		||||
    last_row = try_read(array)
 | 
			
		||||
    # guard against unreadable case
 | 
			
		||||
    if not last_row:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    # update graphics
 | 
			
		||||
| 
						 | 
				
			
			@ -850,13 +868,17 @@ async def check_for_new_bars(
 | 
			
		|||
            # current bar) and then either write the current bar manually
 | 
			
		||||
            # or place a cursor for visual cue of the current time step.
 | 
			
		||||
 | 
			
		||||
            array = ohlcv.array
 | 
			
		||||
            # avoid unreadable race case on backfills
 | 
			
		||||
            while not try_read(array):
 | 
			
		||||
                await trio.sleep(0.01)
 | 
			
		||||
 | 
			
		||||
            # XXX: this puts a flat bar on the current time step
 | 
			
		||||
            # TODO: if we eventually have an x-axis time-step "cursor"
 | 
			
		||||
            # we can get rid of this since it is extra overhead.
 | 
			
		||||
 | 
			
		||||
            price_chart.update_ohlc_from_array(
 | 
			
		||||
                price_chart.name,
 | 
			
		||||
                ohlcv.array,
 | 
			
		||||
                array,
 | 
			
		||||
                just_history=False,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -870,6 +892,9 @@ async def check_for_new_bars(
 | 
			
		|||
 | 
			
		||||
            # each subplot
 | 
			
		||||
            for name, chart in linkedsplits.subplots.items():
 | 
			
		||||
 | 
			
		||||
                # TODO: do we need the same unreadable guard as for the
 | 
			
		||||
                # price chart (above) here?
 | 
			
		||||
                chart.update_curve_from_array(
 | 
			
		||||
                    chart.name,
 | 
			
		||||
                    chart._shm.array,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue