def _base_bar_handler(self, bar):
        """ non threaded bar handler (called by threaded _tick_handler) """
        # bar symbol
        symbol = bar['symbol'].values
        if len(symbol) == 0:
            return
        symbol = symbol[0]
        self_bars = self.bars.copy()  # work on copy

        is_tick_or_volume_bar = False
        handle_bar = True

        if self.resolution[-1] in ("S", "K", "V"):
            is_tick_or_volume_bar = True
            handle_bar = self._caller("_tick_handler")

        # drip is also ok
        handle_bar = handle_bar or self._caller("drip")

        if is_tick_or_volume_bar:
            # just add a bar (used by tick bar bandler)
            if self.threads == 0:
                self.bars = self._update_window(self.bars, bar,
                                                window=self.bar_window)
            else:
                self_bars = self._update_window(self_bars, bar,
                                                window=self.bar_window)
        else:
            # add the bar and resample to resolution
            if self.threads == 0:
                self.bars = self._update_window(self.bars, bar,
                                                window=self.bar_window,
                                                resolution=self.resolution)
            else:
                self_bars = self._update_window(self_bars, bar,
                                                window=self.bar_window,
                                                resolution=self.resolution)

        # assign new data to self.bars if threaded
        if self.threads > 0:
            self.bars = self._thread_safe_merge(symbol, self.bars, self_bars)

        # optimize pandas
        if len(self.bars) == 1:
            self.bars['symbol'] = self.bars['symbol'].astype('category')
            self.bars['symbol_group'] = self.bars['symbol_group'].astype('category')
            self.bars['asset_class'] = self.bars['asset_class'].astype('category')

        # new bar?
        hash_string = bar[:1]['symbol'].to_string().translate(
            str.maketrans({key: None for key in "\n -:+"}))
        this_bar_hash = abs(hash(hash_string)) % (10 ** 8)

        newbar = True
        if symbol in self.bar_hashes.keys():
            newbar = self.bar_hashes[symbol] != this_bar_hash
        self.bar_hashes[symbol] = this_bar_hash

        if newbar and handle_bar:
            if self.bars[(self.bars['symbol'] == symbol) | (
                    self.bars['symbol_group'] == symbol)].empty:
                return
            bar_instrument = self.get_instrument(symbol)
            if bar_instrument:
                self.record_ts = bar.index[0]
                self.on_bar(bar_instrument)
                # if self.resolution[-1] not in ("S", "K", "V"):
                self.record(bar)
non threaded bar handler (called by threaded _tick_handler)